From acba887fa46c70d0fbea3aefecd83831f45b9172 Mon Sep 17 00:00:00 2001 From: Andreu Botella Date: Mon, 21 Nov 2022 12:46:57 +0100 Subject: [PATCH] Add `ArrayBuffer::set_detach_key` (#1127) This method, introduced in V8 10.9, makes it so an `ArrayBuffer` object is impossible to detach unless a specific detach key is passed to the `detach` method. Such `ArrayBuffer`s still count as detachable as per the `ArrayBuffer::is_detachable` method, but otherwise behave much like WebAssembly memories. --- src/array_buffer.rs | 9 ++++++++- src/binding.cc | 5 +++++ tests/test_api.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/array_buffer.rs b/src/array_buffer.rs index 66b0cdf2..776802ec 100644 --- a/src/array_buffer.rs +++ b/src/array_buffer.rs @@ -41,6 +41,7 @@ extern "C" { this: *const ArrayBuffer, key: *const Value, ) -> MaybeBool; + fn v8__ArrayBuffer__SetDetachKey(this: *const ArrayBuffer, key: *const Value); fn v8__ArrayBuffer__Data(this: *const ArrayBuffer) -> *mut c_void; fn v8__ArrayBuffer__IsDetachable(this: *const ArrayBuffer) -> bool; fn v8__ArrayBuffer__WasDetached(this: *const ArrayBuffer) -> bool; @@ -408,7 +409,7 @@ impl ArrayBuffer { /// Detaching sets the byte length of the buffer and all typed arrays to zero, /// preventing JavaScript from ever accessing underlying backing store. /// ArrayBuffer should have been externalized and must be detachable. Returns - /// `None` if the key didn't pass the [[ArrayBufferDetachKey]] check, + /// `None` if the key didn't pass the `[[ArrayBufferDetachKey]]` check, /// and `Some(true)` otherwise. #[inline(always)] pub fn detach(&self, key: Local) -> Option { @@ -421,6 +422,12 @@ impl ArrayBuffer { } } + /// Sets the `[[ArrayBufferDetachKey]]`. + #[inline(always)] + pub fn set_detach_key(&self, key: Local) { + unsafe { v8__ArrayBuffer__SetDetachKey(self, &*key) }; + } + /// More efficient shortcut for GetBackingStore()->Data(). /// The returned pointer is valid as long as the ArrayBuffer is alive. #[inline(always)] diff --git a/src/binding.cc b/src/binding.cc index a5c9b2f6..f319e609 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -835,6 +835,11 @@ bool v8__ArrayBuffer__WasDetached(const v8::ArrayBuffer& self) { return v8::Utils::OpenHandle(&self)->was_detached(); } +void v8__ArrayBuffer__SetDetachKey(const v8::ArrayBuffer& self, + const v8::Value* key) { + return ptr_to_local(&self)->SetDetachKey(ptr_to_local(key)); +} + void* v8__BackingStore__Data(const v8::BackingStore& self) { return self.Data(); } diff --git a/tests/test_api.rs b/tests/test_api.rs index e199094a..feece1bd 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -8524,3 +8524,50 @@ fn test_fast_calls_callback_options_data() { eval(scope, source).unwrap(); assert!(unsafe { DATA }); } + +#[test] +fn test_detach_key() { + let _setup_guard = setup(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + + // Object detach key + { + let detach_key = eval(scope, "({})").unwrap(); + assert!(detach_key.is_object()); + let buffer = v8::ArrayBuffer::new(scope, 1024); + buffer.set_detach_key(detach_key); + assert!(buffer.is_detachable()); + assert_eq!(buffer.detach(v8::undefined(scope).into()), None); + assert!(!buffer.was_detached()); + assert_eq!(buffer.detach(detach_key), Some(true)); + assert!(buffer.was_detached()); + } + + // External detach key + { + let mut rust_detach_key = Box::new(42usize); + let v8_detach_key = v8::External::new( + scope, + &mut *rust_detach_key as *mut usize as *mut c_void, + ); + let buffer = v8::ArrayBuffer::new(scope, 1024); + buffer.set_detach_key(v8_detach_key.into()); + assert!(buffer.is_detachable()); + assert_eq!(buffer.detach(v8::undefined(scope).into()), None); + assert!(!buffer.was_detached()); + assert_eq!(buffer.detach(v8_detach_key.into()), Some(true)); + assert!(buffer.was_detached()); + } + + // Undefined detach key + { + let buffer = v8::ArrayBuffer::new(scope, 1024); + buffer.set_detach_key(v8::undefined(scope).into()); + assert!(buffer.is_detachable()); + assert_eq!(buffer.detach(v8::undefined(scope).into()), Some(true)); + assert!(buffer.was_detached()); + } +}