aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/d8.cc
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2017-03-08 21:41:09 +0100
committerMichaƫl Zasso <targos@protonmail.com>2017-03-25 09:44:10 +0100
commit83bf2975ec7a105c1e08caba21e5c4cccd9beabb (patch)
treedd619038cdc120de77ef4f1bcea5f1b305946024 /deps/v8/src/d8.cc
parent3f9c8fb87fe7ac79f433104ab564130d000772b8 (diff)
downloadandroid-node-v8-83bf2975ec7a105c1e08caba21e5c4cccd9beabb.tar.gz
android-node-v8-83bf2975ec7a105c1e08caba21e5c4cccd9beabb.tar.bz2
android-node-v8-83bf2975ec7a105c1e08caba21e5c4cccd9beabb.zip
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 <info@bnoordhuis.nl> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Diffstat (limited to 'deps/v8/src/d8.cc')
-rw-r--r--deps/v8/src/d8.cc580
1 files changed, 222 insertions, 358 deletions
diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc
index a34bafcc65..368b33ecc7 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<Value> Throw(Isolate* isolate, const char* message) {
}
-bool FindInObjectList(Local<Object> 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> object) {
if (object->InternalFieldCount() != 1) {
Throw(isolate, "this is not a Worker");
@@ -416,7 +407,10 @@ Global<Function> Shell::stringify_function_;
base::LazyMutex Shell::workers_mutex_;
bool Shell::allow_new_workers_ = true;
i::List<Worker*> Shell::workers_;
-i::List<SharedArrayBuffer::Contents> Shell::externalized_shared_contents_;
+std::unordered_set<SharedArrayBuffer::Contents,
+ Shell::SharedArrayBufferContentsHash,
+ Shell::SharedArrayBufferContentsIsEqual>
+ Shell::externalized_shared_contents_;
Global<Context> Shell::evaluation_context_;
ArrayBuffer::Allocator* Shell::array_buffer_allocator;
@@ -1179,7 +1173,6 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
- Local<Context> context = isolate->GetCurrentContext();
if (args.Length() < 1) {
Throw(isolate, "Invalid argument");
@@ -1192,36 +1185,12 @@ void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
Local<Value> message = args[0];
- ObjectList to_transfer;
- if (args.Length() >= 2) {
- if (!args[1]->IsArray()) {
- Throw(isolate, "Transfer list must be an Array");
- return;
- }
-
- Local<Array> transfer = Local<Array>::Cast(args[1]);
- uint32_t length = transfer->Length();
- for (uint32_t i = 0; i < length; ++i) {
- Local<Value> 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<Object>::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<Value> transfer =
+ args.Length() >= 2 ? args[1] : Local<Value>::Cast(Undefined(isolate));
+ std::unique_ptr<SerializationData> data =
+ Shell::SerializeValue(isolate, message, transfer);
+ if (data) {
+ worker->PostMessage(std::move(data));
}
}
@@ -1234,14 +1203,12 @@ void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
return;
}
- SerializationData* data = worker->GetMessage();
+ std::unique_ptr<SerializationData> data = worker->GetMessage();
if (data) {
- int offset = 0;
- Local<Value> data_value;
- if (Shell::DeserializeValue(isolate, *data, &offset).ToLocal(&data_value)) {
- args.GetReturnValue().Set(data_value);
+ Local<Value> 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<uint8_t> 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<SerializationTag>(Read<uint8_t>(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<int>(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<int>(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<SerializationData> data) {
base::LockGuard<base::Mutex> lock_guard(&mutex_);
- data_.Add(data);
+ data_.push_back(std::move(data));
}
-
-bool SerializationDataQueue::Dequeue(SerializationData** data) {
+bool SerializationDataQueue::Dequeue(
+ std::unique_ptr<SerializationData>* out_data) {
+ out_data->reset();
base::LockGuard<base::Mutex> 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<base::Mutex> lock_guard(&mutex_);
- return data_.is_empty();
+ return data_.empty();
}
void SerializationDataQueue::Clear() {
base::LockGuard<base::Mutex> 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<SerializationData> data) {
+ in_queue_.Enqueue(std::move(data));
in_semaphore_.Signal();
}
-
-SerializationData* Worker::GetMessage() {
- SerializationData* data = NULL;
- while (!out_queue_.Dequeue(&data)) {
+std::unique_ptr<SerializationData> Worker::GetMessage() {
+ std::unique_ptr<SerializationData> 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<SerializationData> data;
if (!in_queue_.Dequeue(&data)) continue;
- if (data == NULL) {
+ if (!data) {
break;
}
- int offset = 0;
- Local<Value> data_value;
- if (Shell::DeserializeValue(isolate, *data, &offset)
- .ToLocal(&data_value)) {
- Local<Value> argv[] = {data_value};
+ v8::TryCatch try_catch(isolate);
+ Local<Value> value;
+ if (Shell::DeserializeValue(isolate, std::move(data))
+ .ToLocal(&value)) {
+ Local<Value> 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<v8::Value>& args) {
}
Local<Value> 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<Value> transfer = Undefined(isolate);
+ std::unique_ptr<SerializationData> data =
+ Shell::SerializeValue(isolate, message, transfer);
+ if (data) {
DCHECK(args.Data()->IsExternal());
Local<External> this_value = Local<External>::Cast(args.Data());
Worker* worker = static_cast<Worker*>(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> value,
- const ObjectList& to_transfer,
- ObjectList* seen_objects,
- SerializationData* out_data) {
- DCHECK(out_data);
- Local<Context> 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<Number> num = Local<Number>::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> array = Local<Array>::Cast(value);
- if (FindInObjectList(array, *seen_objects)) {
- Throw(isolate, "Duplicated arrays not supported");
- return false;
+ Maybe<bool> WriteValue(Local<Context> context, Local<Value> value,
+ Local<Value> transfer) {
+ bool ok;
+ DCHECK(!data_);
+ data_.reset(new SerializationData);
+ if (!PrepareTransfer(context, transfer).To(&ok)) {
+ return Nothing<bool>();
}
- 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<Value> 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<bool>();
}
- } else if (value->IsArrayBuffer()) {
- Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(value);
- if (FindInObjectList(array_buffer, *seen_objects)) {
- Throw(isolate, "Duplicated array buffers not supported");
- return false;
+
+ if (!FinalizeTransfer().To(&ok)) {
+ return Nothing<bool>();
}
- 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<uint8_t*, size_t> pair = serializer_.Release();
+ data_->data_.reset(pair.first);
+ data_->size_ = pair.second;
+ return Just(true);
+ }
- int32_t byte_length = static_cast<int32_t>(contents.ByteLength());
- out_data->WriteTag(kSerializationTagArrayBuffer);
- out_data->Write(byte_length);
- out_data->WriteMemory(contents.Data(), byte_length);
- }
- } else if (value->IsSharedArrayBuffer()) {
- Local<SharedArrayBuffer> sab = Local<SharedArrayBuffer>::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<SerializationData> Release() { return std::move(data_); }
+
+ protected:
+ // Implements ValueSerializer::Delegate.
+ void ThrowDataCloneError(Local<String> message) override {
+ isolate_->ThrowException(Exception::Error(message));
+ }
+
+ Maybe<uint32_t> GetSharedArrayBufferId(
+ Isolate* isolate, Local<SharedArrayBuffer> 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<uint32_t>(static_cast<uint32_t>(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<uint32_t>(static_cast<uint32_t>(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<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
+ if (transfer->IsArray()) {
+ Local<Array> transfer_array = Local<Array>::Cast(transfer);
+ uint32_t length = transfer_array->Length();
+ for (uint32_t i = 0; i < length; ++i) {
+ Local<Value> element;
+ if (transfer_array->Get(context, i).ToLocal(&element)) {
+ if (!element->IsArrayBuffer()) {
+ Throw(isolate_, "Transfer array elements must be an ArrayBuffer");
+ break;
+ }
+
+ Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(element);
+ serializer_.TransferArrayBuffer(
+ static_cast<uint32_t>(array_buffers_.size()), array_buffer);
+ array_buffers_.emplace_back(isolate_, array_buffer);
+ } else {
+ return Nothing<bool>();
+ }
+ }
+ return Just(true);
+ } else if (transfer->IsUndefined()) {
+ return Just(true);
} else {
- contents = sab->Externalize();
- base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
- externalized_shared_contents_.Add(contents);
- }
- out_data->WriteSharedArrayBufferContents(contents);
- } else if (value->IsObject()) {
- Local<Object> object = Local<Object>::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<bool>();
}
- seen_objects->Add(object);
- Local<Array> property_names;
- if (!object->GetOwnPropertyNames(context).ToLocal(&property_names)) {
- Throw(isolate, "Unable to get property names");
- return false;
+ }
+
+ Maybe<bool> FinalizeTransfer() {
+ for (const auto& global_array_buffer : array_buffers_) {
+ Local<ArrayBuffer> array_buffer =
+ Local<ArrayBuffer>::New(isolate_, global_array_buffer);
+ if (!array_buffer->IsNeuterable()) {
+ Throw(isolate_, "ArrayBuffer could not be transferred");
+ return Nothing<bool>();
+ }
+
+ 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<Value> name;
- Local<Value> 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<SharedArrayBuffer> shared_array_buffer =
+ Local<SharedArrayBuffer>::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<SerializationData> data_;
+ std::vector<Global<ArrayBuffer>> array_buffers_;
+ std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
+ DISALLOW_COPY_AND_ASSIGN(Serializer);
+};
-MaybeLocal<Value> Shell::DeserializeValue(Isolate* isolate,
- const SerializationData& data,
- int* offset) {
- DCHECK(offset);
- EscapableHandleScope scope(isolate);
- Local<Value> result;
- SerializationTag tag = data.ReadTag(offset);
+class Deserializer : public ValueDeserializer::Delegate {
+ public:
+ Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> 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<double>(offset));
- break;
- case kSerializationTagString: {
- int length = data.Read<int>(offset);
- CHECK(length >= 0);
- std::vector<char> buffer(length + 1); // + 1 so it is never empty.
- data.ReadMemory(&buffer[0], length, offset);
- MaybeLocal<String> str =
- String::NewFromUtf8(isolate, &buffer[0], NewStringType::kNormal,
- length).ToLocalChecked();
- if (!str.IsEmpty()) result = str.ToLocalChecked();
- break;
+ MaybeLocal<Value> ReadValue(Local<Context> context) {
+ bool read_header;
+ if (!deserializer_.ReadHeader(context).To(&read_header)) {
+ return MaybeLocal<Value>();
}
- case kSerializationTagArray: {
- uint32_t length = data.Read<uint32_t>(offset);
- Local<Array> array = Array::New(isolate, length);
- for (uint32_t i = 0; i < length; ++i) {
- Local<Value> 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<int>(offset);
- Local<Object> object = Object::New(isolate);
- for (int i = 0; i < length; ++i) {
- Local<Value> property_name;
- CHECK(DeserializeValue(isolate, data, offset).ToLocal(&property_name));
- Local<Value> 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<ArrayBuffer> array_buffer =
+ ArrayBuffer::New(isolate_, contents.Data(), contents.ByteLength());
+ deserializer_.TransferArrayBuffer(index++, array_buffer);
}
- case kSerializationTagArrayBuffer: {
- int32_t byte_length = data.Read<int32_t>(offset);
- Local<ArrayBuffer> array_buffer = ArrayBuffer::New(isolate, byte_length);
- ArrayBuffer::Contents contents = array_buffer->GetContents();
- DCHECK(static_cast<size_t>(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<SharedArrayBuffer> 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<Value> 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<SerializationData> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(Deserializer);
+};
+
+std::unique_ptr<SerializationData> Shell::SerializeValue(
+ Isolate* isolate, Local<Value> value, Local<Value> transfer) {
+ bool ok;
+ Local<Context> context = isolate->GetCurrentContext();
+ Serializer serializer(isolate);
+ if (serializer.WriteValue(context, value, transfer).To(&ok)) {
+ std::unique_ptr<SerializationData> data = serializer.Release();
+ base::LockGuard<base::Mutex> 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<Value> Shell::DeserializeValue(
+ Isolate* isolate, std::unique_ptr<SerializationData> data) {
+ Local<Value> value;
+ Local<Context> context = isolate->GetCurrentContext();
+ Deserializer deserializer(isolate, std::move(data));
+ return deserializer.ReadValue(context);
}
@@ -2890,12 +2756,10 @@ void Shell::CleanupWorkers() {
base::LockGuard<base::Mutex> 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();
}