| Index: src/value-serializer.cc
|
| diff --git a/src/value-serializer.cc b/src/value-serializer.cc
|
| index ad7492a2a60f32c03ca30b4808fb8954c3f95fb1..880dc8046d7a7f2111b72c146c0193ef03ec0bf4 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,69 @@ Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) {
|
| return Just(true);
|
| }
|
|
|
| +Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
|
| + uint32_t length = 0;
|
| + bool valid_length = array->length()->ToArrayLength(&length);
|
| + DCHECK(valid_length);
|
| + USE(valid_length);
|
| +
|
| + // To keep things simple, for now we decide between dense and sparse
|
| + // serialization based on elements kind. A more principled heuristic could
|
| + // count the elements, but would need to take care to note which indices
|
| + // existed (as only indices which were enumerable own properties at this point
|
| + // should be serialized).
|
| + const bool should_serialize_densely =
|
| + array->HasFastElements() && !array->HasFastHoleyElements();
|
| +
|
| + if (should_serialize_densely) {
|
| + // 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++) {
|
| + // Serializing the array's elements can have arbitrary side effects, so we
|
| + // cannot rely on still having fast elements, even if it did to begin
|
| + // with.
|
| + Handle<Object> element;
|
| + LookupIterator it(isolate_, array, i, array, LookupIterator::OWN);
|
| + 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);
|
| + } 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 +529,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 +596,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);
|
| + 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;
|
| + 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++) {
|
|
|