[d8] Use ValueSerializer for postMessage (instead of ad-hoc serializer)
Review-Url: https://codereview.chromium.org/2643723010 Cr-Commit-Position: refs/heads/master@{#42749}
This commit is contained in:
parent
9515f7edf0
commit
966355585b
@ -1740,6 +1740,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);
|
||||
|
612
src/d8.cc
612
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"
|
||||
@ -218,16 +219,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");
|
||||
@ -410,7 +401,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;
|
||||
@ -1150,7 +1144,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");
|
||||
@ -1163,36 +1156,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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1205,14 +1174,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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2148,14 +2115,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());
|
||||
@ -2163,96 +2128,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::ClearTransferredArrayBuffers() {
|
||||
array_buffer_contents_.clear();
|
||||
}
|
||||
|
||||
|
||||
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 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();
|
||||
}
|
||||
|
||||
|
||||
@ -2281,22 +2185,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;
|
||||
}
|
||||
|
||||
|
||||
@ -2360,19 +2262,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2399,21 +2303,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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2606,234 +2504,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();
|
||||
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>();
|
||||
}
|
||||
serializer_.WriteHeader();
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
if (!serializer_.WriteValue(context, value).To(&ok)) {
|
||||
data_.reset();
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
SharedArrayBuffer::Contents contents;
|
||||
if (sab->IsExternal()) {
|
||||
contents = sab->GetContents();
|
||||
} 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;
|
||||
}
|
||||
seen_objects->Add(object);
|
||||
Local<Array> property_names;
|
||||
if (!object->GetOwnPropertyNames(context).ToLocal(&property_names)) {
|
||||
Throw(isolate, "Unable to get property names");
|
||||
return false;
|
||||
if (!FinalizeTransfer().To(&ok)) {
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Throw(isolate, "Don't know how to serialize object");
|
||||
return false;
|
||||
std::pair<uint8_t*, size_t> pair = serializer_.Release();
|
||||
data_->data_.reset(pair.first);
|
||||
data_->size_ = pair.second;
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
Throw(isolate_, "Transfer list must be an Array or undefined");
|
||||
return Nothing<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
return Just(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);
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
MaybeLocal<Value> ReadValue(Local<Context> context) {
|
||||
bool read_header;
|
||||
if (!deserializer_.ReadHeader(context).To(&read_header)) {
|
||||
return MaybeLocal<Value>();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
MaybeLocal<Value> result = deserializer_.ReadValue(context);
|
||||
if (!result.IsEmpty()) {
|
||||
data_->ClearTransferredArrayBuffers();
|
||||
}
|
||||
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);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
MaybeLocal<Value> Shell::DeserializeValue(Isolate* isolate,
|
||||
const SerializationData& data,
|
||||
int* offset) {
|
||||
DCHECK(offset);
|
||||
EscapableHandleScope scope(isolate);
|
||||
Local<Value> result;
|
||||
SerializationTag tag = data.ReadTag(offset);
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
case kSerializationTagTransferredArrayBuffer: {
|
||||
ArrayBuffer::Contents contents;
|
||||
data.ReadArrayBufferContents(&contents, offset);
|
||||
result = ArrayBuffer::New(isolate, contents.Data(), contents.ByteLength(),
|
||||
ArrayBufferCreationMode::kInternalized);
|
||||
break;
|
||||
}
|
||||
case kSerializationTagTransferredSharedArrayBuffer: {
|
||||
SharedArrayBuffer::Contents contents;
|
||||
data.ReadSharedArrayBufferContents(&contents, offset);
|
||||
result = SharedArrayBuffer::New(isolate, contents.Data(),
|
||||
contents.ByteLength());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -2859,12 +2725,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();
|
||||
}
|
||||
|
||||
|
||||
|
109
src/d8.h
109
src/d8.h
@ -5,9 +5,13 @@
|
||||
#ifndef V8_D8_H_
|
||||
#define V8_D8_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#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 <typename T>
|
||||
void Write(const T& data) {
|
||||
WriteMemory(&data, sizeof(data));
|
||||
uint8_t* data() { return data_.get(); }
|
||||
size_t size() { return size_; }
|
||||
const std::vector<ArrayBuffer::Contents>& array_buffer_contents() {
|
||||
return array_buffer_contents_;
|
||||
}
|
||||
const std::vector<SharedArrayBuffer::Contents>&
|
||||
shared_array_buffer_contents() {
|
||||
return shared_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 <typename T>
|
||||
T Read(int* offset) const {
|
||||
T value;
|
||||
ReadMemory(&value, sizeof(value), offset);
|
||||
return value;
|
||||
}
|
||||
void ClearTransferredArrayBuffers();
|
||||
|
||||
private:
|
||||
i::List<uint8_t> data_;
|
||||
i::List<ArrayBuffer::Contents> array_buffer_contents_;
|
||||
i::List<SharedArrayBuffer::Contents> shared_array_buffer_contents_;
|
||||
struct DataDeleter {
|
||||
void operator()(uint8_t* p) const { free(p); }
|
||||
};
|
||||
|
||||
std::unique_ptr<uint8_t, DataDeleter> data_;
|
||||
size_t size_;
|
||||
std::vector<ArrayBuffer::Contents> array_buffer_contents_;
|
||||
std::vector<SharedArrayBuffer::Contents> shared_array_buffer_contents_;
|
||||
|
||||
private:
|
||||
friend class Serializer;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SerializationData);
|
||||
};
|
||||
|
||||
|
||||
class SerializationDataQueue {
|
||||
public:
|
||||
void Enqueue(SerializationData* data);
|
||||
bool Dequeue(SerializationData** data);
|
||||
void Enqueue(std::unique_ptr<SerializationData> data);
|
||||
bool Dequeue(std::unique_ptr<SerializationData>* data);
|
||||
bool IsEmpty();
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
base::Mutex mutex_;
|
||||
i::List<SerializationData*> data_;
|
||||
std::vector<std::unique_ptr<SerializationData>> 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<SerializationData> 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<SerializationData> 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<Local<Object>> ObjectList;
|
||||
static bool SerializeValue(Isolate* isolate, Local<Value> value,
|
||||
const ObjectList& to_transfer,
|
||||
ObjectList* seen_objects,
|
||||
SerializationData* out_data);
|
||||
static MaybeLocal<Value> DeserializeValue(Isolate* isolate,
|
||||
const SerializationData& data,
|
||||
int* offset);
|
||||
static std::unique_ptr<SerializationData> SerializeValue(
|
||||
Isolate* isolate, Local<Value> value, Local<Value> transfer);
|
||||
static MaybeLocal<Value> DeserializeValue(
|
||||
Isolate* isolate, std::unique_ptr<SerializationData> data);
|
||||
static void CleanupWorkers();
|
||||
static int* LookupCounter(const char* name);
|
||||
static void* CreateHistogram(const char* name,
|
||||
@ -443,10 +424,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<Worker*> workers_;
|
||||
static i::List<SharedArrayBuffer::Contents> externalized_shared_contents_;
|
||||
static std::unordered_set<SharedArrayBuffer::Contents,
|
||||
SharedArrayBufferContentsHash,
|
||||
SharedArrayBufferContentsIsEqual>
|
||||
externalized_shared_contents_;
|
||||
|
||||
static void WriteIgnitionDispatchCountersFile(v8::Isolate* isolate);
|
||||
static Counter* GetCounter(const char* name, bool is_histogram);
|
||||
|
@ -676,6 +676,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, \
|
||||
|
@ -217,18 +217,26 @@ void ValueSerializer::WriteTwoByteString(Vector<const uc16> 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<uint8_t*> 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 (new_size > buffer_capacity_) {
|
||||
bool ok;
|
||||
if (!ExpandBuffer(new_size).To(&ok)) {
|
||||
return Nothing<uint8_t*>();
|
||||
}
|
||||
}
|
||||
buffer_size_ = new_size;
|
||||
return &buffer_[old_size];
|
||||
return Just(&buffer_[old_size]);
|
||||
}
|
||||
|
||||
void ValueSerializer::ExpandBuffer(size_t required_capacity) {
|
||||
Maybe<bool> 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 +249,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<uint8_t*>(new_buffer);
|
||||
buffer_capacity_ = provided_capacity;
|
||||
if (new_buffer) {
|
||||
DCHECK(provided_capacity >= requested_capacity);
|
||||
buffer_ = reinterpret_cast<uint8_t*>(new_buffer);
|
||||
buffer_capacity_ = provided_capacity;
|
||||
return Just(true);
|
||||
} else {
|
||||
out_of_memory_ = true;
|
||||
return Nothing<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
void ValueSerializer::WriteUint32(uint32_t value) {
|
||||
@ -274,20 +288,21 @@ void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id,
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteObject(Handle<Object> 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
|
||||
@ -308,7 +323,7 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
|
||||
default:
|
||||
if (object->IsString()) {
|
||||
WriteString(Handle<String>::cast(object));
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
} else if (object->IsJSReceiver()) {
|
||||
return WriteJSReceiver(Handle<JSReceiver>::cast(object));
|
||||
} else {
|
||||
@ -369,9 +384,11 @@ void ValueSerializer::WriteString(Handle<String> string) {
|
||||
v8::Local<v8::String> api_string = Utils::ToLocal(string);
|
||||
uint32_t utf8_length = api_string->Utf8Length();
|
||||
WriteVarint(utf8_length);
|
||||
api_string->WriteUtf8(
|
||||
reinterpret_cast<char*>(ReserveRawBytes(utf8_length)), utf8_length,
|
||||
nullptr, v8::String::NO_NULL_TERMINATION);
|
||||
uint8_t* dest;
|
||||
if (ReserveRawBytes(utf8_length).To(&dest)) {
|
||||
api_string->WriteUtf8(reinterpret_cast<char*>(dest), utf8_length,
|
||||
nullptr, v8::String::NO_NULL_TERMINATION);
|
||||
}
|
||||
}
|
||||
} else if (flat.IsTwoByte()) {
|
||||
Vector<const uc16> chars = flat.ToUC16Vector();
|
||||
@ -392,7 +409,7 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> 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 +449,12 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
|
||||
return WriteHostObject(Handle<JSObject>::cast(receiver));
|
||||
case JS_DATE_TYPE:
|
||||
WriteJSDate(JSDate::cast(*receiver));
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
case JS_VALUE_TYPE:
|
||||
return WriteJSValue(Handle<JSValue>::cast(receiver));
|
||||
case JS_REGEXP_TYPE:
|
||||
WriteJSRegExp(JSRegExp::cast(*receiver));
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
case JS_MAP_TYPE:
|
||||
return WriteJSMap(Handle<JSMap>::cast(receiver));
|
||||
case JS_SET_TYPE:
|
||||
@ -498,7 +515,7 @@ Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) {
|
||||
|
||||
WriteTag(SerializationTag::kEndJSObject);
|
||||
WriteVarint<uint32_t>(properties_written);
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteJSObjectSlow(Handle<JSObject> object) {
|
||||
@ -513,7 +530,7 @@ Maybe<bool> ValueSerializer::WriteJSObjectSlow(Handle<JSObject> object) {
|
||||
}
|
||||
WriteTag(SerializationTag::kEndJSObject);
|
||||
WriteVarint<uint32_t>(properties_written);
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
|
||||
@ -618,7 +635,7 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
|
||||
WriteVarint<uint32_t>(properties_written);
|
||||
WriteVarint<uint32_t>(length);
|
||||
}
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
void ValueSerializer::WriteJSDate(JSDate* date) {
|
||||
@ -643,15 +660,17 @@ Maybe<bool> ValueSerializer::WriteJSValue(Handle<JSValue> value) {
|
||||
Utils::ToLocal(handle(String::cast(inner_value), isolate_));
|
||||
uint32_t utf8_length = api_string->Utf8Length();
|
||||
WriteVarint(utf8_length);
|
||||
api_string->WriteUtf8(reinterpret_cast<char*>(ReserveRawBytes(utf8_length)),
|
||||
utf8_length, nullptr,
|
||||
v8::String::NO_NULL_TERMINATION);
|
||||
uint8_t* dest;
|
||||
if (ReserveRawBytes(utf8_length).To(&dest)) {
|
||||
api_string->WriteUtf8(reinterpret_cast<char*>(dest), utf8_length, nullptr,
|
||||
v8::String::NO_NULL_TERMINATION);
|
||||
}
|
||||
} else {
|
||||
DCHECK(inner_value->IsSymbol());
|
||||
ThrowDataCloneError(MessageTemplate::kDataCloneError, value);
|
||||
return Nothing<bool>();
|
||||
}
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
void ValueSerializer::WriteJSRegExp(JSRegExp* regexp) {
|
||||
@ -660,8 +679,11 @@ void ValueSerializer::WriteJSRegExp(JSRegExp* regexp) {
|
||||
Utils::ToLocal(handle(regexp->Pattern(), isolate_));
|
||||
uint32_t utf8_length = api_string->Utf8Length();
|
||||
WriteVarint(utf8_length);
|
||||
api_string->WriteUtf8(reinterpret_cast<char*>(ReserveRawBytes(utf8_length)),
|
||||
utf8_length, nullptr, v8::String::NO_NULL_TERMINATION);
|
||||
uint8_t* dest;
|
||||
if (ReserveRawBytes(utf8_length).To(&dest)) {
|
||||
api_string->WriteUtf8(reinterpret_cast<char*>(dest), utf8_length, nullptr,
|
||||
v8::String::NO_NULL_TERMINATION);
|
||||
}
|
||||
WriteVarint(static_cast<uint32_t>(regexp->GetFlags()));
|
||||
}
|
||||
|
||||
@ -693,7 +715,7 @@ Maybe<bool> ValueSerializer::WriteJSMap(Handle<JSMap> map) {
|
||||
}
|
||||
WriteTag(SerializationTag::kEndJSMap);
|
||||
WriteVarint<uint32_t>(length);
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) {
|
||||
@ -723,7 +745,7 @@ Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) {
|
||||
}
|
||||
WriteTag(SerializationTag::kEndJSSet);
|
||||
WriteVarint<uint32_t>(length);
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteJSArrayBuffer(
|
||||
@ -741,14 +763,14 @@ Maybe<bool> 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,7 +784,7 @@ Maybe<bool> ValueSerializer::WriteJSArrayBuffer(
|
||||
WriteTag(SerializationTag::kArrayBuffer);
|
||||
WriteVarint<uint32_t>(byte_length);
|
||||
WriteRawBytes(array_buffer->backing_store(), byte_length);
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) {
|
||||
@ -784,7 +806,7 @@ Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) {
|
||||
WriteVarint(static_cast<uint8_t>(tag));
|
||||
WriteVarint(NumberToUint32(view->byte_offset()));
|
||||
WriteVarint(NumberToUint32(view->byte_length()));
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteWasmModule(Handle<JSObject> object) {
|
||||
@ -797,8 +819,10 @@ Maybe<bool> ValueSerializer::WriteWasmModule(Handle<JSObject> object) {
|
||||
Handle<String> wire_bytes(compiled_part->module_bytes(), isolate_);
|
||||
int wire_bytes_length = wire_bytes->length();
|
||||
WriteVarint<uint32_t>(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<ScriptData> script_data =
|
||||
WasmCompiledModuleSerializer::SerializeWasmModule(isolate_,
|
||||
@ -807,7 +831,7 @@ Maybe<bool> ValueSerializer::WriteWasmModule(Handle<JSObject> object) {
|
||||
WriteVarint<uint32_t>(script_data_length);
|
||||
WriteRawBytes(script_data->data(), script_data_length);
|
||||
|
||||
return Just(true);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteHostObject(Handle<JSObject> object) {
|
||||
@ -858,6 +882,14 @@ void ValueSerializer::ThrowDataCloneError(
|
||||
isolate_->factory()->empty_string());
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::ThrowIfOutOfMemory() {
|
||||
if (out_of_memory_) {
|
||||
ThrowDataCloneError(MessageTemplate::kDataCloneErrorOutOfMemory);
|
||||
return Nothing<bool>();
|
||||
}
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
void ValueSerializer::ThrowDataCloneError(
|
||||
MessageTemplate::Template template_index, Handle<Object> arg0) {
|
||||
Handle<String> message =
|
||||
|
@ -86,7 +86,7 @@ class ValueSerializer {
|
||||
|
||||
private:
|
||||
// Managing allocations of the internal buffer.
|
||||
void ExpandBuffer(size_t required_capacity);
|
||||
Maybe<bool> ExpandBuffer(size_t required_capacity);
|
||||
|
||||
// Writing the wire format.
|
||||
void WriteTag(SerializationTag tag);
|
||||
@ -96,7 +96,7 @@ class ValueSerializer {
|
||||
void WriteZigZag(T value);
|
||||
void WriteOneByteString(Vector<const uint8_t> chars);
|
||||
void WriteTwoByteString(Vector<const uc16> chars);
|
||||
uint8_t* ReserveRawBytes(size_t bytes);
|
||||
Maybe<uint8_t*> ReserveRawBytes(size_t bytes);
|
||||
|
||||
// Writing V8 objects of various kinds.
|
||||
void WriteOddball(Oddball* oddball);
|
||||
@ -134,11 +134,14 @@ class ValueSerializer {
|
||||
V8_NOINLINE void ThrowDataCloneError(MessageTemplate::Template template_index,
|
||||
Handle<Object> arg0);
|
||||
|
||||
Maybe<bool> ThrowIfOutOfMemory();
|
||||
|
||||
Isolate* const isolate_;
|
||||
v8::ValueSerializer::Delegate* const delegate_;
|
||||
uint8_t* buffer_ = nullptr;
|
||||
size_t buffer_size_ = 0;
|
||||
size_t buffer_capacity_ = 0;
|
||||
bool out_of_memory_ = false;
|
||||
Zone zone_;
|
||||
|
||||
// To avoid extra lookups in the identity map, ID+1 is actually stored in the
|
||||
|
@ -28,20 +28,19 @@
|
||||
// Flags: --harmony-sharedarraybuffer
|
||||
|
||||
if (this.Worker) {
|
||||
|
||||
(function TestTransfer() {
|
||||
var workerScript =
|
||||
`onmessage = function(m) {
|
||||
var sab = m;
|
||||
var ta = new Uint32Array(sab);
|
||||
if (sab.byteLength !== 16) {
|
||||
throw new Error('SharedArrayBuffer transfer byteLength');
|
||||
}
|
||||
for (var i = 0; i < 4; ++i) {
|
||||
if (ta[i] !== i) {
|
||||
throw new Error('SharedArrayBuffer transfer value ' + i);
|
||||
}
|
||||
}
|
||||
var sab = m;
|
||||
var ta = new Uint32Array(sab);
|
||||
if (sab.byteLength !== 16) {
|
||||
throw new Error('SharedArrayBuffer transfer byteLength');
|
||||
}
|
||||
for (var i = 0; i < 4; ++i) {
|
||||
if (ta[i] !== i) {
|
||||
throw new Error('SharedArrayBuffer transfer value ' + i);
|
||||
}
|
||||
}
|
||||
// Atomically update ta[0]
|
||||
Atomics.store(ta, 0, 100);
|
||||
};`;
|
||||
@ -55,7 +54,7 @@ if (this.Worker) {
|
||||
}
|
||||
|
||||
// Transfer SharedArrayBuffer
|
||||
w.postMessage(sab, [sab]);
|
||||
w.postMessage(sab);
|
||||
assertEquals(16, sab.byteLength); // ArrayBuffer should not be neutered.
|
||||
|
||||
// Spinwait for the worker to update ta[0]
|
||||
@ -86,7 +85,7 @@ if (this.Worker) {
|
||||
var workers = [];
|
||||
for (id = 0; id < 4; ++id) {
|
||||
workers[id] = new Worker(workerScript);
|
||||
workers[id].postMessage({sab: sab, id: id}, [sab]);
|
||||
workers[id].postMessage({sab: sab, id: id});
|
||||
}
|
||||
|
||||
// Spinwait for each worker to update ta[id]
|
||||
|
@ -61,8 +61,8 @@ var workerScript =
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if (JSON.stringify(m) !== \"{'a':1,'b':2.5,'c':'three'}\")
|
||||
throw new Error('Object');
|
||||
if (JSON.stringify(m) !== '{"a":1,"b":2.5,"c":"three"}')
|
||||
throw new Error('Object' + JSON.stringify(m));
|
||||
break;
|
||||
case 8:
|
||||
var ab = m;
|
||||
@ -88,7 +88,6 @@ var workerScript =
|
||||
}
|
||||
};`;
|
||||
|
||||
|
||||
if (this.Worker) {
|
||||
function createArrayBuffer(byteLength) {
|
||||
var ab = new ArrayBuffer(byteLength);
|
||||
@ -111,6 +110,17 @@ if (this.Worker) {
|
||||
w.postMessage([4, true, "bye"]);
|
||||
w.postMessage({a: 1, b: 2.5, c: "three"});
|
||||
|
||||
// Test bad get in transfer list.
|
||||
var transferList = [undefined];
|
||||
Object.defineProperty(transferList, '0', {
|
||||
get: function() {
|
||||
throw 'unexpected!';
|
||||
}
|
||||
});
|
||||
assertThrows(function() {
|
||||
w.postMessage([], transferList);
|
||||
});
|
||||
|
||||
// Clone ArrayBuffer
|
||||
var ab1 = createArrayBuffer(16);
|
||||
w.postMessage(ab1);
|
||||
|
@ -120,7 +120,7 @@ if (this.Worker) {
|
||||
};`;
|
||||
|
||||
var worker = new Worker(workerScript);
|
||||
worker.postMessage({sab: sab, offset: offset}, [sab]);
|
||||
worker.postMessage({sab: sab, offset: offset});
|
||||
|
||||
// Spin until the worker is waiting on the futex.
|
||||
while (%AtomicsNumWaitersForTesting(i32a, 0) != 1) {}
|
||||
@ -132,7 +132,7 @@ if (this.Worker) {
|
||||
var worker2 = new Worker(workerScript);
|
||||
var offset = 8;
|
||||
var i32a2 = new Int32Array(sab, offset);
|
||||
worker2.postMessage({sab: sab, offset: offset}, [sab]);
|
||||
worker2.postMessage({sab: sab, offset: offset});
|
||||
|
||||
// Spin until the worker is waiting on the futex.
|
||||
while (%AtomicsNumWaitersForTesting(i32a2, 0) != 1) {}
|
||||
@ -144,7 +144,7 @@ if (this.Worker) {
|
||||
// the real address is the same.
|
||||
var worker3 = new Worker(workerScript);
|
||||
i32a2 = new Int32Array(sab, 4);
|
||||
worker3.postMessage({sab: sab, offset: 8}, [sab]);
|
||||
worker3.postMessage({sab: sab, offset: 8});
|
||||
|
||||
// Spin until the worker is waiting on the futex.
|
||||
while (%AtomicsNumWaitersForTesting(i32a2, 1) != 1) {}
|
||||
@ -190,7 +190,7 @@ if (this.Worker) {
|
||||
var workers = [];
|
||||
for (id = 0; id < 4; id++) {
|
||||
workers[id] = new Worker(workerScript);
|
||||
workers[id].postMessage({sab: sab, id: id}, [sab]);
|
||||
workers[id].postMessage({sab: sab, id: id});
|
||||
}
|
||||
|
||||
// Spin until all workers are waiting on the futex.
|
||||
|
@ -4,10 +4,15 @@
|
||||
|
||||
if (this.Worker) {
|
||||
var __v_7 = new Worker('onmessage = function() {};');
|
||||
var e;
|
||||
try {
|
||||
var ab = new ArrayBuffer(2147483648);
|
||||
// If creating the ArrayBuffer succeeded, then postMessage should fail.
|
||||
assertThrows(function() { __v_7.postMessage(ab); });
|
||||
try {
|
||||
__v_7.postMessage(ab);
|
||||
} catch (e) {
|
||||
// postMessage failed, should be a DataCloneError message.
|
||||
assertContains('cloned', e.message);
|
||||
}
|
||||
} catch (e) {
|
||||
// Creating the ArrayBuffer failed.
|
||||
assertInstanceof(e, RangeError);
|
||||
|
Loading…
Reference in New Issue
Block a user