Blink-compatible serialization of SharedArrayBuffer.
BUG=chromium:148757 Review-Url: https://codereview.chromium.org/2290753002 Cr-Commit-Position: refs/heads/master@{#39017}
This commit is contained in:
parent
bcf4c66b66
commit
e2361954e5
13
include/v8.h
13
include/v8.h
@ -1698,6 +1698,12 @@ class V8_EXPORT ValueSerializer {
|
||||
void TransferArrayBuffer(uint32_t transfer_id,
|
||||
Local<ArrayBuffer> array_buffer);
|
||||
|
||||
/*
|
||||
* Similar to TransferArrayBuffer, but for SharedArrayBuffer.
|
||||
*/
|
||||
void TransferSharedArrayBuffer(uint32_t transfer_id,
|
||||
Local<SharedArrayBuffer> shared_array_buffer);
|
||||
|
||||
private:
|
||||
ValueSerializer(const ValueSerializer&) = delete;
|
||||
void operator=(const ValueSerializer&) = delete;
|
||||
@ -1737,6 +1743,13 @@ class V8_EXPORT ValueDeserializer {
|
||||
void TransferArrayBuffer(uint32_t transfer_id,
|
||||
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
|
||||
* wire format (i.e., which predates this being shipped).
|
||||
|
12
src/api.cc
12
src/api.cc
@ -2871,6 +2871,12 @@ void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id,
|
||||
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 {
|
||||
PrivateData(i::Isolate* i, i::Vector<const uint8_t> data)
|
||||
: isolate(i), deserializer(i, data) {}
|
||||
@ -2941,6 +2947,12 @@ void ValueDeserializer::TransferArrayBuffer(uint32_t transfer_id,
|
||||
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 ---
|
||||
|
||||
bool Value::FullIsUndefined() const {
|
||||
|
@ -103,6 +103,8 @@ enum class SerializationTag : uint8_t {
|
||||
// ObjectReference to one) serialized just before it. This is a quirk arising
|
||||
// from the previous stack-based implementation.
|
||||
kArrayBufferView = 'V',
|
||||
// Shared array buffer (transferred). transferID:uint32_t
|
||||
kSharedArrayBufferTransfer = 'u',
|
||||
};
|
||||
|
||||
namespace {
|
||||
@ -557,12 +559,15 @@ Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) {
|
||||
Maybe<bool> ValueSerializer::WriteJSArrayBuffer(JSArrayBuffer* array_buffer) {
|
||||
uint32_t* transfer_entry = array_buffer_transfer_map_.Find(array_buffer);
|
||||
if (transfer_entry) {
|
||||
DCHECK(array_buffer->was_neutered());
|
||||
WriteTag(SerializationTag::kArrayBufferTransfer);
|
||||
DCHECK(array_buffer->was_neutered() || array_buffer->is_shared());
|
||||
WriteTag(array_buffer->is_shared()
|
||||
? SerializationTag::kSharedArrayBufferTransfer
|
||||
: SerializationTag::kArrayBufferTransfer);
|
||||
WriteVarint(*transfer_entry);
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
if (array_buffer->is_shared()) return Nothing<bool>();
|
||||
if (array_buffer->was_neutered()) return Nothing<bool>();
|
||||
double byte_length = array_buffer->byte_length()->Number();
|
||||
if (byte_length > std::numeric_limits<uint32_t>::max()) {
|
||||
@ -832,8 +837,14 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
|
||||
return ReadJSSet();
|
||||
case SerializationTag::kArrayBuffer:
|
||||
return ReadJSArrayBuffer();
|
||||
case SerializationTag::kArrayBufferTransfer:
|
||||
return ReadTransferredJSArrayBuffer();
|
||||
case SerializationTag::kArrayBufferTransfer: {
|
||||
const bool is_shared = false;
|
||||
return ReadTransferredJSArrayBuffer(is_shared);
|
||||
}
|
||||
case SerializationTag::kSharedArrayBufferTransfer: {
|
||||
const bool is_shared = true;
|
||||
return ReadTransferredJSArrayBuffer(is_shared);
|
||||
}
|
||||
default:
|
||||
return MaybeHandle<Object>();
|
||||
}
|
||||
@ -1120,7 +1131,8 @@ MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadJSArrayBuffer() {
|
||||
return array_buffer;
|
||||
}
|
||||
|
||||
MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer() {
|
||||
MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer(
|
||||
bool is_shared) {
|
||||
uint32_t id = next_id_++;
|
||||
uint32_t transfer_id;
|
||||
Handle<SeededNumberDictionary> transfer_map;
|
||||
@ -1134,6 +1146,7 @@ MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer() {
|
||||
}
|
||||
Handle<JSArrayBuffer> array_buffer(
|
||||
JSArrayBuffer::cast(transfer_map->ValueAt(index)), isolate_);
|
||||
DCHECK_EQ(is_shared, array_buffer->is_shared());
|
||||
AddObjectWithID(id, array_buffer);
|
||||
return array_buffer;
|
||||
}
|
||||
|
@ -193,7 +193,8 @@ class ValueDeserializer {
|
||||
MaybeHandle<JSMap> ReadJSMap() WARN_UNUSED_RESULT;
|
||||
MaybeHandle<JSSet> ReadJSSet() 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(
|
||||
Handle<JSArrayBuffer> buffer) WARN_UNUSED_RESULT;
|
||||
|
||||
|
@ -1903,5 +1903,97 @@ TEST_F(ValueSerializerTest, DecodeInvalidDataView) {
|
||||
{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 v8
|
||||
|
Loading…
Reference in New Issue
Block a user