Commit 96635558 authored by binji's avatar binji Committed by Commit bot

[d8] Use ValueSerializer for postMessage (instead of ad-hoc serializer)

Review-Url: https://codereview.chromium.org/2643723010
Cr-Commit-Position: refs/heads/master@{#42749}
parent 9515f7ed
......@@ -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);
......
......@@ -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::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();
}
......@@ -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();
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);
}
......@@ -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();
}
......
......@@ -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_;
}
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;
const std::vector<SharedArrayBuffer::Contents>&
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<uint8_t, DataDeleter> data_;
size_t size_;
std::vector<ArrayBuffer::Contents> array_buffer_contents_;
std::vector<SharedArrayBuffer::Contents> shared_array_buffer_contents_;
private:
i::List<uint8_t> data_;
i::List<ArrayBuffer::Contents> array_buffer_contents_;
i::List<SharedArrayBuffer::Contents> 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<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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment