Skip to content

Commit

Permalink
Add ArrayBuffer::set_detach_key (denoland#1127)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
andreubotella committed Nov 21, 2022
1 parent 601fed8 commit acba887
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 1 deletion.
9 changes: 8 additions & 1 deletion src/array_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Value>) -> Option<bool> {
Expand All @@ -421,6 +422,12 @@ impl ArrayBuffer {
}
}

/// Sets the `[[ArrayBufferDetachKey]]`.
#[inline(always)]
pub fn set_detach_key(&self, key: Local<Value>) {
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)]
Expand Down
5 changes: 5 additions & 0 deletions src/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
47 changes: 47 additions & 0 deletions tests/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

0 comments on commit acba887

Please sign in to comment.