Skip to content

Commit

Permalink
Copy safety
Browse files Browse the repository at this point in the history
  • Loading branch information
kjvalencik committed Jan 14, 2022
1 parent 7b164b9 commit 84a8497
Show file tree
Hide file tree
Showing 16 changed files with 386 additions and 162 deletions.
2 changes: 1 addition & 1 deletion crates/neon-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::ptr::null_mut;
/// `Local` handles get associated to a V8 `HandleScope` container. Note: Node.js creates a
/// `HandleScope` right before calling functions in native addons.
#[repr(C)]
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub struct Local {
pub handle: *mut c_void,
}
Expand Down
10 changes: 6 additions & 4 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ impl<'a> ModuleContext<'a> {
f: fn(FunctionContext) -> JsResult<T>,
) -> NeonResult<()> {
let value = JsFunction::new(self, f)?.upcast::<JsValue>();
self.exports.set(self, key, value)?;
self.exports.clone().set(self, key, value)?;
Ok(())
}

Expand All @@ -727,21 +727,23 @@ impl<'a> ModuleContext<'a> {
V: Value,
{
let value = JsFunction::new(self, f)?.upcast::<JsValue>();
self.exports.set(self, key, value)?;
// Note: Cloning `exports` is necessary to avoid holding a shared reference to
// `self` while attempting to use it mutably in `set`.
self.exports.clone().set(self, key, value)?;
Ok(())
}

#[cfg(feature = "legacy-runtime")]
/// Convenience method for exporting a Neon class constructor from a module.
pub fn export_class<T: Class>(&mut self, key: &str) -> NeonResult<()> {
let constructor = T::constructor(self)?;
self.exports.set(self, key, constructor)?;
self.exports.clone().set(self, key, constructor)?;
Ok(())
}

/// Exports a JavaScript value from a Neon module.
pub fn export_value<T: Value>(&mut self, key: &str, val: Handle<T>) -> NeonResult<()> {
self.exports.set(self, key, val)?;
self.exports.clone().set(self, key, val)?;
Ok(())
}

Expand Down
27 changes: 26 additions & 1 deletion src/handle/internal.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
use std::fmt::Debug;
use std::mem;

use crate::types::Value;

pub trait SuperType<T: Value> {
fn upcast_internal(v: T) -> Self;
fn upcast_internal(v: &T) -> Self;
}

#[doc(hidden)]
/// Trait asserting that `Self` is a transparent wrapper around `Self::Inner`
/// with identical representation and may be safely transmuted.
///
/// # Safety
/// `Self` must be `#[repr(transparent)]` with a field `Self::Inner`
pub unsafe trait TransparentWrapper: Sized {
type Inner: Debug + Copy;

// A default implementation cannot be provided because it would create
// dependently sized types. This may be supported in a future Rust version.
fn into_inner(self) -> Self::Inner;

fn wrap_ref(s: &Self::Inner) -> &Self {
unsafe { mem::transmute(s) }
}

fn wrap_mut(s: &mut Self::Inner) -> &mut Self {
unsafe { mem::transmute(s) }
}
}
42 changes: 28 additions & 14 deletions src/handle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub(crate) mod root;
#[cfg(feature = "napi-1")]
pub use self::root::Root;

use self::internal::SuperType;
use self::internal::{SuperType, TransparentWrapper};
use crate::context::internal::Env;
use crate::context::Context;
use crate::result::{JsResult, JsResultExt};
Expand All @@ -72,20 +72,23 @@ use neon_runtime::raw;
use std::error::Error;
use std::fmt::{self, Debug, Display};
use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};

/// The trait of data owned by the JavaScript engine and that can only be accessed via handles.
pub trait Managed: Copy {
fn to_raw(self) -> raw::Local;
pub trait Managed: TransparentWrapper {
fn to_raw(&self) -> raw::Local;

fn from_raw(env: Env, h: raw::Local) -> Self;
}

/// A handle to a JavaScript value that is owned by the JavaScript engine.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[derive(Debug)]
#[repr(transparent)]
pub struct Handle<'a, T: Managed + 'a> {
value: T,
// Contains the actual `Copy` JavaScript value data. It will be wrapped in
// in a `!Copy` type when dereferencing. Only `T` should be visible to the user.
value: <T as TransparentWrapper>::Inner,
phantom: PhantomData<&'a T>,
}

Expand All @@ -99,10 +102,21 @@ impl<'a, T: Managed + 'a> PartialEq for Handle<'a, T> {
#[cfg(feature = "legacy-runtime")]
impl<'a, T: Managed + 'a> Eq for Handle<'a, T> {}

impl<'a, T: Managed> Clone for Handle<'a, T> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
phantom: PhantomData,
}
}
}

