From 00d592cd4d3859c16d7cdedce5d9a7859e765782 Mon Sep 17 00:00:00 2001 From: Andy Finch <andyfinch7@gmail.com> Date: Sat, 28 Dec 2019 16:29:42 -0500 Subject: [PATCH] add v8::SharedArrayBuffer (#134) --- src/array_buffer.rs | 15 +++++++-- src/binding.cc | 16 ++++++++++ src/lib.rs | 2 ++ src/shared_array_buffer.rs | 62 ++++++++++++++++++++++++++++++++++++++ tests/test_api.rs | 49 ++++++++++++++++++++++++++++++ 5 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/shared_array_buffer.rs diff --git a/src/array_buffer.rs b/src/array_buffer.rs index 3237ae3e..08bcbd59 100644 --- a/src/array_buffer.rs +++ b/src/array_buffer.rs @@ -28,6 +28,7 @@ extern "C" { isolate: *mut Isolate, byte_length: usize, ) -> *mut BackingStore; + fn v8__BackingStore__Data(self_: &mut BackingStore) -> *mut std::ffi::c_void; fn v8__BackingStore__ByteLength(self_: &BackingStore) -> usize; fn v8__BackingStore__IsShared(self_: &BackingStore) -> bool; fn v8__BackingStore__DELETE(self_: &mut BackingStore); @@ -98,11 +99,21 @@ impl Delete for Allocator { pub struct BackingStore([usize; 6]); impl BackingStore { + /// Returns a rust u8 slice with a lifetime equal to the lifetime of the BackingStore. + pub fn data_bytes<'a>(&'a mut self) -> &'a mut [u8] { + unsafe { + std::slice::from_raw_parts_mut::<'a, u8>( + v8__BackingStore__Data(self) as *mut u8, + self.byte_length(), + ) + } + } + /// Return a pointer to the beginning of the memory block for this backing /// store. The pointer is only valid as long as this backing store object /// lives. - pub fn data(&self) -> std::ffi::c_void { - unimplemented!() + pub fn data(&mut self) -> &mut std::ffi::c_void { + unsafe { &mut *v8__BackingStore__Data(self) } } /// The length (in bytes) of this backing store. diff --git a/src/binding.cc b/src/binding.cc index f16bc6a2..d93f7ea2 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -335,6 +335,10 @@ two_pointers_t v8__ArrayBuffer__GetBackingStore(v8::ArrayBuffer& self) { return make_pod<two_pointers_t>(self.GetBackingStore()); } +void* v8__BackingStore__Data(v8::BackingStore& self) { + return self.Data(); +} + size_t v8__BackingStore__ByteLength(v8::BackingStore& self) { return self.ByteLength(); } @@ -682,6 +686,18 @@ v8::PrimitiveArray* v8__ScriptOrModule__GetHostDefinedOptions( return local_to_ptr(self.GetHostDefinedOptions()); } +v8::SharedArrayBuffer* v8__SharedArrayBuffer__New(v8::Isolate* isolate, size_t byte_length) { + return local_to_ptr(v8::SharedArrayBuffer::New(isolate, byte_length)); +} + +size_t v8__SharedArrayBuffer__ByteLength(v8::SharedArrayBuffer& self) { + return self.ByteLength(); +} + +two_pointers_t v8__SharedArrayBuffer__GetBackingStore(v8::SharedArrayBuffer& self) { + return make_pod<two_pointers_t>(self.GetBackingStore()); +} + v8::Value* v8__JSON__Parse(v8::Local<v8::Context> context, v8::Local<v8::String> json_string) { return maybe_local_to_ptr(v8::JSON::Parse(context, json_string)); diff --git a/src/lib.rs b/src/lib.rs index 8c2ea51e..66317de0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,6 +61,7 @@ mod promise; mod property; mod script; mod script_or_module; +mod shared_array_buffer; mod snapshot; mod string; mod support; @@ -104,6 +105,7 @@ pub use promise::{ pub use property::PropertyCallbackInfo; pub use script::{Script, ScriptOrigin}; pub use script_or_module::ScriptOrModule; +pub use shared_array_buffer::SharedArrayBuffer; pub use snapshot::{FunctionCodeHandling, SnapshotCreator, StartupData}; pub use string::NewStringType; pub use string::String; diff --git a/src/shared_array_buffer.rs b/src/shared_array_buffer.rs new file mode 100644 index 00000000..dd8464cb --- /dev/null +++ b/src/shared_array_buffer.rs @@ -0,0 +1,62 @@ +use std::ops::Deref; + +use crate::support::Opaque; +use crate::support::SharedRef; +use crate::BackingStore; +use crate::Isolate; +use crate::Local; +use crate::ToLocal; +use crate::Value; + +extern "C" { + fn v8__SharedArrayBuffer__New( + isolate: *mut Isolate, + byte_length: usize, + ) -> *mut SharedArrayBuffer; + fn v8__SharedArrayBuffer__ByteLength( + self_: *const SharedArrayBuffer, + ) -> usize; + fn v8__SharedArrayBuffer__GetBackingStore( + self_: *const SharedArrayBuffer, + ) -> SharedRef<BackingStore>; +} + +/// An instance of the built-in SharedArrayBuffer constructor. +/// This API is experimental and may change significantly. +#[repr(C)] +pub struct SharedArrayBuffer(Opaque); + +impl SharedArrayBuffer { + /// Create a new SharedArrayBuffer. Allocate |byte_length| bytes. + /// Allocated memory will be owned by a created SharedArrayBuffer and + /// will be deallocated when it is garbage-collected, + /// unless the object is externalized. + pub fn new<'sc>( + scope: &mut impl ToLocal<'sc>, + byte_length: usize, + ) -> Option<Local<'sc, SharedArrayBuffer>> { + unsafe { + Local::from_raw(v8__SharedArrayBuffer__New(scope.isolate(), byte_length)) + } + } + + /// Data length in bytes. + pub fn byte_length(&self) -> usize { + unsafe { v8__SharedArrayBuffer__ByteLength(self) } + } + + /// Get a shared pointer to the backing store of this array buffer. This + /// pointer coordinates the lifetime management of the internal storage + /// with any live ArrayBuffers on the heap, even across isolates. The embedder + /// should not attempt to manage lifetime of the storage through other means. + pub fn get_backing_store(&self) -> SharedRef<BackingStore> { + unsafe { v8__SharedArrayBuffer__GetBackingStore(self) } + } +} + +impl Deref for SharedArrayBuffer { + type Target = Value; + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const _ as *const Value) } + } +} diff --git a/tests/test_api.rs b/tests/test_api.rs index aa46c032..9b78745e 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -1491,3 +1491,52 @@ fn dynamic_import() { isolate.exit(); drop(g); } + +#[test] +fn shared_array_buffer() { + let g = setup(); + let mut params = v8::Isolate::create_params(); + params.set_array_buffer_allocator(v8::new_default_allocator()); + let mut isolate = v8::Isolate::new(params); + isolate.enter(); + let mut locker = v8::Locker::new(&isolate); + { + let mut hs = v8::HandleScope::new(&mut locker); + let s = hs.enter(); + let mut context = v8::Context::new(s); + context.enter(); + let maybe_sab = v8::SharedArrayBuffer::new(s, 16); + assert!(maybe_sab.is_some()); + let sab = maybe_sab.unwrap(); + let mut backing_store = sab.get_backing_store(); + let shared_buf = backing_store.data_bytes(); + shared_buf[5] = 12; + shared_buf[12] = 52; + let global = context.global(s); + assert_eq!( + global.create_data_property( + context, + cast(v8_str(s, "shared")), + cast(sab) + ), + v8::MaybeBool::JustTrue + ); + let source = v8::String::new( + s, + "sharedBytes = new Uint8Array(shared); sharedBytes[2] = 16; sharedBytes[14] = 62; sharedBytes[5] + sharedBytes[12]", + ) + .unwrap(); + let mut script = v8::Script::compile(s, context, source, None).unwrap(); + source.to_rust_string_lossy(s); + let result = script.run(s, context).unwrap(); + // TODO: safer casts. + let result: v8::Local<v8::Integer> = cast(result); + assert_eq!(result.value(), 64); + assert_eq!(shared_buf[2], 16); + assert_eq!(shared_buf[14], 62); + context.exit(); + } + drop(locker); + isolate.exit(); + drop(g); +}