Commit 2646749c authored by jbroman's avatar jbroman Committed by Commit bot

Blink-compatible serialization of ArrayBuffer.

Transferral is not included in this CL, nor is SharedArrayBuffer.

BUG=chromium:148757

Review-Url: https://codereview.chromium.org/2264403004
Cr-Commit-Position: refs/heads/master@{#38913}
parent 3866975f
......@@ -90,6 +90,8 @@ enum class SerializationTag : uint8_t {
kBeginJSSet = '\'',
// End of a JS set. length:uint32_t.
kEndJSSet = ',',
// Array buffer. byteLength:uint32_t, then raw data.
kArrayBuffer = 'B',
};
ValueSerializer::ValueSerializer(Isolate* isolate)
......@@ -158,6 +160,11 @@ void ValueSerializer::WriteTwoByteString(Vector<const uc16> chars) {
reinterpret_cast<const uint8_t*>(chars.end()));
}
void ValueSerializer::WriteRawBytes(const void* source, size_t length) {
const uint8_t* begin = reinterpret_cast<const uint8_t*>(source);
buffer_.insert(buffer_.end(), begin, begin + length);
}
uint8_t* ValueSerializer::ReserveRawBytes(size_t bytes) {
if (!bytes) return nullptr;
auto old_size = buffer_.size();
......@@ -301,6 +308,8 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
return WriteJSMap(Handle<JSMap>::cast(receiver));
case JS_SET_TYPE:
return WriteJSSet(Handle<JSSet>::cast(receiver));
case JS_ARRAY_BUFFER_TYPE:
return WriteJSArrayBuffer(JSArrayBuffer::cast(*receiver));
default:
UNIMPLEMENTED();
break;
......@@ -490,6 +499,18 @@ Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) {
return Just(true);
}
Maybe<bool> ValueSerializer::WriteJSArrayBuffer(JSArrayBuffer* array_buffer) {
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()) {
return Nothing<bool>();
}
WriteTag(SerializationTag::kArrayBuffer);
WriteVarint<uint32_t>(byte_length);
WriteRawBytes(array_buffer->backing_store(), byte_length);
return Just(true);
}
Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties(
Handle<JSObject> object, Handle<FixedArray> keys) {
uint32_t properties_written = 0;
......@@ -683,6 +704,8 @@ MaybeHandle<Object> ValueDeserializer::ReadObject() {
return ReadJSMap();
case SerializationTag::kBeginJSSet:
return ReadJSSet();
case SerializationTag::kArrayBuffer:
return ReadJSArrayBuffer();
default:
return MaybeHandle<Object>();
}
......@@ -951,6 +974,24 @@ MaybeHandle<JSSet> ValueDeserializer::ReadJSSet() {
return scope.CloseAndEscape(set);
}
MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadJSArrayBuffer() {
uint32_t id = next_id_++;
uint32_t byte_length;
Vector<const uint8_t> bytes;
if (!ReadVarint<uint32_t>().To(&byte_length) ||
byte_length > static_cast<size_t>(end_ - position_)) {
return MaybeHandle<JSArrayBuffer>();
}
const bool should_initialize = false;
Handle<JSArrayBuffer> array_buffer = isolate_->factory()->NewJSArrayBuffer();
JSArrayBuffer::SetupAllocatingData(array_buffer, isolate_, byte_length,
should_initialize);
memcpy(array_buffer->backing_store(), position_, byte_length);
position_ += byte_length;
AddObjectWithID(id, array_buffer);
return array_buffer;
}
Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
Handle<JSObject> object, SerializationTag end_tag) {
for (uint32_t num_properties = 0;; num_properties++) {
......
......@@ -20,6 +20,7 @@ namespace internal {
class HeapNumber;
class Isolate;
class JSArrayBuffer;
class JSDate;
class JSMap;
class JSRegExp;
......@@ -68,6 +69,7 @@ class ValueSerializer {
void WriteDouble(double value);
void WriteOneByteString(Vector<const uint8_t> chars);
void WriteTwoByteString(Vector<const uc16> chars);
void WriteRawBytes(const void* source, size_t length);
uint8_t* ReserveRawBytes(size_t bytes);
// Writing V8 objects of various kinds.
......@@ -83,6 +85,7 @@ class ValueSerializer {
void WriteJSRegExp(JSRegExp* regexp);
Maybe<bool> WriteJSMap(Handle<JSMap> map) WARN_UNUSED_RESULT;
Maybe<bool> WriteJSSet(Handle<JSSet> map) WARN_UNUSED_RESULT;
Maybe<bool> WriteJSArrayBuffer(JSArrayBuffer* array_buffer);
/*
* Reads the specified keys from the object and writes key-value pairs to the
......@@ -158,6 +161,7 @@ class ValueDeserializer {
MaybeHandle<JSRegExp> ReadJSRegExp() WARN_UNUSED_RESULT;
MaybeHandle<JSMap> ReadJSMap() WARN_UNUSED_RESULT;
MaybeHandle<JSSet> ReadJSSet() WARN_UNUSED_RESULT;
MaybeHandle<JSArrayBuffer> ReadJSArrayBuffer() WARN_UNUSED_RESULT;
/*
* Reads key-value pairs into the object until the specified end tag is
......
......@@ -1559,5 +1559,58 @@ TEST_F(ValueSerializerTest, RoundTripSetWithTrickyGetters) {
});
}
TEST_F(ValueSerializerTest, RoundTripArrayBuffer) {
RoundTripTest("new ArrayBuffer()", [this](Local<Value> value) {
ASSERT_TRUE(value->IsArrayBuffer());
EXPECT_EQ(0u, ArrayBuffer::Cast(*value)->ByteLength());
EXPECT_TRUE(EvaluateScriptForResultBool(
"Object.getPrototypeOf(result) === ArrayBuffer.prototype"));
});
RoundTripTest("new Uint8Array([0, 128, 255]).buffer",
[this](Local<Value> value) {
ASSERT_TRUE(value->IsArrayBuffer());
EXPECT_EQ(3u, ArrayBuffer::Cast(*value)->ByteLength());
EXPECT_TRUE(EvaluateScriptForResultBool(
"new Uint8Array(result).toString() === '0,128,255'"));
});
RoundTripTest(
"({ a: new ArrayBuffer(), get b() { return this.a; }})",
[this](Local<Value> value) {
EXPECT_TRUE(
EvaluateScriptForResultBool("result.a instanceof ArrayBuffer"));
EXPECT_TRUE(EvaluateScriptForResultBool("result.a === result.b"));
});
}
TEST_F(ValueSerializerTest, DecodeArrayBuffer) {
DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x42, 0x00},
[this](Local<Value> value) {
ASSERT_TRUE(value->IsArrayBuffer());
EXPECT_EQ(0u, ArrayBuffer::Cast(*value)->ByteLength());
EXPECT_TRUE(EvaluateScriptForResultBool(
"Object.getPrototypeOf(result) === ArrayBuffer.prototype"));
});
DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x42, 0x03, 0x00, 0x80, 0xff, 0x00},
[this](Local<Value> value) {
ASSERT_TRUE(value->IsArrayBuffer());
EXPECT_EQ(3u, ArrayBuffer::Cast(*value)->ByteLength());
EXPECT_TRUE(EvaluateScriptForResultBool(
"new Uint8Array(result).toString() === '0,128,255'"));
});
DecodeTest(
{0xff, 0x09, 0x3f, 0x00, 0x6f, 0x3f, 0x01, 0x53, 0x01,
0x61, 0x3f, 0x01, 0x42, 0x00, 0x3f, 0x02, 0x53, 0x01,
0x62, 0x3f, 0x02, 0x5e, 0x01, 0x7b, 0x02, 0x00},
[this](Local<Value> value) {
EXPECT_TRUE(
EvaluateScriptForResultBool("result.a instanceof ArrayBuffer"));
EXPECT_TRUE(EvaluateScriptForResultBool("result.a === result.b"));
});
}
TEST_F(ValueSerializerTest, DecodeInvalidArrayBuffer) {
InvalidDecodeTest({0xff, 0x09, 0x42, 0xff, 0xff, 0x00});
}
} // namespace
} // 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