impl<'a, T: Managed> Copy for Handle<'a, T> {}

impl<'a, T: Managed + 'a> Handle<'a, T> {
pub(crate) fn new_internal(value: T) -> Handle<'a, T> {
Handle {
value,
value: value.into_inner(),
phantom: PhantomData,
}
}
Expand Down Expand Up @@ -155,7 +169,7 @@ impl<'a, T: Value> Handle<'a, T> {
///
/// This method does not require an execution context because it only copies a handle.
pub fn upcast<U: Value + SuperType<T>>(&self) -> Handle<'a, U> {
Handle::new_internal(SuperType::upcast_internal(self.value))
Handle::new_internal(SuperType::upcast_internal(self.deref()))
}

#[cfg(feature = "legacy-runtime")]
Expand All @@ -174,7 +188,7 @@ impl<'a, T: Value> Handle<'a, T> {
/// # }
/// ```
pub fn is_a<U: Value>(&self) -> bool {
U::is_typeof(Env::current(), self.value)
U::is_typeof(Env::current(), self.deref())
}

#[cfg(feature = "napi-1")]
Expand All @@ -193,7 +207,7 @@ impl<'a, T: Value> Handle<'a, T> {
/// # }
/// ```
pub fn is_a<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> bool {
U::is_typeof(cx.env(), self.value)
U::is_typeof(cx.env(), self.deref())
}

#[cfg(feature = "legacy-runtime")]
Expand All @@ -202,7 +216,7 @@ impl<'a, T: Value> Handle<'a, T> {
/// continue interacting with the JS engine if this method produces an `Err`
/// result.
pub fn downcast<U: Value>(&self) -> DowncastResult<'a, T, U> {
match U::downcast(Env::current(), self.value) {
match U::downcast(Env::current(), self.deref()) {
Some(v) => Ok(Handle::new_internal(v)),
None => Err(DowncastError::new()),
}
Expand All @@ -214,7 +228,7 @@ impl<'a, T: Value> Handle<'a, T> {
/// continue interacting with the JS engine if this method produces an `Err`
/// result.
pub fn downcast<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> DowncastResult<'a, T, U> {
match U::downcast(cx.env(), self.value) {
match U::downcast(cx.env(), self.deref()) {
Some(v) => Ok(Handle::new_internal(v)),
None => Err(DowncastError::new()),
}
Expand Down Expand Up @@ -251,12 +265,12 @@ impl<'a, T: Value> Handle<'a, T> {
impl<'a, T: Managed> Deref for Handle<'a, T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
unsafe { mem::transmute(&self.value) }
}
}

impl<'a, T: Managed> DerefMut for Handle<'a, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.value
unsafe { mem::transmute(&mut self.value) }
}
}
17 changes: 12 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,17 @@ macro_rules! class_definition {
#[macro_export(local_inner_macros)]
macro_rules! impl_managed {
($cls:ident) => {
unsafe impl $crate::macro_internal::TransparentWrapper for $cls {
type Inner = $crate::macro_internal::runtime::raw::Local;

fn into_inner(self) -> Self::Inner {
self.0
}
}

impl $crate::handle::Managed for $cls {
fn to_raw(self) -> $crate::macro_internal::runtime::raw::Local {
let $cls(raw) = self;
raw
fn to_raw(&self) -> $crate::macro_internal::runtime::raw::Local {
self.0
}

fn from_raw(
Expand Down Expand Up @@ -406,8 +413,8 @@ macro_rules! declare_types {
};

{ $(#[$attr:meta])* pub class $cls:ident as $cname:ident for $typ:ty { $($body:tt)* } $($rest:tt)* } => {
#[derive(Copy, Clone)]
#[repr(C)]
#[derive(Debug)]
#[repr(transparent)]
$(#[$attr])*
pub struct $cls($crate::macro_internal::runtime::raw::Local);

Expand Down
2 changes: 2 additions & 0 deletions src/macro_internal/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Internals needed by macros. These have to be exported for the macros to work
pub use crate::context::internal::{initialize_module, Env};
#[cfg(feature = "legacy-runtime")]
pub use crate::handle::internal::TransparentWrapper;
/// but are subject to change and should never be explicitly used.

#[cfg(feature = "legacy-runtime")]
Expand Down
2 changes: 1 addition & 1 deletion src/object/class/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ impl<T: Class> ValueInternal for T {
}
}

fn is_typeof<Other: Value>(mut env: Env, value: Other) -> bool {
fn is_typeof<Other: Value>(mut env: Env, value: &Other) -> bool {
let map = env.class_map();
match map.get(&TypeId::of::<T>()) {
None => false,
Expand Down
12 changes: 6 additions & 6 deletions src/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,22 +91,22 @@ mod traits {
/// The trait of all object types.
pub trait Object: Value {
fn get<'a, C: Context<'a>, K: PropertyKey>(
self,
&self,
cx: &mut C,
key: K,
) -> NeonResult<Handle<'a, JsValue>> {
build(cx.env(), |out| unsafe { key.get_from(out, self.to_raw()) })
}

fn get_own_property_names<'a, C: Context<'a>>(self, cx: &mut C) -> JsResult<'a, JsArray> {
fn get_own_property_names<'a, C: Context<'a>>(&self, cx: &mut C) -> JsResult<'a, JsArray> {
let env = cx.env();
build(env, |out| unsafe {
neon_runtime::object::get_own_property_names(out, env.to_raw(), self.to_raw())
})
}

fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
self,
&self,
_: &mut C,
key: K,
val: Handle<W>,
Expand Down Expand Up @@ -236,7 +236,7 @@ mod traits {
/// The trait of all object types.
pub trait Object: Value {
fn get<'a, C: Context<'a>, K: PropertyKey>(
self,
&self,
cx: &mut C,
key: K,
) -> NeonResult<Handle<'a, JsValue>> {
Expand All @@ -247,7 +247,7 @@ mod traits {

#[cfg(feature = "napi-6")]
#[cfg_attr(docsrs, doc(cfg(feature = "napi-6")))]
fn get_own_property_names<'a, C: Context<'a>>(self, cx: &mut C) -> JsResult<'a, JsArray> {
fn get_own_property_names<'a, C: Context<'a>>(&self, cx: &mut C) -> JsResult<'a, JsArray> {
let env = cx.env();

build(cx.env(), |out| unsafe {
Expand All @@ -256,7 +256,7 @@ mod traits {
}

fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
self,
&self,
cx: &mut C,
key: K,
val: Handle<W>,
Expand Down
34 changes: 25 additions & 9 deletions src/types/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::borrow::internal::Pointer;
use crate::borrow::{Borrow, BorrowMut, LoanError, Ref, RefMut};
use crate::context::internal::Env;
use crate::context::{Context, Lock};
use crate::handle::Managed;
use crate::handle::{internal::TransparentWrapper, Managed};
use crate::result::JsResult;
use crate::types::private::ValueInternal;
use crate::types::{build, Object, Value};
Expand All @@ -14,10 +14,18 @@ use std::os::raw::c_void;
use std::slice;

/// The Node [`Buffer`](https://nodejs.org/api/buffer.html) type.
#[repr(C)]
#[derive(Clone, Copy)]
#[derive(Debug)]
#[repr(transparent)]
pub struct JsBuffer(raw::Local);

unsafe impl TransparentWrapper for JsBuffer {
type Inner = raw::Local;

fn into_inner(self) -> Self::Inner {
self.0
}
}

impl JsBuffer {
/// Constructs a new `Buffer` object, safely zero-filled.
pub fn new<'a, C: Context<'a>>(cx: &mut C, size: u32) -> JsResult<'a, JsBuffer> {
Expand All @@ -40,7 +48,7 @@ impl JsBuffer {
}

impl Managed for JsBuffer {
fn to_raw(self) -> raw::Local {
fn to_raw(&self) -> raw::Local {
self.0
}

Expand All @@ -54,7 +62,7 @@ impl ValueInternal for JsBuffer {
"Buffer".to_string()
}

fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
unsafe { neon_runtime::tag::is_buffer(env.to_raw(), other.to_raw()) }
}
}
Expand All @@ -64,8 +72,8 @@ impl Value for JsBuffer {}
impl Object for JsBuffer {}

/// The standard JS [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) type.
#[repr(C)]
#[derive(Clone, Copy)]
#[derive(Debug)]
#[repr(transparent)]
pub struct JsArrayBuffer(raw::Local);

impl JsArrayBuffer {
Expand All @@ -77,8 +85,16 @@ impl JsArrayBuffer {
}
}

unsafe impl TransparentWrapper for JsArrayBuffer {
type Inner = raw::Local;

fn into_inner(self) -> Self::Inner {
self.0
}
}

impl Managed for JsArrayBuffer {
fn to_raw(self) -> raw::Local {
fn to_raw(&self) -> raw::Local {
self.0
}

Expand All @@ -92,7 +108,7 @@ impl ValueInternal for JsArrayBuffer {
"ArrayBuffer".to_string()
}

fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
unsafe { neon_runtime::tag::is_arraybuffer(env.to_raw(), other.to_raw()) }
}
}
Expand Down

0 comments on commit 84a8497

Please sign in to comment.