1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! Provides temporary access to JavaScript typed arrays.
//!
//! ## Typed Arrays
//!
//! JavaScript's [typed arrays][typed-arrays] are objects that allow reading and writing
//! raw binary data in memory.
//!
//! Typed arrays are managed with the [`ArrayBuffer`][ArrayBuffer] type, which controls
//! the storage of the underlying data buffer, and several typed views for managing access
//! to the buffer. Neon provides access to the `ArrayBuffer` class with the
//! [`JsArrayBuffer`](crate::types::JsArrayBuffer) type.
//!
//! Node also provides a [`Buffer`][Buffer] type, which is built on top of `ArrayBuffer`
//! and provides additional functionality. Neon provides access to the `Buffer` class
//! with the [`JsBuffer`](crate::types::JsBuffer) type.
//!
//! Many of Node's I/O APIs work with these types, and they can also be used for
//! compact in-memory data structures, which can be shared efficiently between
//! JavaScript and Rust without copying.
//!
//! ## Borrowing
//!
//! Neon makes it possible to [borrow][borrow] temporary access to the internal memory
//! of a typed array by pausing execution of JavaScript with a
//! [`Lock`](crate::context::Lock) and returning a reference to a
//! [`BinaryData`](crate::types::BinaryData) struct. The [`Borrow`](Borrow) and
//! [`BorrowMut`](BorrowMut) traits provide the methods for borrowing this typed array data.
//!
//! [typed-arrays]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays
//! [borrow]: https://doc.rust-lang.org/beta/rust-by-example/scope/borrow.html
//! [ArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
//! [Buffer]: https://nodejs.org/api/buffer.html

pub(crate) mod internal;

use std::fmt;
use std::ops::{Deref, DerefMut, Drop};
use std::os::raw::c_void;

use self::internal::Pointer;
use context::Lock;

/// A trait for JS values whose internal contents can be borrowed immutably by Rust while the JS engine is locked.
pub trait Borrow: Sized {
    /// The type of the value's internal contents.
    type Target: Pointer;

    /// Borrow the contents of this value immutably.
    ///
    /// If there is already an outstanding mutable loan for this value, this method panics.
    fn borrow<'a>(self, lock: &'a Lock<'a>) -> Ref<'a, Self::Target> {
        match self.try_borrow(lock) {
            Ok(r) => r,
            Err(e) => panic!("{}", e),
        }
    }

    /// Borrow the contents of this value immutably.
    ///
    /// If there is already an outstanding mutable loan for this value, this method fails with a `LoanError`.
    fn try_borrow<'a>(self, lock: &'a Lock<'a>) -> Result<Ref<'a, Self::Target>, LoanError>;
}

/// A trait for JS values whose internal contents can be borrowed mutably by Rust while the JS engine is locked.
pub trait BorrowMut: Borrow {
    /// Borrow the contents of this value mutably.
    ///
    /// If there is already an outstanding loan for this value, this method panics.
    fn borrow_mut<'a>(self, lock: &'a Lock<'a>) -> RefMut<'a, Self::Target> {
        match self.try_borrow_mut(lock) {
            Ok(r) => r,
            Err(e) => panic!("{}", e),
        }
    }

    /// Borrow the contents of this value mutably.
    ///
    /// If there is already an outstanding loan for this value, this method panics.
    fn try_borrow_mut<'a>(self, lock: &'a Lock<'a>) -> Result<RefMut<'a, Self::Target>, LoanError>;
}

/// An error produced by a failed loan in the `Borrow` or `BorrowMut` traits.
pub enum LoanError {
    /// Indicates that there is already an outstanding mutable loan for the object at this address.
    Mutating(*const c_void),

    /// Indicates that there is already an outstanding immutable loan for the object at this address.
    Frozen(*const c_void),
}

impl fmt::Display for LoanError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            LoanError::Mutating(p) => {
                write!(f, "outstanding mutable loan exists for object at {:?}", p)
            }
            LoanError::Frozen(p) => {
                write!(f, "object at {:?} is frozen", p)
            }
        }
    }
}

/// An immutable reference to the contents of a borrowed JS value.
pub struct Ref<'a, T: Pointer> {
    pointer: T,
    lock: &'a Lock<'a>,
}

impl<'a, T: Pointer> Ref<'a, T> {
    pub(crate) unsafe fn new(lock: &'a Lock<'a>, pointer: T) -> Result<Self, LoanError> {
        let mut ledger = lock.ledger.borrow_mut();
        ledger.try_borrow(pointer.as_ptr())?;
        Ok(Ref { pointer, lock })
    }
}

impl<'a, T: Pointer> Drop for Ref<'a, T> {
    fn drop(&mut self) {
        let mut ledger = self.lock.ledger.borrow_mut();
        ledger.settle(unsafe { self.pointer.as_ptr() });
    }
}

impl<'a, T: Pointer> Deref for Ref<'a, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.pointer
    }
}

/// A mutable reference to the contents of a borrowed JS value.
pub struct RefMut<'a, T: Pointer> {
    pointer: T,
    lock: &'a Lock<'a>,
}

impl<'a, T: Pointer> RefMut<'a, T> {
    pub(crate) unsafe fn new(lock: &'a Lock<'a>, mut pointer: T) -> Result<Self, LoanError> {
        let mut ledger = lock.ledger.borrow_mut();
        ledger.try_borrow_mut(pointer.as_mut())?;
        Ok(RefMut { pointer, lock })
    }
}

impl<'a, T: Pointer> Drop for RefMut<'a, T> {
    fn drop(&mut self) {
        let mut ledger = self.lock.ledger.borrow_mut();
        ledger.settle_mut(unsafe { self.pointer.as_mut() });
    }
}

impl<'a, T: Pointer> Deref for RefMut<'a, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.pointer
    }
}

impl<'a, T: Pointer> DerefMut for RefMut<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.pointer
    }
}