[api] Add [Shared]ArrayBuffer::GetBackingStore()
This adds an additional V8 API to get the backing store of an array buffer. Unlike the existing API, the backing store comes wrapped in a std::shared_ptr, making lifetime management with the embedder explicit. This obviates the need for the old GetContents() and Externalize() APIs, which will be deprecated in a future CL. Contributed by titzer@chromium.org Bug: v8:9380 Change-Id: I8a87f5dc141dab684693fe536b636e33f6e45173 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1807354 Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#63883}
This commit is contained in:
parent
cd8100b61e
commit
55c48820f8
@ -381,6 +381,10 @@ V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj);
|
||||
// language mode is strict.
|
||||
V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate);
|
||||
|
||||
// A base class for backing stores, which is needed due to vagaries of
|
||||
// how static casts work with std::shared_ptr.
|
||||
class BackingStoreBase {};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
65
include/v8.h
65
include/v8.h
@ -4711,6 +4711,33 @@ class V8_EXPORT WasmModuleObjectBuilderStreaming final {
|
||||
|
||||
enum class ArrayBufferCreationMode { kInternalized, kExternalized };
|
||||
|
||||
/**
|
||||
* A wrapper around the backing store (i.e. the raw memory) of an array buffer.
|
||||
*
|
||||
* The allocation and destruction of backing stores is generally managed by
|
||||
* V8. Clients should always use standard C++ memory ownership types (i.e.
|
||||
* std::unique_ptr and std::shared_ptr) to manage lifetimes of backing stores
|
||||
* properly, since V8 internal objects may alias backing stores.
|
||||
*/
|
||||
class V8_EXPORT BackingStore : public v8::internal::BackingStoreBase {
|
||||
public:
|
||||
~BackingStore();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void* Data() const;
|
||||
|
||||
/**
|
||||
* The length (in bytes) of this backing store.
|
||||
*/
|
||||
size_t ByteLength() const;
|
||||
|
||||
private:
|
||||
BackingStore();
|
||||
};
|
||||
|
||||
/**
|
||||
* An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5).
|
||||
@ -4878,6 +4905,15 @@ class V8_EXPORT ArrayBuffer : public Object {
|
||||
*/
|
||||
Contents Externalize();
|
||||
|
||||
/**
|
||||
* Marks this ArrayBuffer external given a witness that the embedder
|
||||
* has fetched the backing store using the new GetBackingStore() function.
|
||||
*
|
||||
* With the new lifetime management of backing stores there is no need for
|
||||
* externalizing, so this function exists only to make the transition easier.
|
||||
*/
|
||||
void Externalize(const std::shared_ptr<BackingStore>& backing_store);
|
||||
|
||||
/**
|
||||
* Get a pointer to the ArrayBuffer's underlying memory block without
|
||||
* externalizing it. If the ArrayBuffer is not externalized, this pointer
|
||||
@ -4888,6 +4924,16 @@ class V8_EXPORT ArrayBuffer : public Object {
|
||||
*/
|
||||
Contents GetContents();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* This function replaces both Externalize() and GetContents().
|
||||
*/
|
||||
std::shared_ptr<BackingStore> GetBackingStore();
|
||||
|
||||
V8_INLINE static ArrayBuffer* Cast(Value* obj);
|
||||
|
||||
static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
|
||||
@ -5293,6 +5339,15 @@ class V8_EXPORT SharedArrayBuffer : public Object {
|
||||
*/
|
||||
Contents Externalize();
|
||||
|
||||
/**
|
||||
* Marks this SharedArrayBuffer external given a witness that the embedder
|
||||
* has fetched the backing store using the new GetBackingStore() function.
|
||||
*
|
||||
* With the new lifetime management of backing stores there is no need for
|
||||
* externalizing, so this function exists only to make the transition easier.
|
||||
*/
|
||||
void Externalize(const std::shared_ptr<BackingStore>& backing_store);
|
||||
|
||||
/**
|
||||
* Get a pointer to the ArrayBuffer's underlying memory block without
|
||||
* externalizing it. If the ArrayBuffer is not externalized, this pointer
|
||||
@ -5307,6 +5362,16 @@ class V8_EXPORT SharedArrayBuffer : public Object {
|
||||
*/
|
||||
Contents GetContents();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* This function replaces both Externalize() and GetContents().
|
||||
*/
|
||||
std::shared_ptr<BackingStore> GetBackingStore();
|
||||
|
||||
V8_INLINE static SharedArrayBuffer* Cast(Value* obj);
|
||||
|
||||
static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
|
||||
|
@ -3725,6 +3725,41 @@ void v8::WasmModuleObject::CheckCast(Value* that) {
|
||||
"Could not convert to wasm module object");
|
||||
}
|
||||
|
||||
v8::BackingStore::~BackingStore() {
|
||||
auto i_this = reinterpret_cast<const i::BackingStore*>(this);
|
||||
i_this->~BackingStore(); // manually call internal destructor
|
||||
}
|
||||
|
||||
void* v8::BackingStore::Data() const {
|
||||
return reinterpret_cast<const i::BackingStore*>(this)->buffer_start();
|
||||
}
|
||||
|
||||
size_t v8::BackingStore::ByteLength() const {
|
||||
return reinterpret_cast<const i::BackingStore*>(this)->byte_length();
|
||||
}
|
||||
|
||||
std::shared_ptr<v8::BackingStore> v8::ArrayBuffer::GetBackingStore() {
|
||||
i::Handle<i::JSArrayBuffer> self = Utils::OpenHandle(this);
|
||||
std::shared_ptr<i::BackingStore> backing_store = self->GetBackingStore();
|
||||
if (!backing_store) {
|
||||
backing_store = i::BackingStore::NewEmptyBackingStore();
|
||||
}
|
||||
i::GlobalBackingStoreRegistry::Register(backing_store);
|
||||
std::shared_ptr<i::BackingStoreBase> bs_base = backing_store;
|
||||
return std::static_pointer_cast<v8::BackingStore>(bs_base);
|
||||
}
|
||||
|
||||
std::shared_ptr<v8::BackingStore> v8::SharedArrayBuffer::GetBackingStore() {
|
||||
i::Handle<i::JSArrayBuffer> self = Utils::OpenHandle(this);
|
||||
std::shared_ptr<i::BackingStore> backing_store = self->GetBackingStore();
|
||||
if (!backing_store) {
|
||||
backing_store = i::BackingStore::NewEmptyBackingStore();
|
||||
}
|
||||
i::GlobalBackingStoreRegistry::Register(backing_store);
|
||||
std::shared_ptr<i::BackingStoreBase> bs_base = backing_store;
|
||||
return std::static_pointer_cast<v8::BackingStore>(bs_base);
|
||||
}
|
||||
|
||||
void v8::ArrayBuffer::CheckCast(Value* that) {
|
||||
i::Handle<i::Object> obj = Utils::OpenHandle(that);
|
||||
Utils::ApiCheck(
|
||||
@ -7205,10 +7240,10 @@ namespace {
|
||||
// The backing store deleter just deletes the indirection, which downrefs
|
||||
// the shared pointer. It will get collected normally.
|
||||
void BackingStoreDeleter(void* buffer, size_t length, void* info) {
|
||||
auto bs_indirection =
|
||||
std::shared_ptr<i::BackingStore>* bs_indirection =
|
||||
reinterpret_cast<std::shared_ptr<i::BackingStore>*>(info);
|
||||
if (bs_indirection) {
|
||||
auto backing_store = bs_indirection->get();
|
||||
i::BackingStore* backing_store = bs_indirection->get();
|
||||
TRACE_BS("API:delete bs=%p mem=%p (length=%zu)\n", backing_store,
|
||||
backing_store->buffer_start(), backing_store->byte_length());
|
||||
USE(backing_store);
|
||||
@ -7290,6 +7325,15 @@ v8::ArrayBuffer::Contents v8::ArrayBuffer::Externalize() {
|
||||
return GetContents(true);
|
||||
}
|
||||
|
||||
void v8::ArrayBuffer::Externalize(
|
||||
const std::shared_ptr<BackingStore>& backing_store) {
|
||||
i::Handle<i::JSArrayBuffer> self = Utils::OpenHandle(this);
|
||||
Utils::ApiCheck(!self->is_external(), "v8_ArrayBuffer_Externalize",
|
||||
"ArrayBuffer already externalized");
|
||||
self->set_is_external(true);
|
||||
DCHECK_EQ(self->backing_store(), backing_store->Data());
|
||||
}
|
||||
|
||||
v8::ArrayBuffer::Contents v8::ArrayBuffer::GetContents() {
|
||||
return GetContents(false);
|
||||
}
|
||||
@ -7576,6 +7620,16 @@ v8::SharedArrayBuffer::Contents v8::SharedArrayBuffer::Externalize() {
|
||||
return GetContents(true);
|
||||
}
|
||||
|
||||
void v8::SharedArrayBuffer::Externalize(
|
||||
const std::shared_ptr<BackingStore>& backing_store) {
|
||||
i::Handle<i::JSArrayBuffer> self = Utils::OpenHandle(this);
|
||||
Utils::ApiCheck(!self->is_external(), "v8_SharedArrayBuffer_Externalize",
|
||||
"SharedArrayBuffer already externalized");
|
||||
self->set_is_external(true);
|
||||
|
||||
DCHECK_EQ(self->backing_store(), backing_store->Data());
|
||||
}
|
||||
|
||||
v8::SharedArrayBuffer::Contents v8::SharedArrayBuffer::GetContents() {
|
||||
return GetContents(false);
|
||||
}
|
||||
|
70
src/d8/d8.cc
70
src/d8/d8.cc
@ -346,7 +346,6 @@ Global<Function> Shell::stringify_function_;
|
||||
base::LazyMutex Shell::workers_mutex_;
|
||||
bool Shell::allow_new_workers_ = true;
|
||||
std::unordered_set<std::shared_ptr<Worker>> Shell::running_workers_;
|
||||
std::vector<ExternalizedContents> Shell::externalized_contents_;
|
||||
std::atomic<bool> Shell::script_executed_{false};
|
||||
base::LazyMutex Shell::isolate_status_lock_;
|
||||
std::map<v8::Isolate*, bool> Shell::isolate_status_;
|
||||
@ -2586,12 +2585,6 @@ void SourceGroup::JoinThread() {
|
||||
thread_->Join();
|
||||
}
|
||||
|
||||
ExternalizedContents::~ExternalizedContents() {
|
||||
if (data_ != nullptr) {
|
||||
deleter_(data_, length_, deleter_data_);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializationDataQueue::Enqueue(std::unique_ptr<SerializationData> data) {
|
||||
base::MutexGuard lock_guard(&mutex_);
|
||||
data_.push_back(std::move(data));
|
||||
@ -3171,22 +3164,10 @@ class Serializer : public ValueSerializer::Delegate {
|
||||
|
||||
std::unique_ptr<SerializationData> Release() { return std::move(data_); }
|
||||
|
||||
void AppendExternalizedContentsTo(std::vector<ExternalizedContents>* to) {
|
||||
for (auto& contents : externalized_contents_) {
|
||||
auto bs_indirection = reinterpret_cast<std::shared_ptr<i::BackingStore>*>(
|
||||
contents.DeleterData());
|
||||
if (bs_indirection) {
|
||||
auto backing_store = bs_indirection->get();
|
||||
TRACE_BS("d8:append bs=%p mem=%p (length=%zu)\n", backing_store,
|
||||
backing_store->buffer_start(), backing_store->byte_length());
|
||||
USE(backing_store);
|
||||
}
|
||||
}
|
||||
|
||||
to->insert(to->end(),
|
||||
std::make_move_iterator(externalized_contents_.begin()),
|
||||
std::make_move_iterator(externalized_contents_.end()));
|
||||
externalized_contents_.clear();
|
||||
void AppendBackingStoresTo(std::vector<std::shared_ptr<BackingStore>>* to) {
|
||||
to->insert(to->end(), std::make_move_iterator(backing_stores_.begin()),
|
||||
std::make_move_iterator(backing_stores_.end()));
|
||||
backing_stores_.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -3206,8 +3187,8 @@ class Serializer : public ValueSerializer::Delegate {
|
||||
|
||||
size_t index = shared_array_buffers_.size();
|
||||
shared_array_buffers_.emplace_back(isolate_, shared_array_buffer);
|
||||
data_->shared_array_buffer_contents_.push_back(
|
||||
MaybeExternalize(shared_array_buffer));
|
||||
data_->sab_backing_stores_.push_back(
|
||||
shared_array_buffer->GetBackingStore());
|
||||
return Just<uint32_t>(static_cast<uint32_t>(index));
|
||||
}
|
||||
|
||||
@ -3278,17 +3259,6 @@ class Serializer : public ValueSerializer::Delegate {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename T::Contents MaybeExternalize(Local<T> array_buffer) {
|
||||
if (array_buffer->IsExternal()) {
|
||||
return array_buffer->GetContents();
|
||||
} else {
|
||||
typename T::Contents contents = array_buffer->Externalize();
|
||||
externalized_contents_.emplace_back(contents);
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<bool> FinalizeTransfer() {
|
||||
for (const auto& global_array_buffer : array_buffers_) {
|
||||
Local<ArrayBuffer> array_buffer =
|
||||
@ -3298,9 +3268,12 @@ class Serializer : public ValueSerializer::Delegate {
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
ArrayBuffer::Contents contents = MaybeExternalize(array_buffer);
|
||||
auto backing_store = array_buffer->GetBackingStore();
|
||||
if (!array_buffer->IsExternal()) {
|
||||
array_buffer->Externalize(backing_store);
|
||||
}
|
||||
data_->backing_stores_.push_back(std::move(backing_store));
|
||||
array_buffer->Detach();
|
||||
data_->array_buffer_contents_.push_back(contents);
|
||||
}
|
||||
|
||||
return Just(true);
|
||||
@ -3312,7 +3285,7 @@ class Serializer : public ValueSerializer::Delegate {
|
||||
std::vector<Global<ArrayBuffer>> array_buffers_;
|
||||
std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
|
||||
std::vector<Global<WasmModuleObject>> wasm_modules_;
|
||||
std::vector<ExternalizedContents> externalized_contents_;
|
||||
std::vector<std::shared_ptr<v8::BackingStore>> backing_stores_;
|
||||
size_t current_memory_usage_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Serializer);
|
||||
@ -3334,9 +3307,9 @@ class Deserializer : public ValueDeserializer::Delegate {
|
||||
}
|
||||
|
||||
uint32_t index = 0;
|
||||
for (const auto& contents : data_->array_buffer_contents()) {
|
||||
Local<ArrayBuffer> array_buffer =
|
||||
ArrayBuffer::New(isolate_, contents.Data(), contents.ByteLength());
|
||||
for (const auto& backing_store : data_->backing_stores()) {
|
||||
Local<ArrayBuffer> array_buffer = ArrayBuffer::New(
|
||||
isolate_, backing_store->Data(), backing_store->ByteLength());
|
||||
deserializer_.TransferArrayBuffer(index++, array_buffer);
|
||||
}
|
||||
|
||||
@ -3346,11 +3319,10 @@ class Deserializer : public ValueDeserializer::Delegate {
|
||||
MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
|
||||
Isolate* isolate, uint32_t clone_id) override {
|
||||
DCHECK_NOT_NULL(data_);
|
||||
if (clone_id < data_->shared_array_buffer_contents().size()) {
|
||||
const SharedArrayBuffer::Contents contents =
|
||||
data_->shared_array_buffer_contents().at(clone_id);
|
||||
return SharedArrayBuffer::New(isolate_, contents.Data(),
|
||||
contents.ByteLength());
|
||||
if (clone_id < data_->sab_backing_stores().size()) {
|
||||
auto backing_store = data_->sab_backing_stores().at(clone_id);
|
||||
return SharedArrayBuffer::New(isolate_, backing_store->Data(),
|
||||
backing_store->ByteLength());
|
||||
}
|
||||
return MaybeLocal<SharedArrayBuffer>();
|
||||
}
|
||||
@ -3382,9 +3354,6 @@ std::unique_ptr<SerializationData> Shell::SerializeValue(
|
||||
if (serializer.WriteValue(context, value, transfer).To(&ok)) {
|
||||
data = serializer.Release();
|
||||
}
|
||||
// Append externalized contents even when WriteValue fails.
|
||||
base::MutexGuard lock_guard(workers_mutex_.Pointer());
|
||||
serializer.AppendExternalizedContentsTo(&externalized_contents_);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -3426,7 +3395,6 @@ void Shell::WaitForRunningWorkers() {
|
||||
base::MutexGuard lock_guard(workers_mutex_.Pointer());
|
||||
DCHECK(running_workers_.empty());
|
||||
allow_new_workers_ = true;
|
||||
externalized_contents_.clear();
|
||||
}
|
||||
|
||||
int Shell::Main(int argc, char* argv[]) {
|
||||
|
64
src/d8/d8.h
64
src/d8/d8.h
@ -111,68 +111,17 @@ class SourceGroup {
|
||||
int end_offset_;
|
||||
};
|
||||
|
||||
// The backing store of an ArrayBuffer or SharedArrayBuffer, after
|
||||
// Externalize() has been called on it.
|
||||
class ExternalizedContents {
|
||||
public:
|
||||
explicit ExternalizedContents(const ArrayBuffer::Contents& contents)
|
||||
: data_(contents.Data()),
|
||||
length_(contents.ByteLength()),
|
||||
deleter_(contents.Deleter()),
|
||||
deleter_data_(contents.DeleterData()) {}
|
||||
explicit ExternalizedContents(const SharedArrayBuffer::Contents& contents)
|
||||
: data_(contents.Data()),
|
||||
length_(contents.ByteLength()),
|
||||
deleter_(contents.Deleter()),
|
||||
deleter_data_(contents.DeleterData()) {}
|
||||
ExternalizedContents(ExternalizedContents&& other) V8_NOEXCEPT
|
||||
: data_(other.data_),
|
||||
length_(other.length_),
|
||||
deleter_(other.deleter_),
|
||||
deleter_data_(other.deleter_data_) {
|
||||
other.data_ = nullptr;
|
||||
other.length_ = 0;
|
||||
other.deleter_ = nullptr;
|
||||
other.deleter_data_ = nullptr;
|
||||
}
|
||||
ExternalizedContents& operator=(ExternalizedContents&& other) V8_NOEXCEPT {
|
||||
if (this != &other) {
|
||||
data_ = other.data_;
|
||||
length_ = other.length_;
|
||||
deleter_ = other.deleter_;
|
||||
deleter_data_ = other.deleter_data_;
|
||||
other.data_ = nullptr;
|
||||
other.length_ = 0;
|
||||
other.deleter_ = nullptr;
|
||||
other.deleter_data_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
~ExternalizedContents();
|
||||
|
||||
void* DeleterData() { return deleter_data_; }
|
||||
|
||||
private:
|
||||
void* data_;
|
||||
size_t length_;
|
||||
ArrayBuffer::Contents::DeleterCallback deleter_;
|
||||
void* deleter_data_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExternalizedContents);
|
||||
};
|
||||
|
||||
class SerializationData {
|
||||
public:
|
||||
SerializationData() : size_(0) {}
|
||||
|
||||
uint8_t* data() { return data_.get(); }
|
||||
size_t size() { return size_; }
|
||||
const std::vector<ArrayBuffer::Contents>& array_buffer_contents() {
|
||||
return array_buffer_contents_;
|
||||
const std::vector<std::shared_ptr<v8::BackingStore>>& backing_stores() {
|
||||
return backing_stores_;
|
||||
}
|
||||
const std::vector<SharedArrayBuffer::Contents>&
|
||||
shared_array_buffer_contents() {
|
||||
return shared_array_buffer_contents_;
|
||||
const std::vector<std::shared_ptr<v8::BackingStore>>& sab_backing_stores() {
|
||||
return sab_backing_stores_;
|
||||
}
|
||||
const std::vector<WasmModuleObject::TransferrableModule>&
|
||||
transferrable_modules() {
|
||||
@ -186,8 +135,8 @@ class SerializationData {
|
||||
|
||||
std::unique_ptr<uint8_t, DataDeleter> data_;
|
||||
size_t size_;
|
||||
std::vector<ArrayBuffer::Contents> array_buffer_contents_;
|
||||
std::vector<SharedArrayBuffer::Contents> shared_array_buffer_contents_;
|
||||
std::vector<std::shared_ptr<v8::BackingStore>> backing_stores_;
|
||||
std::vector<std::shared_ptr<v8::BackingStore>> sab_backing_stores_;
|
||||
std::vector<WasmModuleObject::TransferrableModule> transferrable_modules_;
|
||||
|
||||
private:
|
||||
@ -523,7 +472,6 @@ class Shell : public i::AllStatic {
|
||||
static base::LazyMutex workers_mutex_; // Guards the following members.
|
||||
static bool allow_new_workers_;
|
||||
static std::unordered_set<std::shared_ptr<Worker>> running_workers_;
|
||||
static std::vector<ExternalizedContents> externalized_contents_;
|
||||
|
||||
// Multiple isolates may update this flag concurrently.
|
||||
static std::atomic<bool> script_executed_;
|
||||
|
@ -691,7 +691,7 @@ v8::Local<v8::Object> V8Console::createCommandLineAPI(
|
||||
|
||||
v8::Local<v8::ArrayBuffer> data =
|
||||
v8::ArrayBuffer::New(isolate, sizeof(CommandLineAPIData));
|
||||
*static_cast<CommandLineAPIData*>(data->GetContents().Data()) =
|
||||
*static_cast<CommandLineAPIData*>(data->GetBackingStore()->Data()) =
|
||||
CommandLineAPIData(this, sessionId);
|
||||
createBoundFunctionProperty(context, commandLineAPI, data, "dir",
|
||||
&V8Console::call<&V8Console::Dir>,
|
||||
|
@ -106,14 +106,14 @@ class V8Console : public v8::debug::ConsoleDelegate {
|
||||
int)>
|
||||
static void call(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
CommandLineAPIData* data = static_cast<CommandLineAPIData*>(
|
||||
info.Data().As<v8::ArrayBuffer>()->GetContents().Data());
|
||||
info.Data().As<v8::ArrayBuffer>()->GetBackingStore()->Data());
|
||||
(data->first->*func)(info, data->second);
|
||||
}
|
||||
template <void (V8Console::*func)(const v8::debug::ConsoleCallArguments&,
|
||||
const v8::debug::ConsoleContext&)>
|
||||
static void call(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
CommandLineAPIData* data = static_cast<CommandLineAPIData*>(
|
||||
info.Data().As<v8::ArrayBuffer>()->GetContents().Data());
|
||||
info.Data().As<v8::ArrayBuffer>()->GetBackingStore()->Data());
|
||||
v8::debug::ConsoleCallArguments args(info);
|
||||
(data->first->*func)(args, v8::debug::ConsoleContext());
|
||||
}
|
||||
|
@ -454,6 +454,18 @@ std::unique_ptr<BackingStore> BackingStore::WrapAllocation(
|
||||
return std::unique_ptr<BackingStore>(result);
|
||||
}
|
||||
|
||||
std::unique_ptr<BackingStore> BackingStore::NewEmptyBackingStore() {
|
||||
auto result = new BackingStore(nullptr, // start
|
||||
0, // length
|
||||
0, // capacity
|
||||
SharedFlag::kNotShared, // shared
|
||||
false, // is_wasm_memory
|
||||
false, // free_on_destruct
|
||||
false); // has_guard_regions
|
||||
|
||||
return std::unique_ptr<BackingStore>(result);
|
||||
}
|
||||
|
||||
void* BackingStore::get_v8_api_array_buffer_allocator() {
|
||||
CHECK(!is_wasm_memory_);
|
||||
auto array_buffer_allocator =
|
||||
@ -485,7 +497,7 @@ inline GlobalBackingStoreRegistryImpl* impl() {
|
||||
|
||||
void GlobalBackingStoreRegistry::Register(
|
||||
std::shared_ptr<BackingStore> backing_store) {
|
||||
if (!backing_store) return;
|
||||
if (!backing_store || !backing_store->buffer_start()) return;
|
||||
|
||||
base::MutexGuard scope_lock(&impl()->mutex_);
|
||||
if (backing_store->globally_registered_) return;
|
||||
@ -501,6 +513,8 @@ void GlobalBackingStoreRegistry::Register(
|
||||
void GlobalBackingStoreRegistry::Unregister(BackingStore* backing_store) {
|
||||
if (!backing_store->globally_registered_) return;
|
||||
|
||||
DCHECK_NOT_NULL(backing_store->buffer_start());
|
||||
|
||||
base::MutexGuard scope_lock(&impl()->mutex_);
|
||||
const auto& result = impl()->map_.find(backing_store->buffer_start());
|
||||
if (result != impl()->map_.end()) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/v8-internal.h"
|
||||
#include "src/handles/handles.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -34,7 +35,7 @@ struct SharedWasmMemoryData;
|
||||
// and the destructor frees the memory (and page allocation if necessary).
|
||||
// Backing stores can also *wrap* embedder-allocated memory. In this case,
|
||||
// they do not own the memory, and upon destruction, they do not deallocate it.
|
||||
class V8_EXPORT_PRIVATE BackingStore {
|
||||
class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase {
|
||||
public:
|
||||
~BackingStore();
|
||||
|
||||
@ -62,6 +63,9 @@ class V8_EXPORT_PRIVATE BackingStore {
|
||||
SharedFlag shared,
|
||||
bool free_on_destruct);
|
||||
|
||||
// Create an empty backing store.
|
||||
static std::unique_ptr<BackingStore> NewEmptyBackingStore();
|
||||
|
||||
// Accessors.
|
||||
void* buffer_start() const { return buffer_start_; }
|
||||
size_t byte_length() const {
|
||||
|
@ -42,21 +42,21 @@ void JSArrayBuffer::SetupEmpty(SharedFlag shared) {
|
||||
set_byte_length(0);
|
||||
}
|
||||
|
||||
std::shared_ptr<BackingStore> JSArrayBuffer::Detach(
|
||||
bool force_for_wasm_memory) {
|
||||
if (was_detached()) return nullptr;
|
||||
void JSArrayBuffer::Detach(bool force_for_wasm_memory) {
|
||||
if (was_detached()) return;
|
||||
|
||||
if (force_for_wasm_memory) {
|
||||
// Skip the is_detachable() check.
|
||||
} else if (!is_detachable()) {
|
||||
// Not detachable, do nothing.
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
Isolate* const isolate = GetIsolate();
|
||||
auto backing_store = isolate->heap()->UnregisterBackingStore(*this);
|
||||
CHECK_IMPLIES(force_for_wasm_memory && backing_store,
|
||||
backing_store->is_wasm_memory());
|
||||
if (backing_store()) {
|
||||
auto backing_store = isolate->heap()->UnregisterBackingStore(*this);
|
||||
CHECK_IMPLIES(force_for_wasm_memory, backing_store->is_wasm_memory());
|
||||
}
|
||||
|
||||
if (isolate->IsArrayBufferDetachingIntact()) {
|
||||
isolate->InvalidateArrayBufferDetachingProtector();
|
||||
@ -67,8 +67,6 @@ std::shared_ptr<BackingStore> JSArrayBuffer::Detach(
|
||||
set_backing_store(nullptr);
|
||||
set_byte_length(0);
|
||||
set_was_detached(true);
|
||||
|
||||
return backing_store;
|
||||
}
|
||||
|
||||
void JSArrayBuffer::Attach(std::shared_ptr<BackingStore> backing_store) {
|
||||
|
@ -84,19 +84,17 @@ class JSArrayBuffer : public JSObject {
|
||||
// (note: this registers it with src/heap/array-buffer-tracker.h)
|
||||
V8_EXPORT_PRIVATE void Attach(std::shared_ptr<BackingStore> backing_store);
|
||||
|
||||
// Detach the backing store from this array buffer if it is detachable
|
||||
// and return a reference to the backing store object. This sets the
|
||||
// internal pointer and length to 0 and unregisters the backing store
|
||||
// from the array buffer tracker.
|
||||
// If the array buffer is not detachable, this is a nop.
|
||||
// Detach the backing store from this array buffer if it is detachable.
|
||||
// This sets the internal pointer and length to 0 and unregisters the backing
|
||||
// store from the array buffer tracker. If the array buffer is not detachable,
|
||||
// this is a nop.
|
||||
//
|
||||
// Array buffers that wrap wasm memory objects are special in that they
|
||||
// are normally not detachable, but can become detached as a side effect
|
||||
// of growing the underlying memory object. The {force_for_wasm_memory} flag
|
||||
// is used by the implementation of Wasm memory growth in order to bypass the
|
||||
// non-detachable check.
|
||||
V8_EXPORT_PRIVATE std::shared_ptr<BackingStore> Detach(
|
||||
bool force_for_wasm_memory = false);
|
||||
V8_EXPORT_PRIVATE void Detach(bool force_for_wasm_memory = false);
|
||||
|
||||
// Get a reference to backing store of this array buffer, if there is a
|
||||
// backing store. Returns nullptr if there is no backing store (e.g. detached
|
||||
|
@ -206,20 +206,20 @@ i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
|
||||
if (source->IsArrayBuffer()) {
|
||||
// A raw array buffer was passed.
|
||||
Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source);
|
||||
ArrayBuffer::Contents contents = buffer->GetContents();
|
||||
auto backing_store = buffer->GetBackingStore();
|
||||
|
||||
start = reinterpret_cast<const uint8_t*>(contents.Data());
|
||||
length = contents.ByteLength();
|
||||
start = reinterpret_cast<const uint8_t*>(backing_store->Data());
|
||||
length = backing_store->ByteLength();
|
||||
*is_shared = buffer->IsSharedArrayBuffer();
|
||||
} else if (source->IsTypedArray()) {
|
||||
// A TypedArray was passed.
|
||||
Local<TypedArray> array = Local<TypedArray>::Cast(source);
|
||||
Local<ArrayBuffer> buffer = array->Buffer();
|
||||
|
||||
ArrayBuffer::Contents contents = buffer->GetContents();
|
||||
auto backing_store = buffer->GetBackingStore();
|
||||
|
||||
start =
|
||||
reinterpret_cast<const uint8_t*>(contents.Data()) + array->ByteOffset();
|
||||
start = reinterpret_cast<const uint8_t*>(backing_store->Data()) +
|
||||
array->ByteOffset();
|
||||
length = array->ByteLength();
|
||||
*is_shared = buffer->IsSharedArrayBuffer();
|
||||
} else {
|
||||
|
@ -13,49 +13,6 @@ using ::v8::Value;
|
||||
|
||||
namespace {
|
||||
|
||||
class ScopedArrayBufferContents {
|
||||
public:
|
||||
explicit ScopedArrayBufferContents(const v8::ArrayBuffer::Contents& contents)
|
||||
: contents_(contents) {}
|
||||
~ScopedArrayBufferContents() {
|
||||
contents_.Deleter()(contents_.Data(), contents_.ByteLength(),
|
||||
contents_.DeleterData());
|
||||
}
|
||||
void* Data() const { return contents_.Data(); }
|
||||
size_t ByteLength() const { return contents_.ByteLength(); }
|
||||
|
||||
void* AllocationBase() const { return contents_.AllocationBase(); }
|
||||
size_t AllocationLength() const { return contents_.AllocationLength(); }
|
||||
v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const {
|
||||
return contents_.AllocationMode();
|
||||
}
|
||||
|
||||
private:
|
||||
const v8::ArrayBuffer::Contents contents_;
|
||||
};
|
||||
|
||||
class ScopedSharedArrayBufferContents {
|
||||
public:
|
||||
explicit ScopedSharedArrayBufferContents(
|
||||
const v8::SharedArrayBuffer::Contents& contents)
|
||||
: contents_(contents) {}
|
||||
~ScopedSharedArrayBufferContents() {
|
||||
contents_.Deleter()(contents_.Data(), contents_.ByteLength(),
|
||||
contents_.DeleterData());
|
||||
}
|
||||
void* Data() const { return contents_.Data(); }
|
||||
size_t ByteLength() const { return contents_.ByteLength(); }
|
||||
|
||||
void* AllocationBase() const { return contents_.AllocationBase(); }
|
||||
size_t AllocationLength() const { return contents_.AllocationLength(); }
|
||||
v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const {
|
||||
return contents_.AllocationMode();
|
||||
}
|
||||
|
||||
private:
|
||||
const v8::SharedArrayBuffer::Contents contents_;
|
||||
};
|
||||
|
||||
void CheckDataViewIsDetached(v8::Local<v8::DataView> dv) {
|
||||
CHECK_EQ(0, static_cast<int>(dv->ByteLength()));
|
||||
CHECK_EQ(0, static_cast<int>(dv->ByteOffset()));
|
||||
@ -89,6 +46,20 @@ Local<TypedArray> CreateAndCheck(Local<v8::ArrayBuffer> ab, int byteOffset,
|
||||
return ta;
|
||||
}
|
||||
|
||||
std::shared_ptr<v8::BackingStore> Externalize(Local<v8::ArrayBuffer> ab) {
|
||||
std::shared_ptr<v8::BackingStore> backing_store = ab->GetBackingStore();
|
||||
ab->Externalize(backing_store);
|
||||
CHECK(ab->IsExternal());
|
||||
return backing_store;
|
||||
}
|
||||
|
||||
std::shared_ptr<v8::BackingStore> Externalize(Local<v8::SharedArrayBuffer> ab) {
|
||||
std::shared_ptr<v8::BackingStore> backing_store = ab->GetBackingStore();
|
||||
ab->Externalize(backing_store);
|
||||
CHECK(ab->IsExternal());
|
||||
return backing_store;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
THREADED_TEST(ArrayBuffer_ApiInternalToExternal) {
|
||||
@ -98,15 +69,14 @@ THREADED_TEST(ArrayBuffer_ApiInternalToExternal) {
|
||||
|
||||
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
|
||||
CheckInternalFieldsAreZero(ab);
|
||||
CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
|
||||
CHECK_EQ(1024, ab->ByteLength());
|
||||
CHECK(!ab->IsExternal());
|
||||
CcTest::CollectAllGarbage();
|
||||
|
||||
ScopedArrayBufferContents ab_contents(ab->Externalize());
|
||||
CHECK(ab->IsExternal());
|
||||
std::shared_ptr<v8::BackingStore> backing_store = Externalize(ab);
|
||||
CHECK_EQ(1024, backing_store->ByteLength());
|
||||
|
||||
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
|
||||
uint8_t* data = static_cast<uint8_t*>(ab_contents.Data());
|
||||
uint8_t* data = static_cast<uint8_t*>(backing_store->Data());
|
||||
CHECK_NOT_NULL(data);
|
||||
CHECK(env->Global()->Set(env.local(), v8_str("ab"), ab).FromJust());
|
||||
|
||||
@ -139,10 +109,9 @@ THREADED_TEST(ArrayBuffer_JSInternalToExternal) {
|
||||
"u8_a[1] = 0xFF; u8_a.buffer");
|
||||
Local<v8::ArrayBuffer> ab1 = Local<v8::ArrayBuffer>::Cast(result);
|
||||
CheckInternalFieldsAreZero(ab1);
|
||||
CHECK_EQ(2, static_cast<int>(ab1->ByteLength()));
|
||||
CHECK_EQ(2, ab1->ByteLength());
|
||||
CHECK(!ab1->IsExternal());
|
||||
ScopedArrayBufferContents ab1_contents(ab1->Externalize());
|
||||
CHECK(ab1->IsExternal());
|
||||
std::shared_ptr<v8::BackingStore> backing_store = Externalize(ab1);
|
||||
|
||||
result = CompileRun("ab1.byteLength");
|
||||
CHECK_EQ(2, result->Int32Value(env.local()).FromJust());
|
||||
@ -158,8 +127,8 @@ THREADED_TEST(ArrayBuffer_JSInternalToExternal) {
|
||||
result = CompileRun("u8_b[1]");
|
||||
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
|
||||
|
||||
CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength()));
|
||||
uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data());
|
||||
CHECK_EQ(2, backing_store->ByteLength());
|
||||
uint8_t* ab1_data = static_cast<uint8_t*>(backing_store->Data());
|
||||
CHECK_EQ(0xBB, ab1_data[0]);
|
||||
CHECK_EQ(0xFF, ab1_data[1]);
|
||||
ab1_data[0] = 0xCC;
|
||||
@ -178,7 +147,7 @@ THREADED_TEST(ArrayBuffer_External) {
|
||||
Local<v8::ArrayBuffer> ab3 =
|
||||
v8::ArrayBuffer::New(isolate, my_data.begin(), 100);
|
||||
CheckInternalFieldsAreZero(ab3);
|
||||
CHECK_EQ(100, static_cast<int>(ab3->ByteLength()));
|
||||
CHECK_EQ(100, ab3->ByteLength());
|
||||
CHECK(ab3->IsExternal());
|
||||
|
||||
CHECK(env->Global()->Set(env.local(), v8_str("ab3"), ab3).FromJust());
|
||||
@ -248,12 +217,12 @@ THREADED_TEST(ArrayBuffer_DetachingApi) {
|
||||
|
||||
v8::Local<v8::DataView> dv = v8::DataView::New(buffer, 1, 1023);
|
||||
CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv);
|
||||
CHECK_EQ(1, static_cast<int>(dv->ByteOffset()));
|
||||
CHECK_EQ(1023, static_cast<int>(dv->ByteLength()));
|
||||
CHECK_EQ(1, dv->ByteOffset());
|
||||
CHECK_EQ(1023, dv->ByteLength());
|
||||
|
||||
ScopedArrayBufferContents contents(buffer->Externalize());
|
||||
Externalize(buffer);
|
||||
buffer->Detach();
|
||||
CHECK_EQ(0, static_cast<int>(buffer->ByteLength()));
|
||||
CHECK_EQ(0, buffer->ByteLength());
|
||||
CheckIsDetached(u8a);
|
||||
CheckIsDetached(u8c);
|
||||
CheckIsDetached(i8a);
|
||||
@ -289,9 +258,9 @@ THREADED_TEST(ArrayBuffer_DetachingScript) {
|
||||
|
||||
v8::Local<v8::DataView> dv = v8::Local<v8::DataView>::Cast(CompileRun("dv"));
|
||||
|
||||
ScopedArrayBufferContents contents(ab->Externalize());
|
||||
Externalize(ab);
|
||||
ab->Detach();
|
||||
CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
|
||||
CHECK_EQ(0, ab->ByteLength());
|
||||
CHECK_EQ(0, v8_run_int32value(v8_compile("ab.byteLength")));
|
||||
|
||||
CheckIsTypedArrayVarDetached("u8a");
|
||||
@ -308,6 +277,7 @@ THREADED_TEST(ArrayBuffer_DetachingScript) {
|
||||
CheckDataViewIsDetached(dv);
|
||||
}
|
||||
|
||||
// TODO(v8:9380) the Contents data structure should be deprecated.
|
||||
THREADED_TEST(ArrayBuffer_AllocationInformation) {
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
@ -315,7 +285,7 @@ THREADED_TEST(ArrayBuffer_AllocationInformation) {
|
||||
|
||||
const size_t ab_size = 1024;
|
||||
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, ab_size);
|
||||
ScopedArrayBufferContents contents(ab->Externalize());
|
||||
v8::ArrayBuffer::Contents contents(ab->GetContents());
|
||||
|
||||
// Array buffers should have normal allocation mode.
|
||||
CHECK_EQ(contents.AllocationMode(),
|
||||
@ -335,13 +305,13 @@ THREADED_TEST(ArrayBuffer_ExternalizeEmpty) {
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 0);
|
||||
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 2);
|
||||
CheckInternalFieldsAreZero(ab);
|
||||
CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
|
||||
CHECK_EQ(2, ab->ByteLength());
|
||||
CHECK(!ab->IsExternal());
|
||||
|
||||
// Externalize the buffer (taking ownership of the backing store memory).
|
||||
ScopedArrayBufferContents ab_contents(ab->Externalize());
|
||||
std::shared_ptr<v8::BackingStore> backing_store = Externalize(ab);
|
||||
|
||||
Local<v8::Uint8Array> u8a = v8::Uint8Array::New(ab, 0, 0);
|
||||
// Calling Buffer() will materialize the ArrayBuffer (transitioning it from
|
||||
@ -350,6 +320,7 @@ THREADED_TEST(ArrayBuffer_ExternalizeEmpty) {
|
||||
USE(u8a->Buffer());
|
||||
|
||||
CHECK(ab->IsExternal());
|
||||
CHECK_EQ(2, backing_store->ByteLength());
|
||||
}
|
||||
|
||||
THREADED_TEST(SharedArrayBuffer_ApiInternalToExternal) {
|
||||
@ -360,15 +331,14 @@ THREADED_TEST(SharedArrayBuffer_ApiInternalToExternal) {
|
||||
|
||||
Local<v8::SharedArrayBuffer> ab = v8::SharedArrayBuffer::New(isolate, 1024);
|
||||
CheckInternalFieldsAreZero(ab);
|
||||
CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
|
||||
CHECK_EQ(1024, ab->ByteLength());
|
||||
CHECK(!ab->IsExternal());
|
||||
CcTest::CollectAllGarbage();
|
||||
|
||||
ScopedSharedArrayBufferContents ab_contents(ab->Externalize());
|
||||
CHECK(ab->IsExternal());
|
||||
std::shared_ptr<v8::BackingStore> backing_store = Externalize(ab);
|
||||
|
||||
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
|
||||
uint8_t* data = static_cast<uint8_t*>(ab_contents.Data());
|
||||
CHECK_EQ(1024, backing_store->ByteLength());
|
||||
uint8_t* data = static_cast<uint8_t*>(backing_store->Data());
|
||||
CHECK_NOT_NULL(data);
|
||||
CHECK(env->Global()->Set(env.local(), v8_str("ab"), ab).FromJust());
|
||||
|
||||
@ -402,10 +372,9 @@ THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) {
|
||||
"u8_a[1] = 0xFF; u8_a.buffer");
|
||||
Local<v8::SharedArrayBuffer> ab1 = Local<v8::SharedArrayBuffer>::Cast(result);
|
||||
CheckInternalFieldsAreZero(ab1);
|
||||
CHECK_EQ(2, static_cast<int>(ab1->ByteLength()));
|
||||
CHECK_EQ(2, ab1->ByteLength());
|
||||
CHECK(!ab1->IsExternal());
|
||||
ScopedSharedArrayBufferContents ab1_contents(ab1->Externalize());
|
||||
CHECK(ab1->IsExternal());
|
||||
std::shared_ptr<v8::BackingStore> backing_store = Externalize(ab1);
|
||||
|
||||
result = CompileRun("ab1.byteLength");
|
||||
CHECK_EQ(2, result->Int32Value(env.local()).FromJust());
|
||||
@ -421,8 +390,8 @@ THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) {
|
||||
result = CompileRun("u8_b[1]");
|
||||
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
|
||||
|
||||
CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength()));
|
||||
uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data());
|
||||
CHECK_EQ(2, backing_store->ByteLength());
|
||||
uint8_t* ab1_data = static_cast<uint8_t*>(backing_store->Data());
|
||||
CHECK_EQ(0xBB, ab1_data[0]);
|
||||
CHECK_EQ(0xFF, ab1_data[1]);
|
||||
ab1_data[0] = 0xCC;
|
||||
@ -464,6 +433,7 @@ THREADED_TEST(SharedArrayBuffer_External) {
|
||||
CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust());
|
||||
}
|
||||
|
||||
// TODO(v8:9380) the Contents data structure should be deprecated.
|
||||
THREADED_TEST(SharedArrayBuffer_AllocationInformation) {
|
||||
i::FLAG_harmony_sharedarraybuffer = true;
|
||||
LocalContext env;
|
||||
@ -473,7 +443,7 @@ THREADED_TEST(SharedArrayBuffer_AllocationInformation) {
|
||||
const size_t ab_size = 1024;
|
||||
Local<v8::SharedArrayBuffer> ab =
|
||||
v8::SharedArrayBuffer::New(isolate, ab_size);
|
||||
ScopedSharedArrayBufferContents contents(ab->Externalize());
|
||||
v8::SharedArrayBuffer::Contents contents(ab->GetContents());
|
||||
|
||||
// Array buffers should have normal allocation mode.
|
||||
CHECK_EQ(contents.AllocationMode(),
|
||||
@ -506,7 +476,7 @@ THREADED_TEST(SkipArrayBufferBackingStoreDuringGC) {
|
||||
CcTest::CollectAllGarbage();
|
||||
|
||||
// Should not move the pointer
|
||||
CHECK_EQ(ab->GetContents().Data(), store_ptr);
|
||||
CHECK_EQ(ab->GetBackingStore()->Data(), store_ptr);
|
||||
}
|
||||
|
||||
THREADED_TEST(SkipArrayBufferDuringScavenge) {
|
||||
@ -531,5 +501,5 @@ THREADED_TEST(SkipArrayBufferDuringScavenge) {
|
||||
CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now
|
||||
|
||||
// Use `ab` to silence compiler warning
|
||||
CHECK_EQ(ab->GetContents().Data(), store_ptr);
|
||||
CHECK_EQ(ab->GetBackingStore()->Data(), store_ptr);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ TEST(AllocateNotExternal) {
|
||||
v8::ArrayBuffer::New(env->GetIsolate(), memory, 1024,
|
||||
v8::ArrayBufferCreationMode::kInternalized);
|
||||
CHECK(!buffer->IsExternal());
|
||||
CHECK_EQ(memory, buffer->GetContents().Data());
|
||||
CHECK_EQ(memory, buffer->GetBackingStore()->Data());
|
||||
}
|
||||
|
||||
void TestSpeciesProtector(char* code,
|
||||
|
@ -968,8 +968,8 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
|
||||
data->StoreCurrentStackTrace(description_view);
|
||||
v8::Local<v8::ArrayBuffer> buffer =
|
||||
v8::ArrayBuffer::New(isolate, sizeof(id));
|
||||
*static_cast<v8_inspector::V8StackTraceId*>(buffer->GetContents().Data()) =
|
||||
id;
|
||||
*static_cast<v8_inspector::V8StackTraceId*>(
|
||||
buffer->GetBackingStore()->Data()) = id;
|
||||
args.GetReturnValue().Set(buffer);
|
||||
}
|
||||
|
||||
@ -983,7 +983,7 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
|
||||
IsolateData* data = IsolateData::FromContext(context);
|
||||
v8_inspector::V8StackTraceId* id =
|
||||
static_cast<v8_inspector::V8StackTraceId*>(
|
||||
args[0].As<v8::ArrayBuffer>()->GetContents().Data());
|
||||
args[0].As<v8::ArrayBuffer>()->GetBackingStore()->Data());
|
||||
data->ExternalAsyncTaskStarted(*id);
|
||||
}
|
||||
|
||||
@ -997,7 +997,7 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
|
||||
IsolateData* data = IsolateData::FromContext(context);
|
||||
v8_inspector::V8StackTraceId* id =
|
||||
static_cast<v8_inspector::V8StackTraceId*>(
|
||||
args[0].As<v8::ArrayBuffer>()->GetContents().Data());
|
||||
args[0].As<v8::ArrayBuffer>()->GetBackingStore()->Data());
|
||||
data->ExternalAsyncTaskFinished(*id);
|
||||
}
|
||||
|
||||
|
84
test/mjsunit/wasm/shared-arraybuffer-worker-simple-gc.js
Normal file
84
test/mjsunit/wasm/shared-arraybuffer-worker-simple-gc.js
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2019 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --expose-gc
|
||||
|
||||
const kNumIterations = 10;
|
||||
|
||||
function NewWorker() {
|
||||
let script =
|
||||
`onmessage = (msg) => {
|
||||
if (msg.memory) postMessage("ack");
|
||||
if (msg.quit) postMessage("bye");
|
||||
gc();
|
||||
}`;
|
||||
return new Worker(script, {type: 'string'});
|
||||
}
|
||||
|
||||
function PingWorker(worker, memory) {
|
||||
worker.postMessage({memory: memory});
|
||||
assertEquals("ack", worker.getMessage());
|
||||
worker.postMessage({quit: true});
|
||||
assertEquals("bye", worker.getMessage());
|
||||
}
|
||||
|
||||
function AllocMemory() {
|
||||
return new SharedArrayBuffer(1024);
|
||||
}
|
||||
|
||||
function RunSingleWorkerSingleMemoryTest() {
|
||||
print(arguments.callee.name);
|
||||
let worker = NewWorker();
|
||||
let first = AllocMemory();
|
||||
for (let i = 0; i < kNumIterations; i++) {
|
||||
print(`iteration ${i}`);
|
||||
PingWorker(worker, first);
|
||||
gc();
|
||||
}
|
||||
worker.terminate();
|
||||
}
|
||||
|
||||
function RunSingleWorkerTwoMemoryTest() {
|
||||
print(arguments.callee.name);
|
||||
let worker = NewWorker();
|
||||
let first = AllocMemory(), second = AllocMemory();
|
||||
for (let i = 0; i < kNumIterations; i++) {
|
||||
print(`iteration ${i}`);
|
||||
PingWorker(worker, first);
|
||||
PingWorker(worker, second);
|
||||
gc();
|
||||
}
|
||||
worker.terminate();
|
||||
}
|
||||
|
||||
function RunSingleWorkerMultipleMemoryTest() {
|
||||
print(arguments.callee.name);
|
||||
let worker = NewWorker();
|
||||
let first = AllocMemory();
|
||||
for (let i = 0; i < kNumIterations; i++) {
|
||||
print(`iteration ${i}`);
|
||||
PingWorker(worker, first);
|
||||
PingWorker(worker, AllocMemory());
|
||||
gc();
|
||||
}
|
||||
worker.terminate();
|
||||
}
|
||||
|
||||
function RunMultipleWorkerMultipleMemoryTest() {
|
||||
print(arguments.callee.name);
|
||||
let first = AllocMemory();
|
||||
for (let i = 0; i < kNumIterations; i++) {
|
||||
print(`iteration ${i}`);
|
||||
let worker = NewWorker();
|
||||
PingWorker(worker, first);
|
||||
PingWorker(worker, AllocMemory());
|
||||
worker.terminate();
|
||||
gc();
|
||||
}
|
||||
}
|
||||
|
||||
RunSingleWorkerSingleMemoryTest();
|
||||
RunSingleWorkerTwoMemoryTest();
|
||||
RunSingleWorkerMultipleMemoryTest();
|
||||
RunMultipleWorkerMultipleMemoryTest();
|
85
test/mjsunit/wasm/shared-memory-worker-simple-gc.js
Normal file
85
test/mjsunit/wasm/shared-memory-worker-simple-gc.js
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2019 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --experimental-wasm-threads --expose-gc
|
||||
|
||||
const kNumIterations = 10;
|
||||
|
||||
function NewWorker() {
|
||||
let script =
|
||||
`onmessage = (msg) => {
|
||||
if (msg.memory) postMessage("ack");
|
||||
if (msg.quit) postMessage("bye");
|
||||
gc();
|
||||
}`;
|
||||
return new Worker(script, {type: 'string'});
|
||||
}
|
||||
|
||||
function PingWorker(worker, memory) {
|
||||
worker.postMessage({memory: memory});
|
||||
assertEquals("ack", worker.getMessage());
|
||||
worker.postMessage({quit: true});
|
||||
assertEquals("bye", worker.getMessage());
|
||||
}
|
||||
|
||||
function AllocMemory() {
|
||||
let pages = 1, max = 1;
|
||||
return new WebAssembly.Memory({initial : pages, maximum : max, shared : true});
|
||||
}
|
||||
|
||||
function RunSingleWorkerSingleMemoryTest() {
|
||||
print(arguments.callee.name);
|
||||
let worker = NewWorker();
|
||||
let first = AllocMemory();
|
||||
for (let i = 0; i < kNumIterations; i++) {
|
||||
print(`iteration ${i}`);
|
||||
PingWorker(worker, first);
|
||||
gc();
|
||||
}
|
||||
worker.terminate();
|
||||
}
|
||||
|
||||
function RunSingleWorkerTwoMemoryTest() {
|
||||
print(arguments.callee.name);
|
||||
let worker = NewWorker();
|
||||
let first = AllocMemory(), second = AllocMemory();
|
||||
for (let i = 0; i < kNumIterations; i++) {
|
||||
print(`iteration ${i}`);
|
||||
PingWorker(worker, first);
|
||||
PingWorker(worker, second);
|
||||
gc();
|
||||
}
|
||||
worker.terminate();
|
||||
}
|
||||
|
||||
function RunSingleWorkerMultipleMemoryTest() {
|
||||
print(arguments.callee.name);
|
||||
let worker = NewWorker();
|
||||
let first = AllocMemory();
|
||||
for (let i = 0; i < kNumIterations; i++) {
|
||||
print(`iteration ${i}`);
|
||||
PingWorker(worker, first);
|
||||
PingWorker(worker, AllocMemory());
|
||||
gc();
|
||||
}
|
||||
worker.terminate();
|
||||
}
|
||||
|
||||
function RunMultipleWorkerMultipleMemoryTest() {
|
||||
print(arguments.callee.name);
|
||||
let first = AllocMemory();
|
||||
for (let i = 0; i < kNumIterations; i++) {
|
||||
print(`iteration ${i}`);
|
||||
let worker = NewWorker();
|
||||
PingWorker(worker, first);
|
||||
PingWorker(worker, AllocMemory());
|
||||
worker.terminate();
|
||||
gc();
|
||||
}
|
||||
}
|
||||
|
||||
RunSingleWorkerSingleMemoryTest();
|
||||
RunSingleWorkerTwoMemoryTest();
|
||||
RunSingleWorkerMultipleMemoryTest();
|
||||
RunMultipleWorkerMultipleMemoryTest();
|
@ -1730,7 +1730,7 @@ class ValueSerializerTestWithArrayBufferTransfer : public ValueSerializerTest {
|
||||
Context::Scope scope(deserialization_context());
|
||||
output_buffer_ = ArrayBuffer::New(isolate(), kTestByteLength);
|
||||
const uint8_t data[kTestByteLength] = {0x00, 0x01, 0x80, 0xFF};
|
||||
memcpy(output_buffer_->GetContents().Data(), data, kTestByteLength);
|
||||
memcpy(output_buffer_->GetBackingStore()->Data(), data, kTestByteLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user