Commit e2361954 authored by jbroman's avatar jbroman Committed by Commit bot

Blink-compatible serialization of SharedArrayBuffer.

BUG=chromium:148757

Review-Url: https://codereview.chromium.org/2290753002
Cr-Commit-Position: refs/heads/master@{#39017}
parent bcf4c66b
...@@ -1698,6 +1698,12 @@ class V8_EXPORT ValueSerializer { ...@@ -1698,6 +1698,12 @@ class V8_EXPORT ValueSerializer {
void TransferArrayBuffer(uint32_t transfer_id, void TransferArrayBuffer(uint32_t transfer_id,
Local<ArrayBuffer> array_buffer); Local<ArrayBuffer> array_buffer);
/*
* Similar to TransferArrayBuffer, but for SharedArrayBuffer.
*/
void TransferSharedArrayBuffer(uint32_t transfer_id,
Local<SharedArrayBuffer> shared_array_buffer);
private: private:
ValueSerializer(const ValueSerializer&) = delete; ValueSerializer(const ValueSerializer&) = delete;
void operator=(const ValueSerializer&) = delete; void operator=(const ValueSerializer&) = delete;
...@@ -1737,6 +1743,13 @@ class V8_EXPORT ValueDeserializer { ...@@ -1737,6 +1743,13 @@ class V8_EXPORT ValueDeserializer {
void TransferArrayBuffer(uint32_t transfer_id, void TransferArrayBuffer(uint32_t transfer_id,
Local<ArrayBuffer> array_buffer); Local<ArrayBuffer> array_buffer);
/*
* Similar to TransferArrayBuffer, but for SharedArrayBuffer.
* transfer_id exists in the same namespace as unshared ArrayBuffer objects.
*/
void TransferSharedArrayBuffer(uint32_t transfer_id,
Local<SharedArrayBuffer> shared_array_buffer);
/* /*
* Must be called before ReadHeader to enable support for reading the legacy * Must be called before ReadHeader to enable support for reading the legacy
* wire format (i.e., which predates this being shipped). * wire format (i.e., which predates this being shipped).
......
...@@ -2871,6 +2871,12 @@ void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id, ...@@ -2871,6 +2871,12 @@ void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id,
Utils::OpenHandle(*array_buffer)); Utils::OpenHandle(*array_buffer));
} }
void ValueSerializer::TransferSharedArrayBuffer(
uint32_t transfer_id, Local<SharedArrayBuffer> shared_array_buffer) {
private_->serializer.TransferArrayBuffer(
transfer_id, Utils::OpenHandle(*shared_array_buffer));
}
struct ValueDeserializer::PrivateData { struct ValueDeserializer::PrivateData {
PrivateData(i::Isolate* i, i::Vector<const uint8_t> data) PrivateData(i::Isolate* i, i::Vector<const uint8_t> data)
: isolate(i), deserializer(i, data) {} : isolate(i), deserializer(i, data) {}
...@@ -2941,6 +2947,12 @@ void ValueDeserializer::TransferArrayBuffer(uint32_t transfer_id, ...@@ -2941,6 +2947,12 @@ void ValueDeserializer::TransferArrayBuffer(uint32_t transfer_id,
Utils::OpenHandle(*array_buffer)); Utils::OpenHandle(*array_buffer));
} }
void ValueDeserializer::TransferSharedArrayBuffer(
uint32_t transfer_id, Local<SharedArrayBuffer> shared_array_buffer) {
private_->deserializer.TransferArrayBuffer(
transfer_id, Utils::OpenHandle(*shared_array_buffer));
}
// --- D a t a --- // --- D a t a ---
bool Value::FullIsUndefined() const { bool Value::FullIsUndefined() const {
......
...@@ -103,6 +103,8 @@ enum class SerializationTag : uint8_t { ...@@ -103,6 +103,8 @@ enum class SerializationTag : uint8_t {
// ObjectReference to one) serialized just before it. This is a quirk arising // ObjectReference to one) serialized just before it. This is a quirk arising
// from the previous stack-based implementation. // from the previous stack-based implementation.
kArrayBufferView = 'V', kArrayBufferView = 'V',
// Shared array buffer (transferred). transferID:uint32_t
kSharedArrayBufferTransfer = 'u',
}; };
namespace { namespace {
...@@ -557,12 +559,15 @@ Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) { ...@@ -557,12 +559,15 @@ Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) {
Maybe<bool> ValueSerializer::WriteJSArrayBuffer(JSArrayBuffer* array_buffer) { Maybe<bool> ValueSerializer::WriteJSArrayBuffer(JSArrayBuffer* array_buffer) {
uint32_t* transfer_entry = array_buffer_transfer_map_.Find(array_buffer); uint32_t* transfer_entry = array_buffer_transfer_map_.Find(array_buffer);
if (transfer_entry) { if (transfer_entry) {
DCHECK(array_buffer->was_neutered()); DCHECK(array_buffer->was_neutered() || array_buffer->is_shared());
WriteTag(SerializationTag::kArrayBufferTransfer); WriteTag(array_buffer->is_shared()
? SerializationTag::kSharedArrayBufferTransfer
: SerializationTag::kArrayBufferTransfer);
WriteVarint(*transfer_entry); WriteVarint(*transfer_entry);
return Just(true); return Just(true);
} }
if (array_buffer->is_shared()) return Nothing<bool>();
if (array_buffer->was_neutered()) return Nothing<bool>(); if (array_buffer->was_neutered()) return Nothing<bool>();
double byte_length = array_buffer->byte_length()->Number(); double byte_length = array_buffer->byte_length()->Number();
if (byte_length > std::numeric_limits<uint32_t>::max()) { if (byte_length > std::numeric_limits<uint32_t>::max()) {
...@@ -832,8 +837,14 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() { ...@@ -832,8 +837,14 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
return ReadJSSet(); return ReadJSSet();
case SerializationTag::kArrayBuffer: case SerializationTag::kArrayBuffer:
return ReadJSArrayBuffer(); return ReadJSArrayBuffer();
case SerializationTag::kArrayBufferTransfer: case SerializationTag::kArrayBufferTransfer: {
return ReadTransferredJSArrayBuffer(); const bool is_shared = false;
return ReadTransferredJSArrayBuffer(is_shared);
}
case SerializationTag::kSharedArrayBufferTransfer: {
const bool is_shared = true;
return ReadTransferredJSArrayBuffer(is_shared);
}
default: default:
return MaybeHandle<Object>(); return MaybeHandle<Object>();
} }
...@@ -1120,7 +1131,8 @@ MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadJSArrayBuffer() { ...@@ -1120,7 +1131,8 @@ MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadJSArrayBuffer() {
return array_buffer; return array_buffer;
} }
MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer() { MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer(
bool is_shared) {
uint32_t id = next_id_++; uint32_t id = next_id_++;
uint32_t transfer_id; uint32_t transfer_id;
Handle<SeededNumberDictionary> transfer_map; Handle<SeededNumberDictionary> transfer_map;
...@@ -1134,6 +1146,7 @@ MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer() { ...@@ -1134,6 +1146,7 @@ MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer() {
} }
Handle<JSArrayBuffer> array_buffer( Handle<JSArrayBuffer> array_buffer(
JSArrayBuffer::cast(transfer_map->ValueAt(index)), isolate_); JSArrayBuffer::cast(transfer_map->ValueAt(index)), isolate_);
DCHECK_EQ(is_shared, array_buffer->is_shared());
AddObjectWithID(id, array_buffer); AddObjectWithID(id, array_buffer);
return array_buffer; return array_buffer;
} }
......
...@@ -193,7 +193,8 @@ class ValueDeserializer { ...@@ -193,7 +193,8 @@ class ValueDeserializer {
MaybeHandle<JSMap> ReadJSMap() WARN_UNUSED_RESULT; MaybeHandle<JSMap> ReadJSMap() WARN_UNUSED_RESULT;
MaybeHandle<JSSet> ReadJSSet() WARN_UNUSED_RESULT; MaybeHandle<JSSet> ReadJSSet() WARN_UNUSED_RESULT;
MaybeHandle<JSArrayBuffer> ReadJSArrayBuffer() WARN_UNUSED_RESULT; MaybeHandle<JSArrayBuffer> ReadJSArrayBuffer() WARN_UNUSED_RESULT;
MaybeHandle<JSArrayBuffer> ReadTransferredJSArrayBuffer() WARN_UNUSED_RESULT; MaybeHandle<JSArrayBuffer> ReadTransferredJSArrayBuffer(bool is_shared)
WARN_UNUSED_RESULT;
MaybeHandle<JSArrayBufferView> ReadJSArrayBufferView( MaybeHandle<JSArrayBufferView> ReadJSArrayBufferView(
Handle<JSArrayBuffer> buffer) WARN_UNUSED_RESULT; Handle<JSArrayBuffer> buffer) WARN_UNUSED_RESULT;
......
...@@ -1903,5 +1903,97 @@ TEST_F(ValueSerializerTest, DecodeInvalidDataView) { ...@@ -1903,5 +1903,97 @@ TEST_F(ValueSerializerTest, DecodeInvalidDataView) {
{0xff, 0x09, 0x42, 0x02, 0x00, 0x00, 0x56, 0x3f, 0x01, 0x03}); {0xff, 0x09, 0x42, 0x02, 0x00, 0x00, 0x56, 0x3f, 0x01, 0x03});
} }
class ValueSerializerTestWithSharedArrayBufferTransfer
: public ValueSerializerTest {
protected:
static const size_t kTestByteLength = 4;
ValueSerializerTestWithSharedArrayBufferTransfer() {
const uint8_t data[kTestByteLength] = {0x00, 0x01, 0x80, 0xff};
memcpy(data_, data, kTestByteLength);
{
Context::Scope scope(serialization_context());
input_buffer_ =
SharedArrayBuffer::New(isolate(), &data_, kTestByteLength);
}
{
Context::Scope scope(deserialization_context());
output_buffer_ =
SharedArrayBuffer::New(isolate(), &data_, kTestByteLength);
}
}
const Local<SharedArrayBuffer>& input_buffer() { return input_buffer_; }
const Local<SharedArrayBuffer>& output_buffer() { return output_buffer_; }
void BeforeEncode(ValueSerializer* serializer) override {
serializer->TransferSharedArrayBuffer(0, input_buffer_);
}
void BeforeDecode(ValueDeserializer* deserializer) override {
deserializer->TransferSharedArrayBuffer(0, output_buffer_);
}
static void SetUpTestCase() {
flag_was_enabled_ = i::FLAG_harmony_sharedarraybuffer;
i::FLAG_harmony_sharedarraybuffer = true;
ValueSerializerTest::SetUpTestCase();
}
static void TearDownTestCase() {
ValueSerializerTest::TearDownTestCase();
i::FLAG_harmony_sharedarraybuffer = flag_was_enabled_;
flag_was_enabled_ = false;
}
private:
static bool flag_was_enabled_;
uint8_t data_[kTestByteLength];
Local<SharedArrayBuffer> input_buffer_;
Local<SharedArrayBuffer> output_buffer_;
};
bool ValueSerializerTestWithSharedArrayBufferTransfer::flag_was_enabled_ =
false;
TEST_F(ValueSerializerTestWithSharedArrayBufferTransfer,
RoundTripSharedArrayBufferTransfer) {
RoundTripTest([this]() { return input_buffer(); },
[this](Local<Value> value) {
ASSERT_TRUE(value->IsSharedArrayBuffer());
EXPECT_EQ(output_buffer(), value);
EXPECT_TRUE(EvaluateScriptForResultBool(
"new Uint8Array(result).toString() === '0,1,128,255'"));
});
RoundTripTest(
[this]() {
Local<Object> object = Object::New(isolate());
EXPECT_TRUE(object
->CreateDataProperty(serialization_context(),
StringFromUtf8("a"),
input_buffer())
.FromMaybe(false));
EXPECT_TRUE(object
->CreateDataProperty(serialization_context(),
StringFromUtf8("b"),
input_buffer())
.FromMaybe(false));
return object;
},
[this](Local<Value> value) {
EXPECT_TRUE(EvaluateScriptForResultBool(
"result.a instanceof SharedArrayBuffer"));
EXPECT_TRUE(EvaluateScriptForResultBool("result.a === result.b"));
EXPECT_TRUE(EvaluateScriptForResultBool(
"new Uint8Array(result.a).toString() === '0,1,128,255'"));
});
}
TEST_F(ValueSerializerTestWithSharedArrayBufferTransfer,
SharedArrayBufferMustBeTransferred) {
// A SharedArrayBuffer which was not marked for transfer should fail encoding.
InvalidEncodeTest("new SharedArrayBuffer(32)");
}
} // namespace } // namespace
} // namespace v8 } // namespace v8
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