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 { ...@@ -90,6 +90,8 @@ enum class SerializationTag : uint8_t {
kBeginJSSet = '\'', kBeginJSSet = '\'',
// End of a JS set. length:uint32_t. // End of a JS set. length:uint32_t.
kEndJSSet = ',', kEndJSSet = ',',
// Array buffer. byteLength:uint32_t, then raw data.
kArrayBuffer = 'B',
}; };
ValueSerializer::ValueSerializer(Isolate* isolate) ValueSerializer::ValueSerializer(Isolate* isolate)
...@@ -158,6 +160,11 @@ void ValueSerializer::WriteTwoByteString(Vector<const uc16> chars) { ...@@ -158,6 +160,11 @@ void ValueSerializer::WriteTwoByteString(Vector<const uc16> chars) {
reinterpret_cast<const uint8_t*>(chars.end())); 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) { uint8_t* ValueSerializer::ReserveRawBytes(size_t bytes) {
if (!bytes) return nullptr; if (!bytes) return nullptr;
auto old_size = buffer_.size(); auto old_size = buffer_.size();
...@@ -301,6 +308,8 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) { ...@@ -301,6 +308,8 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
return WriteJSMap(Handle<JSMap>::cast(receiver)); return WriteJSMap(Handle<JSMap>::cast(receiver));
case JS_SET_TYPE: case JS_SET_TYPE:
return WriteJSSet(Handle<JSSet>::cast(receiver)); return WriteJSSet(Handle<JSSet>::cast(receiver));
case JS_ARRAY_BUFFER_TYPE:
return WriteJSArrayBuffer(JSArrayBuffer::cast(*receiver));
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
...@@ -490,6 +499,18 @@ Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) { ...@@ -490,6 +499,18 @@ Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) {
return Just(true); 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( Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties(
Handle<JSObject> object, Handle<FixedArray> keys) { Handle<JSObject> object, Handle<FixedArray> keys) {
uint32_t properties_written = 0; uint32_t properties_written = 0;
...@@ -683,6 +704,8 @@ MaybeHandle<Object> ValueDeserializer::ReadObject() { ...@@ -683,6 +704,8 @@ MaybeHandle<Object> ValueDeserializer::ReadObject() {
return ReadJSMap(); return ReadJSMap();
case SerializationTag::kBeginJSSet: case SerializationTag::kBeginJSSet:
return ReadJSSet(); return ReadJSSet();
case SerializationTag::kArrayBuffer:
return ReadJSArrayBuffer();
default: default:
return MaybeHandle<Object>(); return MaybeHandle<Object>();
} }
...@@ -951,6 +974,24 @@ MaybeHandle<JSSet> ValueDeserializer::ReadJSSet() { ...@@ -951,6 +974,24 @@ MaybeHandle<JSSet> ValueDeserializer::ReadJSSet() {
return scope.CloseAndEscape(set); 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( Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
Handle<JSObject> object, SerializationTag end_tag) { Handle<JSObject> object, SerializationTag end_tag) {
for (uint32_t num_properties = 0;; num_properties++) { for (uint32_t num_properties = 0;; num_properties++) {
......
...@@ -20,6 +20,7 @@ namespace internal { ...@@ -20,6 +20,7 @@ namespace internal {
class HeapNumber; class HeapNumber;
class Isolate; class Isolate;
class JSArrayBuffer;
class JSDate; class JSDate;
class JSMap; class JSMap;
class JSRegExp; class JSRegExp;
...@@ -68,6 +69,7 @@ class ValueSerializer { ...@@ -68,6 +69,7 @@ class ValueSerializer {
void WriteDouble(double value); void WriteDouble(double value);
void WriteOneByteString(Vector<const uint8_t> chars); void WriteOneByteString(Vector<const uint8_t> chars);
void WriteTwoByteString(Vector<const uc16> chars); void WriteTwoByteString(Vector<const uc16> chars);
void WriteRawBytes(const void* source, size_t length);
uint8_t* ReserveRawBytes(size_t bytes); uint8_t* ReserveRawBytes(size_t bytes);
// Writing V8 objects of various kinds. // Writing V8 objects of various kinds.
...@@ -83,6 +85,7 @@ class ValueSerializer { ...@@ -83,6 +85,7 @@ class ValueSerializer {
void WriteJSRegExp(JSRegExp* regexp); void WriteJSRegExp(JSRegExp* regexp);
Maybe<bool> WriteJSMap(Handle<JSMap> map) WARN_UNUSED_RESULT; Maybe<bool> WriteJSMap(Handle<JSMap> map) WARN_UNUSED_RESULT;
Maybe<bool> WriteJSSet(Handle<JSSet> 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 * Reads the specified keys from the object and writes key-value pairs to the
...@@ -158,6 +161,7 @@ class ValueDeserializer { ...@@ -158,6 +161,7 @@ class ValueDeserializer {
MaybeHandle<JSRegExp> ReadJSRegExp() WARN_UNUSED_RESULT; MaybeHandle<JSRegExp> ReadJSRegExp() WARN_UNUSED_RESULT;
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;
/* /*
* Reads key-value pairs into the object until the specified end tag is * Reads key-value pairs into the object until the specified end tag is
......
...@@ -1559,5 +1559,58 @@ TEST_F(ValueSerializerTest, RoundTripSetWithTrickyGetters) { ...@@ -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
} // 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