Skip to content

Commit

Permalink
Merge pull request #217 from DelSkayn/impl_module
Browse files Browse the repository at this point in the history
Expose raw pointers to array buffer.
  • Loading branch information
DelSkayn authored Sep 18, 2023
2 parents 9cee95a + 2eedb57 commit 6a2fcfe
Show file tree
Hide file tree
Showing 15 changed files with 443 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
- name: Documentation
env:
DOCS_RS: 1
run: cargo doc --features full-async,parallel,doc-cfg
run: cargo doc --no-deps --features full-async,parallel,doc-cfg
- name: Upload docs
uses: actions/upload-artifact@v3
with:
Expand Down
5 changes: 4 additions & 1 deletion core/src/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ pub use trace::{Trace, Tracer};
#[doc(hidden)]
pub mod impl_;

/// The trait which allows rust types to be used from javascript.
pub trait JsClass<'js>: Trace<'js> {
/// The name the constructor has in JavaScript
const NAME: &'static str;

/// Can the type be mutated while a JavaScript value.
///
/// This should either be [`Readable`] or [`Writable`].
type Mutable: Mutability;

/// A unique id for the class.
Expand Down Expand Up @@ -112,7 +115,7 @@ impl<'js, C: JsClass<'js>> Class<'js, C> {
))
}

/// Create a class from a Rust object with a given prototype
/// Create a class from a Rust object with a given prototype.
pub fn instance_proto(value: C, proto: Object<'js>) -> Result<Class<'js, C>> {
if !Self::is_registered(proto.ctx()) {
Self::register(proto.ctx())?;
Expand Down
23 changes: 23 additions & 0 deletions core/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use crate::{
atom::PredefinedAtom, qjs, Context, Ctx, Exception, Object, StdResult, StdString, Type, Value,
};

#[cfg(feature = "array-buffer")]
use crate::value::array_buffer::AsSliceError;

/// Result type used throughout the library.
pub type Result<T> = StdResult<T, Error>;

Expand Down Expand Up @@ -113,6 +116,9 @@ pub enum Error {
name: StdString,
message: Option<StdString>,
},

#[cfg(feature = "array-buffer")]
AsSlice(AsSliceError),
/// Error when restoring a Persistent in a runtime other than the original runtime.
UnrelatedRuntime,
/// An error from QuickJS from which the specifics are unknown.
Expand Down Expand Up @@ -276,6 +282,11 @@ impl Error {
let message = self.to_cstring();
unsafe { qjs::JS_ThrowTypeError(ctx.as_ptr(), message.as_ptr()) }
}
#[cfg(feature = "array-buffer")]
AsSlice(_) => {
let message = self.to_cstring();
unsafe { qjs::JS_ThrowReferenceError(ctx.as_ptr(), message.as_ptr()) }
}
#[cfg(feature = "loader")]
Resolving { .. } | Loading { .. } => {
let message = self.to_cstring();
Expand Down Expand Up @@ -417,6 +428,11 @@ impl Display for Error {
"Error borrowing function: ".fmt(f)?;
x.fmt(f)?;
}
#[cfg(feature = "array-buffer")]
AsSlice(x) => {
"Could not convert array buffer to slice: ".fmt(f)?;
x.fmt(f)?;
}
UnrelatedRuntime => "Restoring Persistent in an unrelated runtime".fmt(f)?,
}
Ok(())
Expand Down Expand Up @@ -448,6 +464,13 @@ impl From<FromUtf8Error> for Error {
}
}

#[cfg(feature = "array-buffer")]
impl From<AsSliceError> for Error {
fn from(value: AsSliceError) -> Self {
Error::AsSlice(value)
}
}

/// An error type containing possible thrown exception values.
#[derive(Debug)]
pub enum CaughtError<'js> {
Expand Down
3 changes: 2 additions & 1 deletion core/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ pub use r#async::{AsyncRuntime, AsyncWeakRuntime};
#[cfg(feature = "futures")]
mod spawner;

pub use crate::qjs::JSMemoryUsage as MemoryUsage;
/// A struct with information about the runtimes memory usage.
pub type MemoryUsage = crate::qjs::JSMemoryUsage;
3 changes: 3 additions & 0 deletions core/src/runtime/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ impl Drop for InnerRuntime {
#[cfg(feature = "parallel")]
unsafe impl Send for InnerRuntime {}

/// A weak handle to the async runtime.
///
/// Holding onto this struct does not prevent the runtime from being dropped.
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "futures")))]
#[derive(Clone)]
pub struct AsyncWeakRuntime {
Expand Down
2 changes: 2 additions & 0 deletions core/src/runtime/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use super::{
};

/// A weak handle to the runtime.
///
/// Holding onto this struct does not prevent the runtime from being dropped.
#[derive(Clone)]
#[repr(transparent)]
pub struct WeakRuntime(Weak<Mut<RawRuntime>>);
Expand Down
10 changes: 5 additions & 5 deletions core/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ pub mod object;
mod string;
mod symbol;

#[cfg(feature = "array-buffer")]
mod array_buffer;
#[cfg(feature = "array-buffer")]
mod typed_array;

pub use array::Array;
pub use atom::Atom;
pub use bigint::BigInt;
Expand All @@ -28,6 +23,11 @@ pub use object::{Filter, Object};
pub use string::String;
pub use symbol::Symbol;

#[cfg(feature = "array-buffer")]
pub mod array_buffer;
#[cfg(feature = "array-buffer")]
pub mod typed_array;

#[cfg(feature = "array-buffer")]
pub use array_buffer::ArrayBuffer;
#[cfg(feature = "array-buffer")]
Expand Down
61 changes: 46 additions & 15 deletions core/src/value/array_buffer.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,39 @@
use crate::{qjs, Ctx, Error, FromJs, IntoJs, Object, Outlive, Result, Value};
use core::fmt;
use std::{
convert::TryInto,
mem::{self, size_of, ManuallyDrop, MaybeUninit},
ops::Deref,
os::raw::c_void,
ptr::NonNull,
result::Result as StdResult,
slice,
};

use super::typed_array::TypedArrayItem;

pub struct RawArrayBuffer {
pub len: usize,
pub ptr: NonNull<u8>,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum AsSliceError {
BufferUsed,
InvalidAlignment,
}

impl fmt::Display for AsSliceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AsSliceError::BufferUsed => write!(f, "Buffer was already used"),
AsSliceError::InvalidAlignment => {
write!(f, "Buffer had a different alignment than was requested")
}
}
}
}

