Index: src/value-serializer.cc |
diff --git a/src/value-serializer.cc b/src/value-serializer.cc |
index bbfb2c77e038e5fe2051ec406e0ae66a15b9f045..8130856ed9e142ab72c37e92fc2871def7fba875 100644 |
--- a/src/value-serializer.cc |
+++ b/src/value-serializer.cc |
@@ -7,6 +7,7 @@ |
#include <type_traits> |
#include "src/base/logging.h" |
+#include "src/conversions.h" |
#include "src/factory.h" |
#include "src/handles-inl.h" |
#include "src/isolate.h" |
@@ -94,8 +95,33 @@ enum class SerializationTag : uint8_t { |
kArrayBuffer = 'B', |
// Array buffer (transferred). transferID:uint32_t |
kArrayBufferTransfer = 't', |
+ // View into an array buffer. |
+ // subtag:ArrayBufferViewTag, byteOffset:uint32_t, byteLength:uint32_t |
+ // For typed arrays, byteOffset and byteLength must be divisible by the size |
+ // of the element. |
+ // Note: kArrayBufferView is special, and should have an ArrayBuffer (or an |
+ // ObjectReference to one) serialized just before it. This is a quirk arising |
+ // from the previous stack-based implementation. |
+ kArrayBufferView = 'V', |
}; |
+namespace { |
+ |
+enum class ArrayBufferViewTag : uint8_t { |
+ kInt8Array = 'b', |
+ kUint8Array = 'B', |
+ kUint8ClampedArray = 'C', |
+ kInt16Array = 'w', |
+ kUint16Array = 'W', |
+ kInt32Array = 'd', |
+ kUint32Array = 'D', |
+ kFloat32Array = 'f', |
+ kFloat64Array = 'F', |
+ kDataView = '?', |
+}; |
+ |
+} // namespace |
+ |
ValueSerializer::ValueSerializer(Isolate* isolate) |
: isolate_(isolate), |
zone_(isolate->allocator()), |
@@ -196,6 +222,23 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) { |
case MUTABLE_HEAP_NUMBER_TYPE: |
WriteHeapNumber(HeapNumber::cast(*object)); |
return Just(true); |
+ case JS_TYPED_ARRAY_TYPE: |
+ case JS_DATA_VIEW_TYPE: { |
+ // Despite being JSReceivers, these have their wrapped buffer serialized |
+ // first. That makes this logic a little quirky, because it needs to |
+ // happen before we assign object IDs. |
+ // TODO(jbroman): It may be possible to avoid materializing a typed |
+ // array's buffer here. |
+ Handle<JSArrayBufferView> view = Handle<JSArrayBufferView>::cast(object); |
+ if (!id_map_.Find(view)) { |
+ Handle<JSArrayBuffer> buffer( |
+ view->IsJSTypedArray() |
+ ? Handle<JSTypedArray>::cast(view)->GetBuffer() |
+ : handle(JSArrayBuffer::cast(view->buffer()), isolate_)); |
+ if (!WriteJSReceiver(buffer).FromMaybe(false)) return Nothing<bool>(); |
+ } |
+ return WriteJSReceiver(view); |
+ } |
default: |
if (object->IsString()) { |
WriteString(Handle<String>::cast(object)); |
@@ -319,6 +362,9 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) { |
return WriteJSSet(Handle<JSSet>::cast(receiver)); |
case JS_ARRAY_BUFFER_TYPE: |
return WriteJSArrayBuffer(JSArrayBuffer::cast(*receiver)); |
+ case JS_TYPED_ARRAY_TYPE: |
+ case JS_DATA_VIEW_TYPE: |
+ return WriteJSArrayBufferView(JSArrayBufferView::cast(*receiver)); |
default: |
UNIMPLEMENTED(); |
break; |
@@ -528,6 +574,28 @@ Maybe<bool> ValueSerializer::WriteJSArrayBuffer(JSArrayBuffer* array_buffer) { |
return Just(true); |
} |
+Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) { |
+ WriteTag(SerializationTag::kArrayBufferView); |
+ ArrayBufferViewTag tag = ArrayBufferViewTag::kInt8Array; |
+ if (view->IsJSTypedArray()) { |
+ switch (JSTypedArray::cast(view)->type()) { |
+#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
+ case kExternal##Type##Array: \ |
+ tag = ArrayBufferViewTag::k##Type##Array; \ |
+ break; |
+ TYPED_ARRAYS(TYPED_ARRAY_CASE) |
+#undef TYPED_ARRAY_CASE |
+ } |
+ } else { |
+ DCHECK(view->IsJSDataView()); |
+ tag = ArrayBufferViewTag::kDataView; |
+ } |
+ WriteVarint(static_cast<uint8_t>(tag)); |
+ WriteVarint(NumberToUint32(view->byte_offset())); |
+ WriteVarint(NumberToUint32(view->byte_length())); |
+ return Just(true); |
+} |
+ |
Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties( |
Handle<JSObject> object, Handle<FixedArray> keys) { |
uint32_t properties_written = 0; |
@@ -688,6 +756,22 @@ void ValueDeserializer::TransferArrayBuffer( |
} |
MaybeHandle<Object> ValueDeserializer::ReadObject() { |
+ MaybeHandle<Object> result = ReadObjectInternal(); |
+ |
+ // ArrayBufferView is special in that it consumes the value before it, even |
+ // after format version 0. |
+ Handle<Object> object; |
+ SerializationTag tag; |
+ if (result.ToHandle(&object) && V8_UNLIKELY(object->IsJSArrayBuffer()) && |
+ PeekTag().To(&tag) && tag == SerializationTag::kArrayBufferView) { |
+ ConsumeTag(SerializationTag::kArrayBufferView); |
+ result = ReadJSArrayBufferView(Handle<JSArrayBuffer>::cast(object)); |
+ } |
+ |
+ return result; |
+} |
+ |
+MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() { |
SerializationTag tag; |
if (!ReadTag().To(&tag)) return MaybeHandle<Object>(); |
switch (tag) { |
@@ -1054,6 +1138,46 @@ MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer() { |
return array_buffer; |
} |
+MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView( |
+ Handle<JSArrayBuffer> buffer) { |
+ uint32_t buffer_byte_length = NumberToUint32(buffer->byte_length()); |
+ uint8_t tag; |
+ uint32_t byte_offset; |
+ uint32_t byte_length; |
+ if (!ReadVarint<uint8_t>().To(&tag) || |
+ !ReadVarint<uint32_t>().To(&byte_offset) || |
+ !ReadVarint<uint32_t>().To(&byte_length) || |
+ byte_offset > buffer_byte_length || |
+ byte_length > buffer_byte_length - byte_offset) { |
+ return MaybeHandle<JSArrayBufferView>(); |
+ } |
+ uint32_t id = next_id_++; |
+ ExternalArrayType external_array_type = kExternalInt8Array; |
+ unsigned element_size = 0; |
+ switch (static_cast<ArrayBufferViewTag>(tag)) { |
+ case ArrayBufferViewTag::kDataView: { |
+ Handle<JSDataView> data_view = |
+ isolate_->factory()->NewJSDataView(buffer, byte_offset, byte_length); |
+ AddObjectWithID(id, data_view); |
+ return data_view; |
+ } |
+#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
+ case ArrayBufferViewTag::k##Type##Array: \ |
+ external_array_type = kExternal##Type##Array; \ |
+ element_size = size; \ |
+ break; |
+ TYPED_ARRAYS(TYPED_ARRAY_CASE) |
+#undef TYPED_ARRAY_CASE |
+ } |
+ if (byte_offset % element_size != 0 || byte_length % element_size != 0) { |
+ return MaybeHandle<JSArrayBufferView>(); |
+ } |
+ Handle<JSTypedArray> typed_array = isolate_->factory()->NewJSTypedArray( |
+ external_array_type, buffer, byte_offset, byte_length / element_size); |
+ AddObjectWithID(id, typed_array); |
+ return typed_array; |
+} |
+ |
Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( |
Handle<JSObject> object, SerializationTag end_tag) { |
for (uint32_t num_properties = 0;; num_properties++) { |