From ebef1b23080868fd794aff31d6bc3dacc2b23831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Thu, 24 Oct 2019 13:48:16 +0200 Subject: [PATCH] deps: V8: cherry-pick bba5f1f Original commit message: [api] Add API functions for constructing standalone BackingStores These are useful for the cases when an embedder works with backing stores without creating JS ArrayBuffer objects. Bug: v8:9380 Change-Id: I452bd911e7b20fb38568f18f9d15ea1a7ffb5a57 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1825339 Commit-Queue: Ulan Degenbaev Reviewed-by: Andreas Haas Cr-Commit-Position: refs/heads/master@{#64460} Refs: https://github.com/v8/v8/commit/bba5f1f43d59cf06e6043eb94d689021cc94a4e2 Backport-PR-URL: https://github.com/nodejs/node/pull/30513 PR-URL: https://github.com/nodejs/node/pull/30020 Reviewed-By: Colin Ihrig Reviewed-By: Jiawen Geng Reviewed-By: Anna Henningsen Reviewed-By: Matteo Collina --- common.gypi | 2 +- deps/v8/include/v8.h | 54 ++++++++++++++++ deps/v8/src/api/api.cc | 52 ++++++++++++++++ deps/v8/src/logging/counters.h | 2 + deps/v8/src/objects/backing-store.cc | 51 +++++++++++++-- deps/v8/src/objects/backing-store.h | 31 +++++++--- deps/v8/test/cctest/test-api-array-buffer.cc | 65 ++++++++++++++++++++ 7 files changed, 242 insertions(+), 15 deletions(-) diff --git a/common.gypi b/common.gypi index 5ea3e365064ffd..ca1f853172063a 100644 --- a/common.gypi +++ b/common.gypi @@ -39,7 +39,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.11', + 'v8_embedder_string': '-node.12', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 12fc16c2be042c..ba801345f160e2 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -4849,6 +4849,14 @@ class V8_EXPORT BackingStore : public v8::internal::BackingStoreBase { BackingStore(); }; +/** + * This callback is used only if the memory block for this backing store cannot + * be allocated with an ArrayBuffer::Allocator. In such cases the destructor + * of this backing store object invokes the callback to free the memory block. + */ +using BackingStoreDeleterCallback = void (*)(void* data, size_t length, + void* deleter_data); + /** * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5). */ @@ -4999,6 +5007,29 @@ class V8_EXPORT ArrayBuffer : public Object { static Local New(Isolate* isolate, std::shared_ptr backing_store); + /** + * Returns a new standalone BackingStore that is allocated using the array + * buffer allocator of the isolate. The result can be later passed to + * ArrayBuffer::New. + * + * 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. + */ + static std::unique_ptr NewBackingStore(Isolate* isolate, + size_t byte_length); + /** + * Returns a new standalone BackingStore that takes over the ownership of + * the given buffer. The destructor of the BackingStore invokes the given + * deleter callback. + * + * The result can be later passed to ArrayBuffer::New. The raw pointer + * to the buffer must not be passed again to any V8 API function. + */ + static std::unique_ptr NewBackingStore( + void* data, size_t byte_length, BackingStoreDeleterCallback deleter, + void* deleter_data); + /** * Returns true if ArrayBuffer is externalized, that is, does not * own its memory block. @@ -5449,6 +5480,29 @@ class V8_EXPORT SharedArrayBuffer : public Object { static Local New( Isolate* isolate, std::shared_ptr backing_store); + /** + * Returns a new standalone BackingStore that is allocated using the array + * buffer allocator of the isolate. The result can be later passed to + * SharedArrayBuffer::New. + * + * 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. + */ + static std::unique_ptr NewBackingStore(Isolate* isolate, + size_t byte_length); + /** + * Returns a new standalone BackingStore that takes over the ownership of + * the given buffer. The destructor of the BackingStore invokes the given + * deleter callback. + * + * The result can be later passed to SharedArrayBuffer::New. The raw pointer + * to the buffer must not be passed again to any V8 functions. + */ + static std::unique_ptr NewBackingStore( + void* data, size_t byte_length, BackingStoreDeleterCallback deleter, + void* deleter_data); + /** * Create a new SharedArrayBuffer over an existing memory block. Propagate * flags to indicate whether the underlying buffer can be grown. diff --git a/deps/v8/src/api/api.cc b/deps/v8/src/api/api.cc index 0d80f986f1f955..870c643491c9e5 100644 --- a/deps/v8/src/api/api.cc +++ b/deps/v8/src/api/api.cc @@ -7452,6 +7452,32 @@ Local v8::ArrayBuffer::New( return Utils::ToLocal(obj); } +std::unique_ptr v8::ArrayBuffer::NewBackingStore( + Isolate* isolate, size_t byte_length) { + i::Isolate* i_isolate = reinterpret_cast(isolate); + LOG_API(i_isolate, ArrayBuffer, NewBackingStore); + ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); + std::unique_ptr backing_store = + i::BackingStore::Allocate(i_isolate, byte_length, + i::SharedFlag::kNotShared, + i::InitializedFlag::kZeroInitialized); + if (!backing_store) { + i::FatalProcessOutOfMemory(i_isolate, "v8::ArrayBuffer::NewBackingStore"); + } + return std::unique_ptr( + static_cast(backing_store.release())); +} + +std::unique_ptr v8::ArrayBuffer::NewBackingStore( + void* data, size_t byte_length, BackingStoreDeleterCallback deleter, + void* deleter_data) { + std::unique_ptr backing_store = + i::BackingStore::WrapAllocation(data, byte_length, deleter, deleter_data, + i::SharedFlag::kNotShared); + return std::unique_ptr( + static_cast(backing_store.release())); +} + Local v8::ArrayBufferView::Buffer() { i::Handle obj = Utils::OpenHandle(this); i::Handle buffer; @@ -7753,6 +7779,32 @@ Local v8::SharedArrayBuffer::New( return Utils::ToLocalShared(buffer); } +std::unique_ptr v8::SharedArrayBuffer::NewBackingStore( + Isolate* isolate, size_t byte_length) { + i::Isolate* i_isolate = reinterpret_cast(isolate); + LOG_API(i_isolate, SharedArrayBuffer, NewBackingStore); + ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); + std::unique_ptr backing_store = + i::BackingStore::Allocate(i_isolate, byte_length, i::SharedFlag::kShared, + i::InitializedFlag::kZeroInitialized); + if (!backing_store) { + i::FatalProcessOutOfMemory(i_isolate, + "v8::SharedArrayBuffer::NewBackingStore"); + } + return std::unique_ptr( + static_cast(backing_store.release())); +} + +std::unique_ptr v8::SharedArrayBuffer::NewBackingStore( + void* data, size_t byte_length, BackingStoreDeleterCallback deleter, + void* deleter_data) { + std::unique_ptr backing_store = + i::BackingStore::WrapAllocation(data, byte_length, deleter, deleter_data, + i::SharedFlag::kShared); + return std::unique_ptr( + static_cast(backing_store.release())); +} + Local v8::Symbol::New(Isolate* isolate, Local name) { i::Isolate* i_isolate = reinterpret_cast(isolate); LOG_API(i_isolate, Symbol, New); diff --git a/deps/v8/src/logging/counters.h b/deps/v8/src/logging/counters.h index 99a3c3cf9b1457..5ba1d4626e230e 100644 --- a/deps/v8/src/logging/counters.h +++ b/deps/v8/src/logging/counters.h @@ -733,6 +733,7 @@ class RuntimeCallTimer final { V(ArrayBuffer_Cast) \ V(ArrayBuffer_Detach) \ V(ArrayBuffer_New) \ + V(ArrayBuffer_NewBackingStore) \ V(Array_CloneElementAt) \ V(Array_New) \ V(BigInt64Array_New) \ @@ -850,6 +851,7 @@ class RuntimeCallTimer final { V(Set_Has) \ V(Set_New) \ V(SharedArrayBuffer_New) \ + V(SharedArrayBuffer_NewBackingStore) \ V(String_Concat) \ V(String_NewExternalOneByte) \ V(String_NewExternalTwoByte) \ diff --git a/deps/v8/src/objects/backing-store.cc b/deps/v8/src/objects/backing-store.cc index 55957e001b2265..ff18a231465862 100644 --- a/deps/v8/src/objects/backing-store.cc +++ b/deps/v8/src/objects/backing-store.cc @@ -124,6 +124,7 @@ BackingStore::~BackingStore() { if (is_wasm_memory_) { DCHECK(free_on_destruct_); + DCHECK(!custom_deleter_); TRACE_BS("BSw:free bs=%p mem=%p (length=%zu, capacity=%zu)\n", this, buffer_start_, byte_length(), byte_capacity_); if (is_shared_) { @@ -149,6 +150,14 @@ BackingStore::~BackingStore() { Clear(); return; } + if (custom_deleter_) { + DCHECK(free_on_destruct_); + TRACE_BS("BS:custome deleter bs=%p mem=%p (length=%zu, capacity=%zu)\n", + this, buffer_start_, byte_length(), byte_capacity_); + type_specific_data_.deleter(buffer_start_, byte_length_, deleter_data_); + Clear(); + return; + } if (free_on_destruct_) { // JSArrayBuffer backing store. Deallocate through the embedder's allocator. auto allocator = reinterpret_cast( @@ -210,7 +219,8 @@ std::unique_ptr BackingStore::Allocate( shared, // shared false, // is_wasm_memory true, // free_on_destruct - false); // has_guard_regions + false, // has_guard_regions + false); // custom_deleter TRACE_BS("BS:alloc bs=%p mem=%p (length=%zu)\n", result, result->buffer_start(), byte_length); @@ -321,7 +331,8 @@ std::unique_ptr BackingStore::TryAllocateWasmMemory( shared, // shared true, // is_wasm_memory true, // free_on_destruct - guards); // has_guard_regions + guards, // has_guard_regions + false); // custom_deleter TRACE_BS("BSw:alloc bs=%p mem=%p (length=%zu, capacity=%zu)\n", result, result->buffer_start(), byte_length, byte_capacity); @@ -451,9 +462,14 @@ void BackingStore::UpdateSharedWasmMemoryObjects(Isolate* isolate) { std::unique_ptr BackingStore::WrapAllocation( Isolate* isolate, void* allocation_base, size_t allocation_length, SharedFlag shared, bool free_on_destruct) { - auto result = - new BackingStore(allocation_base, allocation_length, allocation_length, - shared, false, free_on_destruct, false); + auto result = new BackingStore(allocation_base, // start + allocation_length, // length + allocation_length, // capacity + shared, // shared + false, // is_wasm_memory + free_on_destruct, // free_on_destruct + false, // has_guard_regions + false); // custom_deleter result->type_specific_data_.v8_api_array_buffer_allocator = isolate->array_buffer_allocator(); TRACE_BS("BS:wrap bs=%p mem=%p (length=%zu)\n", result, @@ -461,6 +477,25 @@ std::unique_ptr BackingStore::WrapAllocation( return std::unique_ptr(result); } +std::unique_ptr BackingStore::WrapAllocation( + void* allocation_base, size_t allocation_length, + v8::BackingStoreDeleterCallback deleter, void* deleter_data, + SharedFlag shared) { + auto result = new BackingStore(allocation_base, // start + allocation_length, // length + allocation_length, // capacity + shared, // shared + false, // is_wasm_memory + true, // free_on_destruct + false, // has_guard_regions + true); // custom_deleter + result->type_specific_data_.deleter = deleter; + result->deleter_data_ = deleter_data; + TRACE_BS("BS:wrap bs=%p mem=%p (length=%zu)\n", result, + result->buffer_start(), result->byte_length()); + return std::unique_ptr(result); +} + std::unique_ptr BackingStore::EmptyBackingStore( SharedFlag shared) { auto result = new BackingStore(nullptr, // start @@ -469,7 +504,8 @@ std::unique_ptr BackingStore::EmptyBackingStore( shared, // shared false, // is_wasm_memory false, // free_on_destruct - false); // has_guard_regions + false, // has_guard_regions + false); // custom_deleter return std::unique_ptr(result); } @@ -512,6 +548,9 @@ void GlobalBackingStoreRegistry::Register( // then we don't have to guarantee that there is single unique // BackingStore per buffer_start() because the destructor of // of the BackingStore will be a no-op in that case. + + // All WASM memory has to be registered. + CHECK(!backing_store->is_wasm_memory()); return; } diff --git a/deps/v8/src/objects/backing-store.h b/deps/v8/src/objects/backing-store.h index 2c6ffb28daf647..c212bebf485664 100644 --- a/deps/v8/src/objects/backing-store.h +++ b/deps/v8/src/objects/backing-store.h @@ -8,6 +8,7 @@ #include #include "include/v8-internal.h" +#include "include/v8.h" #include "src/handles/handles.h" namespace v8 { @@ -63,6 +64,11 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { SharedFlag shared, bool free_on_destruct); + static std::unique_ptr WrapAllocation( + void* allocation_base, size_t allocation_length, + v8::BackingStoreDeleterCallback deleter, void* deleter_data, + SharedFlag shared); + // Create an empty backing store. static std::unique_ptr EmptyBackingStore(SharedFlag shared); @@ -116,7 +122,7 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { BackingStore(void* buffer_start, size_t byte_length, size_t byte_capacity, SharedFlag shared, bool is_wasm_memory, bool free_on_destruct, - bool has_guard_regions) + bool has_guard_regions, bool custom_deleter) : buffer_start_(buffer_start), byte_length_(byte_length), byte_capacity_(byte_capacity), @@ -124,19 +130,15 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { is_wasm_memory_(is_wasm_memory), free_on_destruct_(free_on_destruct), has_guard_regions_(has_guard_regions), - globally_registered_(false) { + globally_registered_(false), + custom_deleter_(custom_deleter) { type_specific_data_.v8_api_array_buffer_allocator = nullptr; + deleter_data_ = nullptr; } void* buffer_start_ = nullptr; std::atomic byte_length_{0}; size_t byte_capacity_ = 0; - bool is_shared_ : 1; - bool is_wasm_memory_ : 1; - bool free_on_destruct_ : 1; - bool has_guard_regions_ : 1; - bool globally_registered_ : 1; - union { // If this backing store was allocated through the ArrayBufferAllocator API, // this is a direct pointer to the API object for freeing the backing @@ -148,8 +150,21 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { // For shared Wasm memories, this is a list of all the attached memory // objects, which is needed to grow shared backing stores. SharedWasmMemoryData* shared_wasm_memory_data; + + // Custom deleter for the backing stores that wrap memory blocks that are + // allocated with a custom allocator. + v8::BackingStoreDeleterCallback deleter; } type_specific_data_; + void* deleter_data_; + + bool is_shared_ : 1; + bool is_wasm_memory_ : 1; + bool free_on_destruct_ : 1; + bool has_guard_regions_ : 1; + bool globally_registered_ : 1; + bool custom_deleter_ : 1; + // Accessors for type-specific data. void* get_v8_api_array_buffer_allocator(); SharedWasmMemoryData* get_shared_wasm_memory_data(); diff --git a/deps/v8/test/cctest/test-api-array-buffer.cc b/deps/v8/test/cctest/test-api-array-buffer.cc index 508604aa4100b7..488dbde272fca0 100644 --- a/deps/v8/test/cctest/test-api-array-buffer.cc +++ b/deps/v8/test/cctest/test-api-array-buffer.cc @@ -543,3 +543,68 @@ THREADED_TEST(Regress1006600) { CHECK_NULL(ab.As()->GetAlignedPointerFromInternalField(i)); } } + +THREADED_TEST(ArrayBuffer_NewBackingStore) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + std::shared_ptr backing_store = + v8::ArrayBuffer::NewBackingStore(isolate, 100); + Local ab = v8::ArrayBuffer::New(isolate, backing_store); + CHECK_EQ(backing_store.get(), ab->GetBackingStore().get()); +} + +THREADED_TEST(SharedArrayBuffer_NewBackingStore) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + std::shared_ptr backing_store = + v8::SharedArrayBuffer::NewBackingStore(isolate, 100); + Local ab = + v8::SharedArrayBuffer::New(isolate, backing_store); + CHECK_EQ(backing_store.get(), ab->GetBackingStore().get()); +} + +static void* backing_store_custom_data = nullptr; +static size_t backing_store_custom_length = 0; +static bool backing_store_custom_called = false; +const intptr_t backing_store_custom_deleter_data = 1234567; + +static void BackingStoreCustomDeleter(void* data, size_t length, + void* deleter_data) { + CHECK(!backing_store_custom_called); + CHECK_EQ(backing_store_custom_data, data); + CHECK_EQ(backing_store_custom_length, length); + CHECK_EQ(backing_store_custom_deleter_data, + reinterpret_cast(deleter_data)); + free(data); + backing_store_custom_called = true; +} + +TEST(ArrayBuffer_NewBackingStore_CustomDeleter) { + { + // Create and destroy a backing store. + backing_store_custom_called = false; + backing_store_custom_data = malloc(100); + backing_store_custom_length = 100; + v8::ArrayBuffer::NewBackingStore( + backing_store_custom_data, backing_store_custom_length, + BackingStoreCustomDeleter, + reinterpret_cast(backing_store_custom_deleter_data)); + } + CHECK(backing_store_custom_called); +} + +TEST(SharedArrayBuffer_NewBackingStore_CustomDeleter) { + { + // Create and destroy a backing store. + backing_store_custom_called = false; + backing_store_custom_data = malloc(100); + backing_store_custom_length = 100; + v8::SharedArrayBuffer::NewBackingStore( + backing_store_custom_data, backing_store_custom_length, + BackingStoreCustomDeleter, + reinterpret_cast(backing_store_custom_deleter_data)); + } + CHECK(backing_store_custom_called); +}