/// Rust representation of a JavaScript object of class ArrayBuffer.
///
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "array-buffer")))]
Expand Down Expand Up @@ -69,7 +94,7 @@ impl<'js> ArrayBuffer<'js> {

/// Get the length of the array buffer in bytes.
pub fn len(&self) -> usize {
Self::get_raw(&self.0).expect("Not an ArrayBuffer").0
Self::get_raw(&self.0).expect("Not an ArrayBuffer").len
}

/// Returns whether an array buffer is empty.
Expand All @@ -81,20 +106,19 @@ impl<'js> ArrayBuffer<'js> {
///
/// Returns `None` if the array is detached.
pub fn as_bytes(&self) -> Option<&[u8]> {
let (len, ptr) = Self::get_raw(self.as_value())?;
Some(unsafe { slice::from_raw_parts_mut(ptr, len) })
let raw = Self::get_raw(self.as_value())?;
Some(unsafe { slice::from_raw_parts_mut(raw.ptr.as_ptr(), raw.len) })
}

/// Returns a slice if the buffer underlying buffer is properly aligned for the type and the
/// buffer is not detached.
pub fn as_slice<T: TypedArrayItem>(&self) -> Option<&[T]> {
let (len, ptr) = Self::get_raw(&self.0)?;
if ptr.align_offset(mem::align_of::<T>()) != 0 {
return None;
pub fn as_slice<T: TypedArrayItem>(&self) -> StdResult<&[T], AsSliceError> {
let raw = Self::get_raw(&self.0).ok_or(AsSliceError::BufferUsed)?;
if raw.ptr.as_ptr().align_offset(mem::align_of::<T>()) != 0 {
return Err(AsSliceError::InvalidAlignment);
}
//assert!(len % size_of::<T>() == 0);
let len = len / size_of::<T>();
Some(unsafe { slice::from_raw_parts(ptr as _, len) })
let len = raw.len / size_of::<T>();
Ok(unsafe { slice::from_raw_parts(raw.ptr.as_ptr().cast(), len) })
}

/// Detach array buffer
Expand Down Expand Up @@ -140,19 +164,26 @@ impl<'js> ArrayBuffer<'js> {
}
}

pub(crate) fn get_raw(val: &Value<'js>) -> Option<(usize, *mut u8)> {
/// Returns a structure with data about the raw buffer which this object contains.
///
/// Returns None if the buffer was already used.
pub fn as_raw(&self) -> Option<RawArrayBuffer> {
Self::get_raw(self.as_value())
}

pub(crate) fn get_raw(val: &Value<'js>) -> Option<RawArrayBuffer> {
let ctx = val.ctx();
let val = val.as_js_value();
let mut size = MaybeUninit::<qjs::size_t>::uninit();
let ptr = unsafe { qjs::JS_GetArrayBuffer(ctx.as_ptr(), size.as_mut_ptr(), val) };

if ptr.is_null() {
None
} else {
if let Some(ptr) = NonNull::new(ptr) {
let len = unsafe { size.assume_init() }
.try_into()
.expect(qjs::SIZE_T_ERROR);
Some((len, ptr))
Some(RawArrayBuffer { len, ptr })
} else {
None
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions core/src/value/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ impl<'js> Function<'js> {
}
}

/// A function which can be used as a constructor.
///
/// Is a subtype of function.
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct Constructor<'js>(pub(crate) Function<'js>);
Expand Down
26 changes: 16 additions & 10 deletions core/src/value/typed_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ use std::{
marker::PhantomData,
mem::{self, MaybeUninit},
ops::Deref,
ptr::null_mut,
ptr::{null_mut, NonNull},
slice,
};

use super::Constructor;
use super::{array_buffer::RawArrayBuffer, Constructor};

/// The trait which implements types which capable to be TypedArray items
///
Expand Down Expand Up @@ -169,7 +169,12 @@ impl<'js, T> TypedArray<'js, T> {
/// Returns `None` if the array is detached.
pub fn as_bytes(&self) -> Option<&[u8]> {
let (_, len, ptr) = Self::get_raw_bytes(self.as_value())?;
Some(unsafe { slice::from_raw_parts(ptr, len) })
Some(unsafe { slice::from_raw_parts(ptr.as_ptr(), len) })
}

pub fn as_raw(&self) -> Option<RawArrayBuffer> {
let (_, len, ptr) = Self::get_raw_bytes(self.as_value())?;
Some(RawArrayBuffer { len, ptr })
}

/// Get underlying ArrayBuffer
Expand All @@ -195,7 +200,7 @@ impl<'js, T> TypedArray<'js, T> {
ctor.construct((arraybuffer,))
}

pub(crate) fn get_raw_bytes(val: &Value<'js>) -> Option<(usize, usize, *mut u8)> {
pub(crate) fn get_raw_bytes(val: &Value<'js>) -> Option<(usize, usize, NonNull<u8>)> {
let ctx = &val.ctx;
let val = val.as_js_value();
let mut off = MaybeUninit::<qjs::size_t>::uninit();
Expand All @@ -221,20 +226,21 @@ impl<'js, T> TypedArray<'js, T> {
let stp: usize = unsafe { stp.assume_init() }
.try_into()
.expect(qjs::SIZE_T_ERROR);
let (full_len, ptr) = ArrayBuffer::get_raw(&buf)?;
if (off + len) > full_len {
let raw = ArrayBuffer::get_raw(&buf)?;
if (off + len) > raw.len {
return None;
}
let ptr = unsafe { ptr.add(off) };
// SAFETY: ptr was non-null and then we added an offset so it should still be non null
let ptr = unsafe { NonNull::new_unchecked(raw.ptr.as_ptr().add(off)) };
Some((stp, len, ptr))
}

pub(crate) fn get_raw(val: &Value<'js>) -> Option<(usize, *mut T)> {
pub(crate) fn get_raw(val: &Value<'js>) -> Option<(usize, NonNull<T>)> {
let (stp, len, ptr) = Self::get_raw_bytes(val)?;
if stp != mem::size_of::<T>() {
return None;
}
debug_assert_eq!(ptr.align_offset(mem::align_of::<T>()), 0);
debug_assert_eq!(ptr.as_ptr().align_offset(mem::align_of::<T>()), 0);
let ptr = ptr.cast::<T>();
Some((len / mem::size_of::<T>(), ptr))
}
Expand All @@ -244,7 +250,7 @@ impl<'js, T: TypedArrayItem> AsRef<[T]> for TypedArray<'js, T> {
fn as_ref(&self) -> &[T] {
let (len, ptr) =
Self::get_raw(&self.0).unwrap_or_else(|| panic!("{}", T::CLASS_NAME.to_str()));
unsafe { slice::from_raw_parts(ptr as _, len) }
unsafe { slice::from_raw_parts(ptr.as_ptr().cast(), len) }
}
}

Expand Down
2 changes: 1 addition & 1 deletion macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ version = "2"
[dev-dependencies.rquickjs]
path = ".."
version = "0.4.0-beta.4"
features = ["macro", "classes", "properties", "futures"]
features = ["macro", "classes", "properties", "futures","phf"]

[dev-dependencies.async-std]
version = "1"
Expand Down
3 changes: 3 additions & 0 deletions macro/my_module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function foo(){
return 1 + 1
}
13 changes: 7 additions & 6 deletions macro/src/embed.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::Path;
use std::{env, path::Path};

use crate::common::crate_ident;
use proc_macro2::TokenStream;
Expand Down Expand Up @@ -55,16 +55,17 @@ pub fn embed(modules: EmbedModules) -> TokenStream {
let path = Path::new(&path);

let path = if path.is_relative() {
match Path::new(env!("CARGO_MANIFEST_DIR"))
.join(path)
.canonicalize()
{
let full_path = Path::new(
&env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR should be set"),
)
.join(path);
match full_path.canonicalize() {
Ok(x) => x,
Err(e) => {
abort!(
f.name,
"Error loading embedded js module from path `{}`: {}",
path.display(),
full_path.display(),
e
);
}
Expand Down
Loading

0 comments on commit 6a2fcfe

Please sign in to comment.