diff --git a/src/array_buffer.rs b/src/array_buffer.rs index cf72b1e7..3237ae3e 100644 --- a/src/array_buffer.rs +++ b/src/array_buffer.rs @@ -1,6 +1,10 @@ +use crate::support::long; use crate::support::Delete; use crate::support::Opaque; +use crate::support::Shared; +use crate::support::SharedRef; use crate::support::UniqueRef; +use crate::InIsolate; use crate::Isolate; use crate::Local; use crate::ToLocal; @@ -8,13 +12,18 @@ use crate::ToLocal; extern "C" { fn v8__ArrayBuffer__Allocator__NewDefaultAllocator() -> *mut Allocator; fn v8__ArrayBuffer__Allocator__DELETE(this: &'static mut Allocator); - - fn v8__ArrayBuffer__New( + fn v8__ArrayBuffer__New__byte_length( isolate: *mut Isolate, byte_length: usize, ) -> *mut ArrayBuffer; + fn v8__ArrayBuffer__New__backing_store( + isolate: *mut Isolate, + backing_store: *mut SharedRef, + ) -> *mut ArrayBuffer; fn v8__ArrayBuffer__ByteLength(self_: *const ArrayBuffer) -> usize; - + fn v8__ArrayBuffer__GetBackingStore( + self_: *const ArrayBuffer, + ) -> SharedRef; fn v8__ArrayBuffer__NewBackingStore( isolate: *mut Isolate, byte_length: usize, @@ -22,6 +31,15 @@ extern "C" { fn v8__BackingStore__ByteLength(self_: &BackingStore) -> usize; fn v8__BackingStore__IsShared(self_: &BackingStore) -> bool; fn v8__BackingStore__DELETE(self_: &mut BackingStore); + fn std__shared_ptr__v8__BackingStore__get( + ptr: *const SharedRef, + ) -> *mut BackingStore; + fn std__shared_ptr__v8__BackingStore__reset( + ptr: *mut SharedRef, + ); + fn std__shared_ptr__v8__BackingStore__use_count( + ptr: *const SharedRef, + ) -> long; } /// A thread-safe allocator that V8 uses to allocate |ArrayBuffer|'s memory. @@ -105,6 +123,18 @@ impl Delete for BackingStore { } } +impl Shared for BackingStore { + fn deref(ptr: *const SharedRef) -> *mut Self { + unsafe { std__shared_ptr__v8__BackingStore__get(ptr) } + } + fn reset(ptr: *mut SharedRef) { + unsafe { std__shared_ptr__v8__BackingStore__reset(ptr) } + } + fn use_count(ptr: *const SharedRef) -> long { + unsafe { std__shared_ptr__v8__BackingStore__use_count(ptr) } + } +} + /// An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5). #[repr(C)] pub struct ArrayBuffer(Opaque); @@ -119,7 +149,19 @@ impl ArrayBuffer { byte_length: usize, ) -> Local<'sc, ArrayBuffer> { let isolate = scope.isolate(); - let ptr = unsafe { v8__ArrayBuffer__New(isolate, byte_length) }; + let ptr = + unsafe { v8__ArrayBuffer__New__byte_length(isolate, byte_length) }; + unsafe { scope.to_local(ptr) }.unwrap() + } + + pub fn new_with_backing_store<'sc>( + scope: &mut impl ToLocal<'sc>, + backing_store: &mut SharedRef, + ) -> Local<'sc, ArrayBuffer> { + let isolate = scope.isolate(); + let ptr = unsafe { + v8__ArrayBuffer__New__backing_store(isolate, &mut *backing_store) + }; unsafe { scope.to_local(ptr) }.unwrap() } @@ -127,6 +169,9 @@ impl ArrayBuffer { pub fn byte_length(&self) -> usize { unsafe { v8__ArrayBuffer__ByteLength(self) } } + pub fn get_backing_store(&self) -> SharedRef { + unsafe { v8__ArrayBuffer__GetBackingStore(self) } + } /// Returns a new standalone BackingStore that is allocated using the array /// buffer allocator of the isolate. The result can be later passed to @@ -135,8 +180,8 @@ impl ArrayBuffer { /// If the allocator returns nullptr, then the function may cause GCs in the /// given isolate and re-try the allocation. If GCs do not help, then the /// function will crash with an out-of-memory error. - pub fn new_backing_store<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new_backing_store( + scope: &mut impl InIsolate, byte_length: usize, ) -> UniqueRef { unsafe { diff --git a/src/binding.cc b/src/binding.cc index c47ad0a9..d335fb58 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -70,7 +70,6 @@ v8::MaybeLocal HostImportModuleDynamicallyCallback( } extern "C" { - void v8__V8__SetFlagsFromCommandLine(int* argc, char** argv) { v8::V8::SetFlagsFromCommandLine(argc, argv, true); } @@ -325,6 +324,10 @@ v8::BackingStore* v8__ArrayBuffer__NewBackingStore(v8::Isolate* isolate, return u.release(); } +two_pointers_t v8__ArrayBuffer__GetBackingStore(v8::ArrayBuffer& self) { + return make_pod(self.GetBackingStore()); +} + size_t v8__BackingStore__ByteLength(v8::BackingStore& self) { return self.ByteLength(); } @@ -335,6 +338,21 @@ bool v8__BackingStore__IsShared(v8::BackingStore& self) { void v8__BackingStore__DELETE(v8::BackingStore& self) { delete &self; } +v8::BackingStore* std__shared_ptr__v8__BackingStore__get( + const std::shared_ptr& ptr) { + return ptr.get(); +} + +void std__shared_ptr__v8__BackingStore__reset( + std::shared_ptr& ptr) { + ptr.reset(); +} + +long std__shared_ptr__v8__BackingStore__use_count( + const std::shared_ptr& ptr) { + return ptr.use_count(); +} + v8::String* v8__String__NewFromUtf8(v8::Isolate* isolate, const char* data, v8::NewStringType type, int length) { return maybe_local_to_ptr( @@ -419,11 +437,16 @@ void v8__ArrayBuffer__Allocator__DELETE(v8::ArrayBuffer::Allocator& self) { delete &self; } -v8::ArrayBuffer* v8__ArrayBuffer__New(v8::Isolate* isolate, - size_t byte_length) { +v8::ArrayBuffer* v8__ArrayBuffer__New__byte_length(v8::Isolate* isolate, + size_t byte_length) { return local_to_ptr(v8::ArrayBuffer::New(isolate, byte_length)); } +v8::ArrayBuffer* v8__ArrayBuffer__New__backing_store( + v8::Isolate* isolate, std::shared_ptr& backing_store) { + return local_to_ptr(v8::ArrayBuffer::New(isolate, backing_store)); +} + size_t v8__ArrayBuffer__ByteLength(v8::ArrayBuffer& self) { return self.ByteLength(); } diff --git a/src/lib.rs b/src/lib.rs index a8fea20b..0a0a9ec7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,6 +74,7 @@ pub use snapshot::{FunctionCodeHandling, SnapshotCreator, StartupData}; pub use string::NewStringType; pub use string::String; pub use support::MaybeBool; +pub use support::SharedRef; pub use support::UniqueRef; pub use try_catch::{TryCatch, TryCatchScope}; pub use uint8_array::Uint8Array; diff --git a/src/support.h b/src/support.h index 81bae366..7c8d4c37 100644 --- a/src/support.h +++ b/src/support.h @@ -2,6 +2,7 @@ #define SUPPORT_H_ #include +#include #include #include #include @@ -11,9 +12,8 @@ #include "v8/include/v8.h" // Check assumptions made in binding code. -// TODO(ry) re-enable the following -// static_assert(sizeof(bool) == sizeof(uint8_t)); -// static_assert(sizeof(std::unique_ptr) == sizeof(void*)); +static_assert(sizeof(bool) == sizeof(uint8_t), ""); +static_assert(sizeof(std::unique_ptr) == sizeof(void*), ""); namespace support { template @@ -35,6 +35,44 @@ void construct_in_place(uninit_t& buf, Args... args) { construct_in_place_helper(buf, std::forward(args)...); } +template +struct make_pod { + template + inline make_pod(V&& value) : pod_(helper(value)) {} + template + inline make_pod(const V& value) : pod_(helper(value)) {} + inline operator P() { return pod_; } + + private: + P pod_; + + template + union helper { + static_assert(std::is_pod

::value, "type P must a pod type"); + static_assert(sizeof(V) <= sizeof(P), + "type P must be at least as big as type V"); + static_assert(alignof(V) <= alignof(P), + "alignment of type P must be compatible with that of type V"); + + inline helper(V&& value) : value_(value), padding_() {} + inline helper(const V& value) : value_(value), padding_() {} + inline ~helper() {} + + inline operator P() { + // Do a memcpy here avoid undefined behavior. + P result; + memcpy(&result, this, sizeof result); + return result; + } + + private: + struct { + V value_; + char padding_[sizeof(P) - sizeof(V)]; + }; + }; +}; + // The C-ABI compatible equivalent of V8's Maybe. enum class MaybeBool { JustFalse = 0, JustTrue = 1, Nothing = 2 }; @@ -87,6 +125,13 @@ inline static v8::Global ptr_to_global(T* ptr) { return global; } +// Because, for some reason, Clang complains that `std::aray) -> *mut Self; + fn reset(shared_ptr: *mut SharedRef); + fn use_count(shared_ptr: *const SharedRef) -> long; +} + +/// Wrapper around a C++ shared_ptr. The shared_ptr is assumed to contain a +/// value and not be null. +#[repr(C)] +#[derive(Debug)] +pub struct SharedRef([*mut Opaque; 2], PhantomData) +where + T: Shared; + +impl SharedRef +where + T: Shared, +{ + pub fn use_count(&self) -> long { + ::use_count(self) + } +} + +impl Deref for SharedRef +where + T: Shared, +{ + // TODO: Maybe this should deref to UnsafeCell? + type Target = T; + fn deref(&self) -> &T { + unsafe { &*::deref(self) } + } +} + +impl DerefMut for SharedRef +where + T: Shared, +{ + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *::deref(self) } + } +} + +impl Drop for SharedRef +where + T: Shared, +{ + fn drop(&mut self) { + ::reset(self); + } +} + #[repr(C)] #[derive(Debug, PartialEq)] pub enum MaybeBool { diff --git a/tests/test_api.rs b/tests/test_api.rs index 3f50bbde..863f7880 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -227,6 +227,61 @@ fn array_buffer() { drop(locker); } +#[test] +fn array_buffer_with_shared_backing_store() { + setup(); + let mut params = v8::Isolate::create_params(); + params.set_array_buffer_allocator(v8::new_default_allocator()); + let isolate = v8::Isolate::new(params); + let mut locker = v8::Locker::new(&isolate); + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); + + let mut context = v8::Context::new(scope); + context.enter(); + + let ab1 = v8::ArrayBuffer::new(scope, 42); + assert_eq!(42, ab1.byte_length()); + + let bs1 = ab1.get_backing_store(); + assert_eq!(ab1.byte_length(), bs1.byte_length()); + assert_eq!(2, v8::SharedRef::use_count(&bs1)); + + let bs2 = ab1.get_backing_store(); + assert_eq!(ab1.byte_length(), bs2.byte_length()); + assert_eq!(3, v8::SharedRef::use_count(&bs1)); + assert_eq!(3, v8::SharedRef::use_count(&bs2)); + + let mut bs3 = ab1.get_backing_store(); + assert_eq!(ab1.byte_length(), bs3.byte_length()); + assert_eq!(4, v8::SharedRef::use_count(&bs1)); + assert_eq!(4, v8::SharedRef::use_count(&bs2)); + assert_eq!(4, v8::SharedRef::use_count(&bs3)); + + drop(bs2); + assert_eq!(3, v8::SharedRef::use_count(&bs1)); + assert_eq!(3, v8::SharedRef::use_count(&bs3)); + + drop(bs1); + assert_eq!(2, v8::SharedRef::use_count(&bs3)); + + let ab2 = v8::ArrayBuffer::new_with_backing_store(scope, &mut bs3); + assert_eq!(ab1.byte_length(), ab2.byte_length()); + assert_eq!(3, v8::SharedRef::use_count(&bs3)); + + let bs4 = ab2.get_backing_store(); + assert_eq!(ab2.byte_length(), bs4.byte_length()); + assert_eq!(4, v8::SharedRef::use_count(&bs4)); + assert_eq!(4, v8::SharedRef::use_count(&bs3)); + + drop(bs3); + assert_eq!(3, v8::SharedRef::use_count(&bs4)); + + context.exit(); + } +} + fn v8_str<'sc>( scope: &mut impl v8::ToLocal<'sc>, s: &str,