From 83bf2975ec7a105c1e08caba21e5c4cccd9beabb Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 8 Mar 2017 21:41:09 +0100 Subject: [PATCH] deps: cherry-pick V8 ValueSerializer changes Refs: https://github.com/nodejs/node/pull/11048 Below is the list of commits: deps: cherry-pick 78c0be52d from V8 upstream Original commit message: ValueSerializer: Promote scheduled exceptions from wasm::ErrorThrower. wasm::ErrorThrower doesn't actually throw exceptions, it just schedules them. As a result, this exception isn't handled properly by code which expects ValueDeserializer to actually throw. For instance, the unit tests use a TryCatch to catch and handle expected exceptions in unit tests. Before this patch, I see local unit test failures because a wasm decode test schedules one, but it isn't caught (and instead causes Context::New to fail at the beginning of the next test). BUG=685713 Review-Url: https://codereview.chromium.org/2659483004 Cr-Commit-Position: refs/heads/master@{#42718} deps: cherry-pick 966355585 from V8 upstream Original commit message: [d8] Use ValueSerializer for postMessage (instead of ad-hoc serializer) Review-Url: https://codereview.chromium.org/2643723010 Cr-Commit-Position: refs/heads/master@{#42749} deps: cherry-pick bf511b426 from V8 upstream Original commit message: ValueSerializer: Support efficiently reading and writing one-byte strings. memcpy is faster than UTF-8 encoding/decoding. This yields 10-20% wins on serializing and deserializing long ASCII strings, according to blink_perf.bindings -- and these are already in a fast path where the entire string is known to be ASCII (but this has to be checked). The win may be larger for strings in Latin-1 but not ASCII (though I suspect this is an uncommon case). A change is also made to make ValueSerializerTest.EncodeTwoByteStringUsesPadding survive wire format version number changes. This is the first of a series of wire format changes from the previous Blink format. The deserializer continues to be able to read the old format, but Chromium M56 will no longer be able to read the messages written by this, in M58. BUG=chromium:686159 Review-Url: https://codereview.chromium.org/2658793004 Cr-Commit-Position: refs/heads/master@{#42753} deps: cherry-pick 6f1639ed1 from V8 upstream Original commit message: ValueSerializer: Distinguish between 'undefined' and an absent property. Dealing with this case requires a wire format change. It is possible that an element can be absent even in an array where the dense format was chosen (because the array initially had no holes), if the elements are modified while they are being serialized. In this case, a new tag for the "hole" is emitted. The logic to treat undefined in dense arrays as an absent property is restricted to versions of the wire format that this tag did not exist. BUG=chromium:686159,chromium:665820 Review-Url: https://codereview.chromium.org/2660093002 Cr-Original-Commit-Position: refs/heads/master@{#42784} Committed: https://chromium.googlesource.com/v8/v8/+/dc85f4c8338c1c824af4f7ee3274dc9f95d14e49 Review-Url: https://codereview.chromium.org/2660093002 Cr-Commit-Position: refs/heads/master@{#42800} deps: cherry-pick c3856de37 from V8 upstream Original commit message: ValueSerializer: Check for zero length before casting to FixedDoubleArray. Even though the elements kind is FAST_DOUBLE_ELEMENTS, if length is zero the isolate's empty_fixed_array is used. It's illegal to cast this to FixedDoubleArray, so we avoid the cast. BUG=chromium:686479 Review-Url: https://codereview.chromium.org/2665313003 Cr-Commit-Position: refs/heads/master@{#42867} deps: cherry-pick 591cc0b4c from V8 upstream Original commit message: ValueSerializer: Share string encoding code with String and RegExp objects. This avoids the need to pull in the UTF-8 encoding code from the public API, and allows it to take advantage of any supported way that i::String can be encoded (one- or two-byte). Backward compatibility is maintained, but this is the behavior beginning with this version. BUG=chromium:686159 Review-Url: https://codereview.chromium.org/2665653004 Cr-Commit-Position: refs/heads/master@{#42872} deps: cherry-pick 79837f5f6 from V8 upstream Original commit message: Improve ValueSerializer perf regression after 96635558 BUG=687196 R=jbroman@chromium.org Review-Url: https://codereview.chromium.org/2674613002 Cr-Commit-Position: refs/heads/master@{#42938} deps: cherry-pick 8990399dc from V8 upstream Original commit message: ValueDeserializer: Only allow valid keys when deserializing object properties. The serializer won't ever write a more complex object. Not validating this allows other things to be used as keys, and converted to string when the property set actually occurs. It turns out this gives an opportunity to trigger OOM by giving an object a key which is a very large sparse array (whose string representation is very large). This case is now rejected by the deserializer. BUG=chromium:686511 Review-Url: https://codereview.chromium.org/2697023002 Cr-Commit-Position: refs/heads/master@{#43249} deps: cherry-pick 68960eeb7 from V8 upstream Original commit message: ValueDeserializer: Make sure that an exception is the legacy path. The entry points to the deserializer are responsible for ensuring that an exception is pending by the time they return. Some failures throw exceptions themselves, while others (like errors in the format) are exceptions caused by the deserializer, not coming from the runtime. Like the non-legacy path, a default deserialization exception should be thrown in such cases. BUG=chromium:693411 Review-Url: https://codereview.chromium.org/2712713002 Cr-Commit-Position: refs/heads/master@{#43390} deps: cherry-pick 3b15d950e from V8 upstream Original commit message: ValueSerializer: Add SetTreatArrayBufferViewsAsHostObjects() flag Add `ValueSerializer::SetTreatArrayBufferViewsAsHostObjects()` which instructs the `ValueSerializer` to treat ArrayBufferView objects as host objects. BUG=v8:5926 Review-Url: https://codereview.chromium.org/2696133007 Cr-Commit-Position: refs/heads/master@{#43281} deps: cherry-pick 654351997 from V8 upstream Original commit message: ValueSerializer: Add an explicit tag for host objects. This makes it no longer necessary to ensure that V8 and Blink have non-colliding tags, which makes it easier for them to evolve independently, and also makes the wire format more suitable for other V8 embedders, who would not necessarily be surveyed before V8 introduced a new tag that might collide with theirs. BUG=chromium:686159 Review-Url: https://codereview.chromium.org/2709023003 Cr-Commit-Position: refs/heads/master@{#43466} PR-URL: https://github.com/nodejs/node/pull/11752 Reviewed-By: Ben Noordhuis Reviewed-By: Franziska Hinkelmann --- deps/v8/include/v8.h | 13 + deps/v8/src/api.cc | 4 + deps/v8/src/d8.cc | 580 +++++++----------- deps/v8/src/d8.h | 109 ++-- deps/v8/src/messages.h | 1 + deps/v8/src/value-serializer.cc | 253 +++++--- deps/v8/src/value-serializer.h | 23 +- .../mjsunit/d8-worker-sharedarraybuffer.js | 25 +- deps/v8/test/mjsunit/d8-worker.js | 16 +- deps/v8/test/mjsunit/harmony/futex.js | 8 +- .../mjsunit/regress/regress-crbug-514081.js | 9 +- .../unittests/value-serializer-unittest.cc | 148 ++++- 12 files changed, 663 insertions(+), 526 deletions(-) diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index b513c7f1f3741b..f00ecc7465d84e 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -1726,6 +1726,10 @@ class V8_EXPORT ValueSerializer { * Allocates memory for the buffer of at least the size provided. The actual * size (which may be greater or equal) is written to |actual_size|. If no * buffer has been allocated yet, nullptr will be provided. + * + * If the memory cannot be allocated, nullptr should be returned. + * |actual_size| will be ignored. It is assumed that |old_buffer| is still + * valid in this case and has not been modified. */ virtual void* ReallocateBufferMemory(void* old_buffer, size_t size, size_t* actual_size); @@ -1781,6 +1785,15 @@ class V8_EXPORT ValueSerializer { uint32_t transfer_id, Local shared_array_buffer)); + /* + * Indicate whether to treat ArrayBufferView objects as host objects, + * i.e. pass them to Delegate::WriteHostObject. This should not be + * called when no Delegate was passed. + * + * The default is not to treat ArrayBufferViews as host objects. + */ + void SetTreatArrayBufferViewsAsHostObjects(bool mode); + /* * Write raw data in various common formats to the buffer. * Note that integer types are written in base-128 varint format, not with a diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 04ba55c1ddecd7..19d693c95e15ec 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -3130,6 +3130,10 @@ ValueSerializer::~ValueSerializer() { delete private_; } void ValueSerializer::WriteHeader() { private_->serializer.WriteHeader(); } +void ValueSerializer::SetTreatArrayBufferViewsAsHostObjects(bool mode) { + private_->serializer.SetTreatArrayBufferViewsAsHostObjects(mode); +} + Maybe ValueSerializer::WriteValue(Local context, Local value) { PREPARE_FOR_EXECUTION_PRIMITIVE(context, ValueSerializer, WriteValue, bool); diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc index a34bafcc652943..368b33ecc78b46 100644 --- a/deps/v8/src/d8.cc +++ b/deps/v8/src/d8.cc @@ -31,6 +31,7 @@ #include "src/base/sys-info.h" #include "src/basic-block-profiler.h" #include "src/interpreter/interpreter.h" +#include "src/list-inl.h" #include "src/msan.h" #include "src/objects-inl.h" #include "src/snapshot/natives.h" @@ -224,16 +225,6 @@ static Local Throw(Isolate* isolate, const char* message) { } -bool FindInObjectList(Local object, const Shell::ObjectList& list) { - for (int i = 0; i < list.length(); ++i) { - if (list[i]->StrictEquals(object)) { - return true; - } - } - return false; -} - - Worker* GetWorkerFromInternalField(Isolate* isolate, Local object) { if (object->InternalFieldCount() != 1) { Throw(isolate, "this is not a Worker"); @@ -416,7 +407,10 @@ Global Shell::stringify_function_; base::LazyMutex Shell::workers_mutex_; bool Shell::allow_new_workers_ = true; i::List Shell::workers_; -i::List Shell::externalized_shared_contents_; +std::unordered_set + Shell::externalized_shared_contents_; Global Shell::evaluation_context_; ArrayBuffer::Allocator* Shell::array_buffer_allocator; @@ -1179,7 +1173,6 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo& args) { void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); HandleScope handle_scope(isolate); - Local context = isolate->GetCurrentContext(); if (args.Length() < 1) { Throw(isolate, "Invalid argument"); @@ -1192,36 +1185,12 @@ void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo& args) { } Local message = args[0]; - ObjectList to_transfer; - if (args.Length() >= 2) { - if (!args[1]->IsArray()) { - Throw(isolate, "Transfer list must be an Array"); - return; - } - - Local transfer = Local::Cast(args[1]); - uint32_t length = transfer->Length(); - for (uint32_t i = 0; i < length; ++i) { - Local element; - if (transfer->Get(context, i).ToLocal(&element)) { - if (!element->IsArrayBuffer() && !element->IsSharedArrayBuffer()) { - Throw(isolate, - "Transfer array elements must be an ArrayBuffer or " - "SharedArrayBuffer."); - break; - } - - to_transfer.Add(Local::Cast(element)); - } - } - } - - ObjectList seen_objects; - SerializationData* data = new SerializationData; - if (SerializeValue(isolate, message, to_transfer, &seen_objects, data)) { - worker->PostMessage(data); - } else { - delete data; + Local transfer = + args.Length() >= 2 ? args[1] : Local::Cast(Undefined(isolate)); + std::unique_ptr data = + Shell::SerializeValue(isolate, message, transfer); + if (data) { + worker->PostMessage(std::move(data)); } } @@ -1234,14 +1203,12 @@ void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo& args) { return; } - SerializationData* data = worker->GetMessage(); + std::unique_ptr data = worker->GetMessage(); if (data) { - int offset = 0; - Local data_value; - if (Shell::DeserializeValue(isolate, *data, &offset).ToLocal(&data_value)) { - args.GetReturnValue().Set(data_value); + Local value; + if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) { + args.GetReturnValue().Set(value); } - delete data; } } @@ -2181,14 +2148,12 @@ void SourceGroup::JoinThread() { thread_->Join(); } - SerializationData::~SerializationData() { // Any ArrayBuffer::Contents are owned by this SerializationData object if - // ownership hasn't been transferred out via ReadArrayBufferContents. + // ownership hasn't been transferred out. // SharedArrayBuffer::Contents may be used by multiple threads, so must be // cleaned up by the main thread in Shell::CleanupWorkers(). - for (int i = 0; i < array_buffer_contents_.length(); ++i) { - ArrayBuffer::Contents& contents = array_buffer_contents_[i]; + for (const auto& contents : array_buffer_contents_) { if (contents.Data()) { Shell::array_buffer_allocator->Free(contents.Data(), contents.ByteLength()); @@ -2196,96 +2161,35 @@ SerializationData::~SerializationData() { } } - -void SerializationData::WriteTag(SerializationTag tag) { data_.Add(tag); } - - -void SerializationData::WriteMemory(const void* p, int length) { - if (length > 0) { - i::Vector block = data_.AddBlock(0, length); - memcpy(&block[0], p, length); - } -} - - -void SerializationData::WriteArrayBufferContents( - const ArrayBuffer::Contents& contents) { - array_buffer_contents_.Add(contents); - WriteTag(kSerializationTagTransferredArrayBuffer); - int index = array_buffer_contents_.length() - 1; - Write(index); -} - - -void SerializationData::WriteSharedArrayBufferContents( - const SharedArrayBuffer::Contents& contents) { - shared_array_buffer_contents_.Add(contents); - WriteTag(kSerializationTagTransferredSharedArrayBuffer); - int index = shared_array_buffer_contents_.length() - 1; - Write(index); -} - - -SerializationTag SerializationData::ReadTag(int* offset) const { - return static_cast(Read(offset)); -} - - -void SerializationData::ReadMemory(void* p, int length, int* offset) const { - if (length > 0) { - memcpy(p, &data_[*offset], length); - (*offset) += length; - } -} - - -void SerializationData::ReadArrayBufferContents(ArrayBuffer::Contents* contents, - int* offset) const { - int index = Read(offset); - DCHECK(index < array_buffer_contents_.length()); - *contents = array_buffer_contents_[index]; - // Ownership of this ArrayBuffer::Contents is passed to the caller. Neuter - // our copy so it won't be double-free'd when this SerializationData is - // destroyed. - array_buffer_contents_[index] = ArrayBuffer::Contents(); -} - - -void SerializationData::ReadSharedArrayBufferContents( - SharedArrayBuffer::Contents* contents, int* offset) const { - int index = Read(offset); - DCHECK(index < shared_array_buffer_contents_.length()); - *contents = shared_array_buffer_contents_[index]; +void SerializationData::ClearTransferredArrayBuffers() { + array_buffer_contents_.clear(); } - -void SerializationDataQueue::Enqueue(SerializationData* data) { +void SerializationDataQueue::Enqueue(std::unique_ptr data) { base::LockGuard lock_guard(&mutex_); - data_.Add(data); + data_.push_back(std::move(data)); } - -bool SerializationDataQueue::Dequeue(SerializationData** data) { +bool SerializationDataQueue::Dequeue( + std::unique_ptr* out_data) { + out_data->reset(); base::LockGuard lock_guard(&mutex_); - *data = NULL; - if (data_.is_empty()) return false; - *data = data_.Remove(0); + if (data_.empty()) return false; + *out_data = std::move(data_[0]); + data_.erase(data_.begin()); return true; } bool SerializationDataQueue::IsEmpty() { base::LockGuard lock_guard(&mutex_); - return data_.is_empty(); + return data_.empty(); } void SerializationDataQueue::Clear() { base::LockGuard lock_guard(&mutex_); - for (int i = 0; i < data_.length(); ++i) { - delete data_[i]; - } - data_.Clear(); + data_.clear(); } @@ -2314,22 +2218,20 @@ void Worker::StartExecuteInThread(const char* script) { thread_->Start(); } - -void Worker::PostMessage(SerializationData* data) { - in_queue_.Enqueue(data); +void Worker::PostMessage(std::unique_ptr data) { + in_queue_.Enqueue(std::move(data)); in_semaphore_.Signal(); } - -SerializationData* Worker::GetMessage() { - SerializationData* data = NULL; - while (!out_queue_.Dequeue(&data)) { +std::unique_ptr Worker::GetMessage() { + std::unique_ptr result; + while (!out_queue_.Dequeue(&result)) { // If the worker is no longer running, and there are no messages in the // queue, don't expect any more messages from it. if (!base::NoBarrier_Load(&running_)) break; out_semaphore_.Wait(); } - return data; + return result; } @@ -2393,19 +2295,21 @@ void Worker::ExecuteInThread() { // Now wait for messages while (true) { in_semaphore_.Wait(); - SerializationData* data; + std::unique_ptr data; if (!in_queue_.Dequeue(&data)) continue; - if (data == NULL) { + if (!data) { break; } - int offset = 0; - Local data_value; - if (Shell::DeserializeValue(isolate, *data, &offset) - .ToLocal(&data_value)) { - Local argv[] = {data_value}; + v8::TryCatch try_catch(isolate); + Local value; + if (Shell::DeserializeValue(isolate, std::move(data)) + .ToLocal(&value)) { + Local argv[] = {value}; (void)onmessage_fun->Call(context, global, 1, argv); } - delete data; + if (try_catch.HasCaught()) { + Shell::ReportException(isolate, &try_catch); + } } } } @@ -2432,21 +2336,15 @@ void Worker::PostMessageOut(const v8::FunctionCallbackInfo& args) { } Local message = args[0]; - - // TODO(binji): Allow transferring from worker to main thread? - Shell::ObjectList to_transfer; - - Shell::ObjectList seen_objects; - SerializationData* data = new SerializationData; - if (Shell::SerializeValue(isolate, message, to_transfer, &seen_objects, - data)) { + Local transfer = Undefined(isolate); + std::unique_ptr data = + Shell::SerializeValue(isolate, message, transfer); + if (data) { DCHECK(args.Data()->IsExternal()); Local this_value = Local::Cast(args.Data()); Worker* worker = static_cast(this_value->Value()); - worker->out_queue_.Enqueue(data); + worker->out_queue_.Enqueue(std::move(data)); worker->out_semaphore_.Signal(); - } else { - delete data; } } @@ -2637,234 +2535,202 @@ void Shell::EmptyMessageQueues(Isolate* isolate) { } } +class Serializer : public ValueSerializer::Delegate { + public: + explicit Serializer(Isolate* isolate) + : isolate_(isolate), serializer_(isolate, this) {} -bool Shell::SerializeValue(Isolate* isolate, Local value, - const ObjectList& to_transfer, - ObjectList* seen_objects, - SerializationData* out_data) { - DCHECK(out_data); - Local context = isolate->GetCurrentContext(); - - if (value->IsUndefined()) { - out_data->WriteTag(kSerializationTagUndefined); - } else if (value->IsNull()) { - out_data->WriteTag(kSerializationTagNull); - } else if (value->IsTrue()) { - out_data->WriteTag(kSerializationTagTrue); - } else if (value->IsFalse()) { - out_data->WriteTag(kSerializationTagFalse); - } else if (value->IsNumber()) { - Local num = Local::Cast(value); - double value = num->Value(); - out_data->WriteTag(kSerializationTagNumber); - out_data->Write(value); - } else if (value->IsString()) { - v8::String::Utf8Value str(value); - out_data->WriteTag(kSerializationTagString); - out_data->Write(str.length()); - out_data->WriteMemory(*str, str.length()); - } else if (value->IsArray()) { - Local array = Local::Cast(value); - if (FindInObjectList(array, *seen_objects)) { - Throw(isolate, "Duplicated arrays not supported"); - return false; + Maybe WriteValue(Local context, Local value, + Local transfer) { + bool ok; + DCHECK(!data_); + data_.reset(new SerializationData); + if (!PrepareTransfer(context, transfer).To(&ok)) { + return Nothing(); } - seen_objects->Add(array); - out_data->WriteTag(kSerializationTagArray); - uint32_t length = array->Length(); - out_data->Write(length); - for (uint32_t i = 0; i < length; ++i) { - Local element_value; - if (array->Get(context, i).ToLocal(&element_value)) { - if (!SerializeValue(isolate, element_value, to_transfer, seen_objects, - out_data)) - return false; - } else { - Throw(isolate, "Failed to serialize array element."); - return false; - } + serializer_.WriteHeader(); + + if (!serializer_.WriteValue(context, value).To(&ok)) { + data_.reset(); + return Nothing(); } - } else if (value->IsArrayBuffer()) { - Local array_buffer = Local::Cast(value); - if (FindInObjectList(array_buffer, *seen_objects)) { - Throw(isolate, "Duplicated array buffers not supported"); - return false; + + if (!FinalizeTransfer().To(&ok)) { + return Nothing(); } - seen_objects->Add(array_buffer); - if (FindInObjectList(array_buffer, to_transfer)) { - // Transfer ArrayBuffer - if (!array_buffer->IsNeuterable()) { - Throw(isolate, "Attempting to transfer an un-neuterable ArrayBuffer"); - return false; - } - ArrayBuffer::Contents contents = array_buffer->IsExternal() - ? array_buffer->GetContents() - : array_buffer->Externalize(); - array_buffer->Neuter(); - out_data->WriteArrayBufferContents(contents); - } else { - ArrayBuffer::Contents contents = array_buffer->GetContents(); - // Clone ArrayBuffer - if (contents.ByteLength() > i::kMaxInt) { - Throw(isolate, "ArrayBuffer is too big to clone"); - return false; - } + std::pair pair = serializer_.Release(); + data_->data_.reset(pair.first); + data_->size_ = pair.second; + return Just(true); + } - int32_t byte_length = static_cast(contents.ByteLength()); - out_data->WriteTag(kSerializationTagArrayBuffer); - out_data->Write(byte_length); - out_data->WriteMemory(contents.Data(), byte_length); - } - } else if (value->IsSharedArrayBuffer()) { - Local sab = Local::Cast(value); - if (FindInObjectList(sab, *seen_objects)) { - Throw(isolate, "Duplicated shared array buffers not supported"); - return false; - } - seen_objects->Add(sab); - if (!FindInObjectList(sab, to_transfer)) { - Throw(isolate, "SharedArrayBuffer must be transferred"); - return false; + std::unique_ptr Release() { return std::move(data_); } + + protected: + // Implements ValueSerializer::Delegate. + void ThrowDataCloneError(Local message) override { + isolate_->ThrowException(Exception::Error(message)); + } + + Maybe GetSharedArrayBufferId( + Isolate* isolate, Local shared_array_buffer) override { + DCHECK(data_ != nullptr); + for (size_t index = 0; index < shared_array_buffers_.size(); ++index) { + if (shared_array_buffers_[index] == shared_array_buffer) { + return Just(static_cast(index)); + } } - SharedArrayBuffer::Contents contents; - if (sab->IsExternal()) { - contents = sab->GetContents(); + size_t index = shared_array_buffers_.size(); + shared_array_buffers_.emplace_back(isolate_, shared_array_buffer); + return Just(static_cast(index)); + } + + void* ReallocateBufferMemory(void* old_buffer, size_t size, + size_t* actual_size) override { + void* result = realloc(old_buffer, size); + *actual_size = result ? size : 0; + return result; + } + + void FreeBufferMemory(void* buffer) override { free(buffer); } + + private: + Maybe PrepareTransfer(Local context, Local transfer) { + if (transfer->IsArray()) { + Local transfer_array = Local::Cast(transfer); + uint32_t length = transfer_array->Length(); + for (uint32_t i = 0; i < length; ++i) { + Local element; + if (transfer_array->Get(context, i).ToLocal(&element)) { + if (!element->IsArrayBuffer()) { + Throw(isolate_, "Transfer array elements must be an ArrayBuffer"); + break; + } + + Local array_buffer = Local::Cast(element); + serializer_.TransferArrayBuffer( + static_cast(array_buffers_.size()), array_buffer); + array_buffers_.emplace_back(isolate_, array_buffer); + } else { + return Nothing(); + } + } + return Just(true); + } else if (transfer->IsUndefined()) { + return Just(true); } else { - contents = sab->Externalize(); - base::LockGuard lock_guard(workers_mutex_.Pointer()); - externalized_shared_contents_.Add(contents); - } - out_data->WriteSharedArrayBufferContents(contents); - } else if (value->IsObject()) { - Local object = Local::Cast(value); - if (FindInObjectList(object, *seen_objects)) { - Throw(isolate, "Duplicated objects not supported"); - return false; + Throw(isolate_, "Transfer list must be an Array or undefined"); + return Nothing(); } - seen_objects->Add(object); - Local property_names; - if (!object->GetOwnPropertyNames(context).ToLocal(&property_names)) { - Throw(isolate, "Unable to get property names"); - return false; + } + + Maybe FinalizeTransfer() { + for (const auto& global_array_buffer : array_buffers_) { + Local array_buffer = + Local::New(isolate_, global_array_buffer); + if (!array_buffer->IsNeuterable()) { + Throw(isolate_, "ArrayBuffer could not be transferred"); + return Nothing(); + } + + if (!array_buffer->IsExternal()) { + array_buffer->Externalize(); + } + ArrayBuffer::Contents contents = array_buffer->GetContents(); + array_buffer->Neuter(); + data_->array_buffer_contents_.push_back(contents); } - uint32_t length = property_names->Length(); - out_data->WriteTag(kSerializationTagObject); - out_data->Write(length); - for (uint32_t i = 0; i < length; ++i) { - Local name; - Local property_value; - if (property_names->Get(context, i).ToLocal(&name) && - object->Get(context, name).ToLocal(&property_value)) { - if (!SerializeValue(isolate, name, to_transfer, seen_objects, out_data)) - return false; - if (!SerializeValue(isolate, property_value, to_transfer, seen_objects, - out_data)) - return false; - } else { - Throw(isolate, "Failed to serialize property."); - return false; + for (const auto& global_shared_array_buffer : shared_array_buffers_) { + Local shared_array_buffer = + Local::New(isolate_, global_shared_array_buffer); + if (!shared_array_buffer->IsExternal()) { + shared_array_buffer->Externalize(); } + data_->shared_array_buffer_contents_.push_back( + shared_array_buffer->GetContents()); } - } else { - Throw(isolate, "Don't know how to serialize object"); - return false; + + return Just(true); } - return true; -} + Isolate* isolate_; + ValueSerializer serializer_; + std::unique_ptr data_; + std::vector> array_buffers_; + std::vector> shared_array_buffers_; + DISALLOW_COPY_AND_ASSIGN(Serializer); +}; -MaybeLocal Shell::DeserializeValue(Isolate* isolate, - const SerializationData& data, - int* offset) { - DCHECK(offset); - EscapableHandleScope scope(isolate); - Local result; - SerializationTag tag = data.ReadTag(offset); +class Deserializer : public ValueDeserializer::Delegate { + public: + Deserializer(Isolate* isolate, std::unique_ptr data) + : isolate_(isolate), + deserializer_(isolate, data->data(), data->size(), this), + data_(std::move(data)) { + deserializer_.SetSupportsLegacyWireFormat(true); + } - switch (tag) { - case kSerializationTagUndefined: - result = Undefined(isolate); - break; - case kSerializationTagNull: - result = Null(isolate); - break; - case kSerializationTagTrue: - result = True(isolate); - break; - case kSerializationTagFalse: - result = False(isolate); - break; - case kSerializationTagNumber: - result = Number::New(isolate, data.Read(offset)); - break; - case kSerializationTagString: { - int length = data.Read(offset); - CHECK(length >= 0); - std::vector buffer(length + 1); // + 1 so it is never empty. - data.ReadMemory(&buffer[0], length, offset); - MaybeLocal str = - String::NewFromUtf8(isolate, &buffer[0], NewStringType::kNormal, - length).ToLocalChecked(); - if (!str.IsEmpty()) result = str.ToLocalChecked(); - break; + MaybeLocal ReadValue(Local context) { + bool read_header; + if (!deserializer_.ReadHeader(context).To(&read_header)) { + return MaybeLocal(); } - case kSerializationTagArray: { - uint32_t length = data.Read(offset); - Local array = Array::New(isolate, length); - for (uint32_t i = 0; i < length; ++i) { - Local element_value; - CHECK(DeserializeValue(isolate, data, offset).ToLocal(&element_value)); - array->Set(isolate->GetCurrentContext(), i, element_value).FromJust(); - } - result = array; - break; - } - case kSerializationTagObject: { - int length = data.Read(offset); - Local object = Object::New(isolate); - for (int i = 0; i < length; ++i) { - Local property_name; - CHECK(DeserializeValue(isolate, data, offset).ToLocal(&property_name)); - Local property_value; - CHECK(DeserializeValue(isolate, data, offset).ToLocal(&property_value)); - object->Set(isolate->GetCurrentContext(), property_name, property_value) - .FromJust(); - } - result = object; - break; + + uint32_t index = 0; + for (const auto& contents : data_->array_buffer_contents()) { + Local array_buffer = + ArrayBuffer::New(isolate_, contents.Data(), contents.ByteLength()); + deserializer_.TransferArrayBuffer(index++, array_buffer); } - case kSerializationTagArrayBuffer: { - int32_t byte_length = data.Read(offset); - Local array_buffer = ArrayBuffer::New(isolate, byte_length); - ArrayBuffer::Contents contents = array_buffer->GetContents(); - DCHECK(static_cast(byte_length) == contents.ByteLength()); - data.ReadMemory(contents.Data(), byte_length, offset); - result = array_buffer; - break; + + index = 0; + for (const auto& contents : data_->shared_array_buffer_contents()) { + Local shared_array_buffer = SharedArrayBuffer::New( + isolate_, contents.Data(), contents.ByteLength()); + deserializer_.TransferSharedArrayBuffer(index++, shared_array_buffer); } - case kSerializationTagTransferredArrayBuffer: { - ArrayBuffer::Contents contents; - data.ReadArrayBufferContents(&contents, offset); - result = ArrayBuffer::New(isolate, contents.Data(), contents.ByteLength(), - ArrayBufferCreationMode::kInternalized); - break; + + MaybeLocal result = deserializer_.ReadValue(context); + if (!result.IsEmpty()) { + data_->ClearTransferredArrayBuffers(); } - case kSerializationTagTransferredSharedArrayBuffer: { - SharedArrayBuffer::Contents contents; - data.ReadSharedArrayBufferContents(&contents, offset); - result = SharedArrayBuffer::New(isolate, contents.Data(), - contents.ByteLength()); - break; + return result; + } + + private: + Isolate* isolate_; + ValueDeserializer deserializer_; + std::unique_ptr data_; + + DISALLOW_COPY_AND_ASSIGN(Deserializer); +}; + +std::unique_ptr Shell::SerializeValue( + Isolate* isolate, Local value, Local transfer) { + bool ok; + Local context = isolate->GetCurrentContext(); + Serializer serializer(isolate); + if (serializer.WriteValue(context, value, transfer).To(&ok)) { + std::unique_ptr data = serializer.Release(); + base::LockGuard lock_guard(workers_mutex_.Pointer()); + for (const auto& contents : data->shared_array_buffer_contents()) { + externalized_shared_contents_.insert(contents); } - default: - UNREACHABLE(); + return data; } + return nullptr; +} - return scope.Escape(result); +MaybeLocal Shell::DeserializeValue( + Isolate* isolate, std::unique_ptr data) { + Local value; + Local context = isolate->GetCurrentContext(); + Deserializer deserializer(isolate, std::move(data)); + return deserializer.ReadValue(context); } @@ -2890,12 +2756,10 @@ void Shell::CleanupWorkers() { base::LockGuard lock_guard(workers_mutex_.Pointer()); allow_new_workers_ = true; - for (int i = 0; i < externalized_shared_contents_.length(); ++i) { - const SharedArrayBuffer::Contents& contents = - externalized_shared_contents_[i]; + for (const auto& contents : externalized_shared_contents_) { Shell::array_buffer_allocator->Free(contents.Data(), contents.ByteLength()); } - externalized_shared_contents_.Clear(); + externalized_shared_contents_.clear(); } diff --git a/deps/v8/src/d8.h b/deps/v8/src/d8.h index c3729f92ba121e..558b8bb58d7684 100644 --- a/deps/v8/src/d8.h +++ b/deps/v8/src/d8.h @@ -5,9 +5,13 @@ #ifndef V8_D8_H_ #define V8_D8_H_ +#include #include +#include +#include #include "src/allocation.h" +#include "src/base/functional.h" #include "src/base/hashmap.h" #include "src/base/platform/time.h" #include "src/list.h" @@ -143,68 +147,51 @@ class SourceGroup { int end_offset_; }; -enum SerializationTag { - kSerializationTagUndefined, - kSerializationTagNull, - kSerializationTagTrue, - kSerializationTagFalse, - kSerializationTagNumber, - kSerializationTagString, - kSerializationTagArray, - kSerializationTagObject, - kSerializationTagArrayBuffer, - kSerializationTagTransferredArrayBuffer, - kSerializationTagTransferredSharedArrayBuffer, -}; - class SerializationData { public: - SerializationData() {} + SerializationData() : data_(nullptr), size_(0) {} ~SerializationData(); - void WriteTag(SerializationTag tag); - void WriteMemory(const void* p, int length); - void WriteArrayBufferContents(const ArrayBuffer::Contents& contents); - void WriteSharedArrayBufferContents( - const SharedArrayBuffer::Contents& contents); - - template - void Write(const T& data) { - WriteMemory(&data, sizeof(data)); + uint8_t* data() { return data_.get(); } + size_t size() { return size_; } + const std::vector& array_buffer_contents() { + return array_buffer_contents_; } - - SerializationTag ReadTag(int* offset) const; - void ReadMemory(void* p, int length, int* offset) const; - void ReadArrayBufferContents(ArrayBuffer::Contents* contents, - int* offset) const; - void ReadSharedArrayBufferContents(SharedArrayBuffer::Contents* contents, - int* offset) const; - - template - T Read(int* offset) const { - T value; - ReadMemory(&value, sizeof(value), offset); - return value; + const std::vector& + shared_array_buffer_contents() { + return shared_array_buffer_contents_; } + void ClearTransferredArrayBuffers(); + + private: + struct DataDeleter { + void operator()(uint8_t* p) const { free(p); } + }; + + std::unique_ptr data_; + size_t size_; + std::vector array_buffer_contents_; + std::vector shared_array_buffer_contents_; + private: - i::List data_; - i::List array_buffer_contents_; - i::List shared_array_buffer_contents_; + friend class Serializer; + + DISALLOW_COPY_AND_ASSIGN(SerializationData); }; class SerializationDataQueue { public: - void Enqueue(SerializationData* data); - bool Dequeue(SerializationData** data); + void Enqueue(std::unique_ptr data); + bool Dequeue(std::unique_ptr* data); bool IsEmpty(); void Clear(); private: base::Mutex mutex_; - i::List data_; + std::vector> data_; }; @@ -219,13 +206,13 @@ class Worker { // Post a message to the worker's incoming message queue. The worker will // take ownership of the SerializationData. // This function should only be called by the thread that created the Worker. - void PostMessage(SerializationData* data); + void PostMessage(std::unique_ptr data); // Synchronously retrieve messages from the worker's outgoing message queue. // If there is no message in the queue, block until a message is available. // If there are no messages in the queue and the worker is no longer running, // return nullptr. // This function should only be called by the thread that created the Worker. - SerializationData* GetMessage(); + std::unique_ptr GetMessage(); // Terminate the worker's event loop. Messages from the worker that have been // queued can still be read via GetMessage(). // This function can be called by any thread. @@ -335,16 +322,10 @@ class Shell : public i::AllStatic { static void CollectGarbage(Isolate* isolate); static void EmptyMessageQueues(Isolate* isolate); - // TODO(binji): stupid implementation for now. Is there an easy way to hash an - // object for use in base::HashMap? By pointer? - typedef i::List> ObjectList; - static bool SerializeValue(Isolate* isolate, Local value, - const ObjectList& to_transfer, - ObjectList* seen_objects, - SerializationData* out_data); - static MaybeLocal DeserializeValue(Isolate* isolate, - const SerializationData& data, - int* offset); + static std::unique_ptr SerializeValue( + Isolate* isolate, Local value, Local transfer); + static MaybeLocal DeserializeValue( + Isolate* isolate, std::unique_ptr data); static void CleanupWorkers(); static int* LookupCounter(const char* name); static void* CreateHistogram(const char* name, @@ -444,10 +425,26 @@ class Shell : public i::AllStatic { static base::LazyMutex context_mutex_; static const base::TimeTicks kInitialTicks; + struct SharedArrayBufferContentsHash { + size_t operator()(const v8::SharedArrayBuffer::Contents& contents) const { + return base::hash_combine(contents.Data(), contents.ByteLength()); + } + }; + + struct SharedArrayBufferContentsIsEqual { + bool operator()(const SharedArrayBuffer::Contents& a, + const SharedArrayBuffer::Contents& b) const { + return a.Data() == b.Data() && a.ByteLength() == b.ByteLength(); + } + }; + static base::LazyMutex workers_mutex_; static bool allow_new_workers_; static i::List workers_; - static i::List externalized_shared_contents_; + static std::unordered_set + externalized_shared_contents_; static void WriteIgnitionDispatchCountersFile(v8::Isolate* isolate); static Counter* GetCounter(const char* name, bool is_histogram); diff --git a/deps/v8/src/messages.h b/deps/v8/src/messages.h index 745bc2a46954e1..dc8c9d05df7213 100644 --- a/deps/v8/src/messages.h +++ b/deps/v8/src/messages.h @@ -675,6 +675,7 @@ class ErrorUtils : public AllStatic { T(AsmJsInstantiated, "Instantiated asm.js: %") \ /* DataCloneError messages */ \ T(DataCloneError, "% could not be cloned.") \ + T(DataCloneErrorOutOfMemory, "Data cannot be cloned, out of memory.") \ T(DataCloneErrorNeuteredArrayBuffer, \ "An ArrayBuffer is neutered and could not be cloned.") \ T(DataCloneErrorSharedArrayBufferTransferred, \ diff --git a/deps/v8/src/value-serializer.cc b/deps/v8/src/value-serializer.cc index f19197af75a886..585ef0f59cc442 100644 --- a/deps/v8/src/value-serializer.cc +++ b/deps/v8/src/value-serializer.cc @@ -23,7 +23,14 @@ namespace v8 { namespace internal { -static const uint32_t kLatestVersion = 9; +// Version 9: (imported from Blink) +// Version 10: one-byte (Latin-1) strings +// Version 11: properly separate undefined from the hole in arrays +// Version 12: regexp and string objects share normal string encoding +// Version 13: host objects have an explicit tag (rather than handling all +// unknown tags) +static const uint32_t kLatestVersion = 13; + static const int kPretenureThreshold = 100 * KB; template @@ -46,6 +53,7 @@ enum class SerializationTag : uint8_t { // refTableSize:uint32_t (previously used for sanity checks; safe to ignore) kVerifyObjectCount = '?', // Oddballs (no data). + kTheHole = '-', kUndefined = '_', kNull = '0', kTrue = 'T', @@ -61,6 +69,7 @@ enum class SerializationTag : uint8_t { kDouble = 'N', // byteLength:uint32_t, then raw data kUtf8String = 'S', + kOneByteString = '"', kTwoByteString = 'c', // Reference to a serialized object. objectID:uint32_t kObjectReference = '^', @@ -117,6 +126,9 @@ enum class SerializationTag : uint8_t { // wasmWireByteLength:uint32_t, then raw data // compiledDataLength:uint32_t, then raw data kWasmModule = 'W', + // The delegate is responsible for processing all following data. + // This "escapes" to whatever wire format the delegate chooses. + kHostObject = '\\', }; namespace { @@ -163,6 +175,10 @@ void ValueSerializer::WriteHeader() { WriteVarint(kLatestVersion); } +void ValueSerializer::SetTreatArrayBufferViewsAsHostObjects(bool mode) { + treat_array_buffer_views_as_host_objects_ = mode; +} + void ValueSerializer::WriteTag(SerializationTag tag) { uint8_t raw_tag = static_cast(tag); WriteRawBytes(&raw_tag, sizeof(raw_tag)); @@ -217,18 +233,26 @@ void ValueSerializer::WriteTwoByteString(Vector chars) { } void ValueSerializer::WriteRawBytes(const void* source, size_t length) { - memcpy(ReserveRawBytes(length), source, length); + uint8_t* dest; + if (ReserveRawBytes(length).To(&dest)) { + memcpy(dest, source, length); + } } -uint8_t* ValueSerializer::ReserveRawBytes(size_t bytes) { +Maybe ValueSerializer::ReserveRawBytes(size_t bytes) { size_t old_size = buffer_size_; size_t new_size = old_size + bytes; - if (new_size > buffer_capacity_) ExpandBuffer(new_size); + if (V8_UNLIKELY(new_size > buffer_capacity_)) { + bool ok; + if (!ExpandBuffer(new_size).To(&ok)) { + return Nothing(); + } + } buffer_size_ = new_size; - return &buffer_[old_size]; + return Just(&buffer_[old_size]); } -void ValueSerializer::ExpandBuffer(size_t required_capacity) { +Maybe ValueSerializer::ExpandBuffer(size_t required_capacity) { DCHECK_GT(required_capacity, buffer_capacity_); size_t requested_capacity = std::max(required_capacity, buffer_capacity_ * 2) + 64; @@ -241,9 +265,15 @@ void ValueSerializer::ExpandBuffer(size_t required_capacity) { new_buffer = realloc(buffer_, requested_capacity); provided_capacity = requested_capacity; } - DCHECK_GE(provided_capacity, requested_capacity); - buffer_ = reinterpret_cast(new_buffer); - buffer_capacity_ = provided_capacity; + if (new_buffer) { + DCHECK(provided_capacity >= requested_capacity); + buffer_ = reinterpret_cast(new_buffer); + buffer_capacity_ = provided_capacity; + return Just(true); + } else { + out_of_memory_ = true; + return Nothing(); + } } void ValueSerializer::WriteUint32(uint32_t value) { @@ -274,20 +304,21 @@ void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id, } Maybe ValueSerializer::WriteObject(Handle object) { + out_of_memory_ = false; if (object->IsSmi()) { WriteSmi(Smi::cast(*object)); - return Just(true); + return ThrowIfOutOfMemory(); } DCHECK(object->IsHeapObject()); switch (HeapObject::cast(*object)->map()->instance_type()) { case ODDBALL_TYPE: WriteOddball(Oddball::cast(*object)); - return Just(true); + return ThrowIfOutOfMemory(); case HEAP_NUMBER_TYPE: case MUTABLE_HEAP_NUMBER_TYPE: WriteHeapNumber(HeapNumber::cast(*object)); - return Just(true); + return ThrowIfOutOfMemory(); case JS_TYPED_ARRAY_TYPE: case JS_DATA_VIEW_TYPE: { // Despite being JSReceivers, these have their wrapped buffer serialized @@ -296,7 +327,7 @@ Maybe ValueSerializer::WriteObject(Handle object) { // TODO(jbroman): It may be possible to avoid materializing a typed // array's buffer here. Handle view = Handle::cast(object); - if (!id_map_.Find(view)) { + if (!id_map_.Find(view) && !treat_array_buffer_views_as_host_objects_) { Handle buffer( view->IsJSTypedArray() ? Handle::cast(view)->GetBuffer() @@ -308,7 +339,7 @@ Maybe ValueSerializer::WriteObject(Handle object) { default: if (object->IsString()) { WriteString(Handle::cast(object)); - return Just(true); + return ThrowIfOutOfMemory(); } else if (object->IsJSReceiver()) { return WriteJSReceiver(Handle::cast(object)); } else { @@ -357,22 +388,9 @@ void ValueSerializer::WriteString(Handle string) { String::FlatContent flat = string->GetFlatContent(); DCHECK(flat.IsFlat()); if (flat.IsOneByte()) { - // The existing format uses UTF-8, rather than Latin-1. As a result we must - // to do work to encode strings that have characters outside ASCII. - // TODO(jbroman): In a future format version, consider adding a tag for - // Latin-1 strings, so that this can be skipped. - WriteTag(SerializationTag::kUtf8String); Vector chars = flat.ToOneByteVector(); - if (String::IsAscii(chars.begin(), chars.length())) { - WriteOneByteString(chars); - } else { - v8::Local api_string = Utils::ToLocal(string); - uint32_t utf8_length = api_string->Utf8Length(); - WriteVarint(utf8_length); - api_string->WriteUtf8( - reinterpret_cast(ReserveRawBytes(utf8_length)), utf8_length, - nullptr, v8::String::NO_NULL_TERMINATION); - } + WriteTag(SerializationTag::kOneByteString); + WriteOneByteString(chars); } else if (flat.IsTwoByte()) { Vector chars = flat.ToUC16Vector(); uint32_t byte_length = chars.length() * sizeof(uc16); @@ -392,7 +410,7 @@ Maybe ValueSerializer::WriteJSReceiver(Handle receiver) { if (uint32_t id = *id_map_entry) { WriteTag(SerializationTag::kObjectReference); WriteVarint(id - 1); - return Just(true); + return ThrowIfOutOfMemory(); } // Otherwise, allocate an ID for it. @@ -432,12 +450,12 @@ Maybe ValueSerializer::WriteJSReceiver(Handle receiver) { return WriteHostObject(Handle::cast(receiver)); case JS_DATE_TYPE: WriteJSDate(JSDate::cast(*receiver)); - return Just(true); + return ThrowIfOutOfMemory(); case JS_VALUE_TYPE: return WriteJSValue(Handle::cast(receiver)); case JS_REGEXP_TYPE: WriteJSRegExp(JSRegExp::cast(*receiver)); - return Just(true); + return ThrowIfOutOfMemory(); case JS_MAP_TYPE: return WriteJSMap(Handle::cast(receiver)); case JS_SET_TYPE: @@ -498,7 +516,7 @@ Maybe ValueSerializer::WriteJSObject(Handle object) { WriteTag(SerializationTag::kEndJSObject); WriteVarint(properties_written); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe ValueSerializer::WriteJSObjectSlow(Handle object) { @@ -513,7 +531,7 @@ Maybe ValueSerializer::WriteJSObjectSlow(Handle object) { } WriteTag(SerializationTag::kEndJSObject); WriteVarint(properties_written); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe ValueSerializer::WriteJSArray(Handle array) { @@ -532,10 +550,6 @@ Maybe ValueSerializer::WriteJSArray(Handle array) { if (should_serialize_densely) { DCHECK_LE(length, static_cast(FixedArray::kMaxLength)); - - // TODO(jbroman): Distinguish between undefined and a hole (this can happen - // if serializing one of the elements deletes another). This requires wire - // format changes. WriteTag(SerializationTag::kBeginDenseJSArray); WriteVarint(length); uint32_t i = 0; @@ -550,6 +564,9 @@ Maybe ValueSerializer::WriteJSArray(Handle array) { break; } case FAST_DOUBLE_ELEMENTS: { + // Elements are empty_fixed_array, not a FixedDoubleArray, if the array + // is empty. No elements to encode in this case anyhow. + if (length == 0) break; Handle elements( FixedDoubleArray::cast(array->elements()), isolate_); for (; i < length; i++) { @@ -583,6 +600,13 @@ Maybe ValueSerializer::WriteJSArray(Handle array) { // with. Handle element; LookupIterator it(isolate_, array, i, array, LookupIterator::OWN); + if (!it.IsFound()) { + // This can happen in the case where an array that was originally dense + // became sparse during serialization. It's too late to switch to the + // sparse format, but we can mark the elements as absent. + WriteTag(SerializationTag::kTheHole); + continue; + } if (!Object::GetProperty(&it).ToHandle(&element) || !WriteObject(element).FromMaybe(false)) { return Nothing(); @@ -618,7 +642,7 @@ Maybe ValueSerializer::WriteJSArray(Handle array) { WriteVarint(properties_written); WriteVarint(length); } - return Just(true); + return ThrowIfOutOfMemory(); } void ValueSerializer::WriteJSDate(JSDate* date) { @@ -636,32 +660,19 @@ Maybe ValueSerializer::WriteJSValue(Handle value) { WriteTag(SerializationTag::kNumberObject); WriteDouble(inner_value->Number()); } else if (inner_value->IsString()) { - // TODO(jbroman): Replace UTF-8 encoding with the same options available for - // ordinary strings. WriteTag(SerializationTag::kStringObject); - v8::Local api_string = - Utils::ToLocal(handle(String::cast(inner_value), isolate_)); - uint32_t utf8_length = api_string->Utf8Length(); - WriteVarint(utf8_length); - api_string->WriteUtf8(reinterpret_cast(ReserveRawBytes(utf8_length)), - utf8_length, nullptr, - v8::String::NO_NULL_TERMINATION); + WriteString(handle(String::cast(inner_value), isolate_)); } else { DCHECK(inner_value->IsSymbol()); ThrowDataCloneError(MessageTemplate::kDataCloneError, value); return Nothing(); } - return Just(true); + return ThrowIfOutOfMemory(); } void ValueSerializer::WriteJSRegExp(JSRegExp* regexp) { WriteTag(SerializationTag::kRegExp); - v8::Local api_string = - Utils::ToLocal(handle(regexp->Pattern(), isolate_)); - uint32_t utf8_length = api_string->Utf8Length(); - WriteVarint(utf8_length); - api_string->WriteUtf8(reinterpret_cast(ReserveRawBytes(utf8_length)), - utf8_length, nullptr, v8::String::NO_NULL_TERMINATION); + WriteString(handle(regexp->Pattern(), isolate_)); WriteVarint(static_cast(regexp->GetFlags())); } @@ -693,7 +704,7 @@ Maybe ValueSerializer::WriteJSMap(Handle map) { } WriteTag(SerializationTag::kEndJSMap); WriteVarint(length); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe ValueSerializer::WriteJSSet(Handle set) { @@ -723,7 +734,7 @@ Maybe ValueSerializer::WriteJSSet(Handle set) { } WriteTag(SerializationTag::kEndJSSet); WriteVarint(length); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe ValueSerializer::WriteJSArrayBuffer( @@ -741,14 +752,14 @@ Maybe ValueSerializer::WriteJSArrayBuffer( WriteTag(SerializationTag::kSharedArrayBuffer); WriteVarint(index.FromJust()); - return Just(true); + return ThrowIfOutOfMemory(); } uint32_t* transfer_entry = array_buffer_transfer_map_.Find(array_buffer); if (transfer_entry) { WriteTag(SerializationTag::kArrayBufferTransfer); WriteVarint(*transfer_entry); - return Just(true); + return ThrowIfOutOfMemory(); } if (array_buffer->was_neutered()) { ThrowDataCloneError(MessageTemplate::kDataCloneErrorNeuteredArrayBuffer); @@ -762,10 +773,13 @@ Maybe ValueSerializer::WriteJSArrayBuffer( WriteTag(SerializationTag::kArrayBuffer); WriteVarint(byte_length); WriteRawBytes(array_buffer->backing_store(), byte_length); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) { + if (treat_array_buffer_views_as_host_objects_) { + return WriteHostObject(handle(view, isolate_)); + } WriteTag(SerializationTag::kArrayBufferView); ArrayBufferViewTag tag = ArrayBufferViewTag::kInt8Array; if (view->IsJSTypedArray()) { @@ -784,7 +798,7 @@ Maybe ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) { WriteVarint(static_cast(tag)); WriteVarint(NumberToUint32(view->byte_offset())); WriteVarint(NumberToUint32(view->byte_length())); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe ValueSerializer::WriteWasmModule(Handle object) { @@ -797,8 +811,10 @@ Maybe ValueSerializer::WriteWasmModule(Handle object) { Handle wire_bytes(compiled_part->module_bytes(), isolate_); int wire_bytes_length = wire_bytes->length(); WriteVarint(wire_bytes_length); - uint8_t* destination = ReserveRawBytes(wire_bytes_length); - String::WriteToFlat(*wire_bytes, destination, 0, wire_bytes_length); + uint8_t* destination; + if (ReserveRawBytes(wire_bytes_length).To(&destination)) { + String::WriteToFlat(*wire_bytes, destination, 0, wire_bytes_length); + } std::unique_ptr script_data = WasmCompiledModuleSerializer::SerializeWasmModule(isolate_, @@ -807,10 +823,11 @@ Maybe ValueSerializer::WriteWasmModule(Handle object) { WriteVarint(script_data_length); WriteRawBytes(script_data->data(), script_data_length); - return Just(true); + return ThrowIfOutOfMemory(); } Maybe ValueSerializer::WriteHostObject(Handle object) { + WriteTag(SerializationTag::kHostObject); if (!delegate_) { isolate_->Throw(*isolate_->factory()->NewError( isolate_->error_function(), MessageTemplate::kDataCloneError, object)); @@ -858,6 +875,14 @@ void ValueSerializer::ThrowDataCloneError( isolate_->factory()->empty_string()); } +Maybe ValueSerializer::ThrowIfOutOfMemory() { + if (out_of_memory_) { + ThrowDataCloneError(MessageTemplate::kDataCloneErrorOutOfMemory); + return Nothing(); + } + return Just(true); +} + void ValueSerializer::ThrowDataCloneError( MessageTemplate::Template template_index, Handle arg0) { Handle message = @@ -1084,6 +1109,8 @@ MaybeHandle ValueDeserializer::ReadObjectInternal() { } case SerializationTag::kUtf8String: return ReadUtf8String(); + case SerializationTag::kOneByteString: + return ReadOneByteString(); case SerializationTag::kTwoByteString: return ReadTwoByteString(); case SerializationTag::kObjectReference: { @@ -1122,14 +1149,28 @@ MaybeHandle ValueDeserializer::ReadObjectInternal() { } case SerializationTag::kWasmModule: return ReadWasmModule(); - default: - // TODO(jbroman): Introduce an explicit tag for host objects to avoid - // having to treat every unknown tag as a potential host object. - position_--; + case SerializationTag::kHostObject: return ReadHostObject(); + default: + // Before there was an explicit tag for host objects, all unknown tags + // were delegated to the host. + if (version_ < 13) { + position_--; + return ReadHostObject(); + } + return MaybeHandle(); } } +MaybeHandle ValueDeserializer::ReadString() { + if (version_ < 12) return ReadUtf8String(); + Handle object; + if (!ReadObject().ToHandle(&object) || !object->IsString()) { + return MaybeHandle(); + } + return Handle::cast(object); +} + MaybeHandle ValueDeserializer::ReadUtf8String() { uint32_t utf8_length; Vector utf8_bytes; @@ -1142,6 +1183,18 @@ MaybeHandle ValueDeserializer::ReadUtf8String() { Vector::cast(utf8_bytes), pretenure_); } +MaybeHandle ValueDeserializer::ReadOneByteString() { + uint32_t byte_length; + Vector bytes; + if (!ReadVarint().To(&byte_length) || + byte_length > + static_cast(std::numeric_limits::max()) || + !ReadRawBytes(byte_length).To(&bytes)) { + return MaybeHandle(); + } + return isolate_->factory()->NewStringFromOneByte(bytes, pretenure_); +} + MaybeHandle ValueDeserializer::ReadTwoByteString() { uint32_t byte_length; Vector bytes; @@ -1280,10 +1333,20 @@ MaybeHandle ValueDeserializer::ReadDenseJSArray() { Handle elements(FixedArray::cast(array->elements()), isolate_); for (uint32_t i = 0; i < length; i++) { + SerializationTag tag; + if (PeekTag().To(&tag) && tag == SerializationTag::kTheHole) { + ConsumeTag(SerializationTag::kTheHole); + continue; + } + Handle element; if (!ReadObject().ToHandle(&element)) return MaybeHandle(); - // TODO(jbroman): Distinguish between undefined and a hole. - if (element->IsUndefined(isolate_)) continue; + + // Serialization versions less than 11 encode the hole the same as + // undefined. For consistency with previous behavior, store these as the + // hole. Past version 11, undefined means undefined. + if (version_ < 11 && element->IsUndefined(isolate_)) continue; + elements->set(i, *element); } @@ -1341,7 +1404,7 @@ MaybeHandle ValueDeserializer::ReadJSValue(SerializationTag tag) { } case SerializationTag::kStringObject: { Handle string; - if (!ReadUtf8String().ToHandle(&string)) return MaybeHandle(); + if (!ReadString().ToHandle(&string)) return MaybeHandle(); value = Handle::cast(isolate_->factory()->NewJSObject( isolate_->string_function(), pretenure_)); value->set_value(*string); @@ -1360,7 +1423,7 @@ MaybeHandle ValueDeserializer::ReadJSRegExp() { Handle pattern; uint32_t raw_flags; Handle regexp; - if (!ReadUtf8String().ToHandle(&pattern) || + if (!ReadString().ToHandle(&pattern) || !ReadVarint().To(&raw_flags) || !JSRegExp::New(pattern, static_cast(raw_flags)) .ToHandle(®exp)) { @@ -1564,11 +1627,16 @@ MaybeHandle ValueDeserializer::ReadWasmModule() { } // If that fails, recompile. - wasm::ErrorThrower thrower(isolate_, "ValueDeserializer::ReadWasmModule"); - return wasm::CreateModuleObjectFromBytes( - isolate_, wire_bytes.begin(), wire_bytes.end(), &thrower, - wasm::ModuleOrigin::kWasmOrigin, Handle