Index: src/value-serializer.cc |
diff --git a/src/value-serializer.cc b/src/value-serializer.cc |
index ad7492a2a60f32c03ca30b4808fb8954c3f95fb1..6061da58f065940fc89fc43d0e6cf26b4c916a18 100644 |
--- a/src/value-serializer.cc |
+++ b/src/value-serializer.cc |
@@ -60,6 +60,16 @@ enum class SerializationTag : uint8_t { |
kBeginJSObject = 'o', |
// End of a JS object. numProperties:uint32_t |
kEndJSObject = '{', |
+ // Beginning of a sparse JS array. length:uint32_t |
+ // Elements and properties are written as key/value pairs, like objects. |
+ kBeginSparseJSArray = 'a', |
+ // End of a sparse JS array. numProperties:uint32_t length:uint32_t |
+ kEndSparseJSArray = '@', |
+ // Beginning of a dense JS array. length:uint32_t |
+ // |length| elements, followed by properties as key/value pairs |
+ kBeginDenseJSArray = 'A', |
+ // End of a dense JS array. numProperties:uint32_t length:uint32_t |
+ kEndDenseJSArray = '$', |
}; |
ValueSerializer::ValueSerializer(Isolate* isolate) |
@@ -253,6 +263,8 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) { |
HandleScope scope(isolate_); |
switch (instance_type) { |
+ case JS_ARRAY_TYPE: |
+ return WriteJSArray(Handle<JSArray>::cast(receiver)); |
case JS_OBJECT_TYPE: |
case JS_API_OBJECT_TYPE: |
return WriteJSObject(Handle<JSObject>::cast(receiver)); |
@@ -278,6 +290,65 @@ Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) { |
return Just(true); |
} |
+Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) { |
+ uint32_t length; |
+ array->length()->ToArrayLength(&length); |
+ |
+ // This currently does not use dense serialization for holey elements, since |
+ // the format currently does not provide for properly distinguishing between |
Jakob Kummerow
2016/08/18 13:49:56
The deserializer implementation below treats every
jbroman
2016/08/18 15:03:03
That's fair enough. I'd originally had the deseria
Jakob Kummerow
2016/08/18 17:42:28
Yes, I know.
I meant my review comment as conside
jbroman
2016/08/18 18:10:11
Reworded the comment to that effect, and expanded
|
+ // undefined and the hole. Additionally, if holey elements were used, it's |
+ // possible that an element which began non-enumerable (by being a hole) |
+ // became enumerable (by being assigned to). |
+ const bool should_serialize_densely = |
+ array->HasFastElements() && !array->HasFastHoleyElements(); |
Jakob Kummerow
2016/08/18 13:49:56
Given that the array could transition to slow or h
jbroman
2016/08/18 15:03:04
A more principled heuristic would be used_elements
Jakob Kummerow
2016/08/18 17:42:28
Yes, something like that. Doing that later (if at
jbroman
2016/08/18 18:10:11
I've reworded the comment to (hopefully) be cleare
|
+ |
+ if (should_serialize_densely) { |
+ // TODO(jbroman): Take fuller advantage of fast elements. |
+ // TODO(jbroman): Distinguish between undefined and a hole (this can happen |
+ // if serializing one of the elements deletes another). This requires wire |
+ // format changes. |
+ WriteTag(SerializationTag::kBeginDenseJSArray); |
+ WriteVarint<uint32_t>(length); |
+ for (uint32_t i = 0; i < length; i++) { |
+ Handle<Object> element; |
+ LookupIterator it(isolate_, array, i, array, LookupIterator::OWN); |
Jakob Kummerow
2016/08/18 13:49:56
Please add a comment:
// Recursively serializing t
jbroman
2016/08/18 15:03:04
Done. That's why I left taking advantage of the el
Jakob Kummerow
2016/08/18 17:42:28
Yes.
|
+ if (!Object::GetProperty(&it).ToHandle(&element) || |
+ !WriteObject(element).FromMaybe(false)) { |
+ return Nothing<bool>(); |
+ } |
+ } |
+ KeyAccumulator accumulator(isolate_, KeyCollectionMode::kOwnOnly, |
+ ENUMERABLE_STRINGS); |
+ if (!accumulator.CollectOwnPropertyNames(array, array).FromMaybe(false)) { |
+ return Nothing<bool>(); |
+ } |
+ Handle<FixedArray> keys = |
+ accumulator.GetKeys(GetKeysConversion::kConvertToString); |
+ uint32_t properties_written; |
+ if (!WriteJSObjectProperties(array, keys).To(&properties_written)) { |
+ return Nothing<bool>(); |
+ } |
+ WriteTag(SerializationTag::kEndDenseJSArray); |
+ WriteVarint<uint32_t>(properties_written); |
+ WriteVarint<uint32_t>(length); |
Jakob Kummerow
2016/08/18 13:49:56
Any particular reason why |length| is written twic
jbroman
2016/08/18 15:03:03
Compatibility with the existing format. Some of th
|
+ } else { |
+ WriteTag(SerializationTag::kBeginSparseJSArray); |
+ WriteVarint<uint32_t>(length); |
+ Handle<FixedArray> keys; |
+ uint32_t properties_written; |
+ if (!KeyAccumulator::GetKeys(array, KeyCollectionMode::kOwnOnly, |
+ ENUMERABLE_STRINGS) |
+ .ToHandle(&keys) || |
+ !WriteJSObjectProperties(array, keys).To(&properties_written)) { |
+ return Nothing<bool>(); |
+ } |
+ WriteTag(SerializationTag::kEndSparseJSArray); |
+ WriteVarint<uint32_t>(properties_written); |
+ WriteVarint<uint32_t>(length); |
+ } |
+ return Just(true); |
+} |
+ |
Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties( |
Handle<JSObject> object, Handle<FixedArray> keys) { |
uint32_t properties_written = 0; |
@@ -454,6 +525,10 @@ MaybeHandle<Object> ValueDeserializer::ReadObject() { |
} |
case SerializationTag::kBeginJSObject: |
return ReadJSObject(); |
+ case SerializationTag::kBeginSparseJSArray: |
+ return ReadSparseJSArray(); |
+ case SerializationTag::kBeginDenseJSArray: |
+ return ReadDenseJSArray(); |
default: |
return MaybeHandle<Object>(); |
} |
@@ -517,6 +592,71 @@ MaybeHandle<JSObject> ValueDeserializer::ReadJSObject() { |
return scope.CloseAndEscape(object); |
} |
+MaybeHandle<JSArray> ValueDeserializer::ReadSparseJSArray() { |
+ // If we are at the end of the stack, abort. This function may recurse. |
+ if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSArray>(); |
+ |
+ uint32_t length; |
+ if (!ReadVarint<uint32_t>().To(&length)) return MaybeHandle<JSArray>(); |
+ |
+ uint32_t id = next_id_++; |
+ HandleScope scope(isolate_); |
+ Handle<JSArray> array = isolate_->factory()->NewJSArray(0); |
+ JSArray::SetLength(array, length); |
+ AddObjectWithID(id, array); |
+ |
+ uint32_t num_properties; |
+ uint32_t expected_num_properties; |
+ uint32_t expected_length; |
+ if (!ReadJSObjectProperties(array, SerializationTag::kEndSparseJSArray) |
+ .To(&num_properties) || |
+ !ReadVarint<uint32_t>().To(&expected_num_properties) || |
+ !ReadVarint<uint32_t>().To(&expected_length) || |
+ num_properties != expected_num_properties || length != expected_length) { |
+ return MaybeHandle<JSArray>(); |
+ } |
+ |
+ DCHECK(HasObjectWithID(id)); |
+ return scope.CloseAndEscape(array); |
+} |
+ |
+MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() { |
+ // If we are at the end of the stack, abort. This function may recurse. |
+ if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSArray>(); |
+ |
+ uint32_t length; |
+ if (!ReadVarint<uint32_t>().To(&length)) return MaybeHandle<JSArray>(); |
+ |
+ uint32_t id = next_id_++; |
+ HandleScope scope(isolate_); |
+ Handle<JSArray> array = isolate_->factory()->NewJSArray( |
+ FAST_HOLEY_ELEMENTS, length, length, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); |
Jakob Kummerow
2016/08/18 13:49:56
It's a bit sad that deserialized arrays will alway
jbroman
2016/08/18 15:03:03
Probably logic to use FAST_ELEMENTS if there are n
|
+ AddObjectWithID(id, array); |
+ |
+ Handle<FixedArray> elements(FixedArray::cast(array->elements()), isolate_); |
+ for (uint32_t i = 0; i < length; i++) { |
+ Handle<Object> element; |
+ if (!ReadObject().ToHandle(&element)) return MaybeHandle<JSArray>(); |
+ // TODO(jbroman): Distinguish between undefined and a hole. |
+ if (element->IsUndefined(isolate_)) continue; |
Jakob Kummerow
2016/08/18 13:49:56
This is inconsistent with the serializer. Are you
jbroman
2016/08/18 15:03:03
In fact I don't want to, but the existing wire for
Jakob Kummerow
2016/08/18 17:42:28
I'm aware of the wire format's limitation. I was j
jbroman
2016/08/18 18:10:11
It's unfortunate but intentional, yes.
|
+ elements->set(i, *element); |
+ } |
+ |
+ uint32_t num_properties; |
+ uint32_t expected_num_properties; |
+ uint32_t expected_length; |
+ if (!ReadJSObjectProperties(array, SerializationTag::kEndDenseJSArray) |
+ .To(&num_properties) || |
+ !ReadVarint<uint32_t>().To(&expected_num_properties) || |
+ !ReadVarint<uint32_t>().To(&expected_length) || |
+ num_properties != expected_num_properties || length != expected_length) { |
+ return MaybeHandle<JSArray>(); |
+ } |
+ |
+ DCHECK(HasObjectWithID(id)); |
+ return scope.CloseAndEscape(array); |
+} |
+ |
Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( |
Handle<JSObject> object, SerializationTag end_tag) { |
for (uint32_t num_properties = 0;; num_properties++) { |