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