Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: src/value-serializer.cc

Issue 2259633002: Blink-compatible serialization of arrays, both dense and sparse. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Micro-tweak: let NewJSArray construct the elements. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 the V8 project authors. All rights reserved. 1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/value-serializer.h" 5 #include "src/value-serializer.h"
6 6
7 #include <type_traits> 7 #include <type_traits>
8 8
9 #include "src/base/logging.h" 9 #include "src/base/logging.h"
10 #include "src/factory.h" 10 #include "src/factory.h"
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
53 kDouble = 'N', 53 kDouble = 'N',
54 // byteLength:uint32_t, then raw data 54 // byteLength:uint32_t, then raw data
55 kUtf8String = 'S', 55 kUtf8String = 'S',
56 kTwoByteString = 'c', 56 kTwoByteString = 'c',
57 // Reference to a serialized object. objectID:uint32_t 57 // Reference to a serialized object. objectID:uint32_t
58 kObjectReference = '^', 58 kObjectReference = '^',
59 // Beginning of a JS object. 59 // Beginning of a JS object.
60 kBeginJSObject = 'o', 60 kBeginJSObject = 'o',
61 // End of a JS object. numProperties:uint32_t 61 // End of a JS object. numProperties:uint32_t
62 kEndJSObject = '{', 62 kEndJSObject = '{',
63 // Beginning of a sparse JS array. length:uint32_t
64 // Elements and properties are written as key/value pairs, like objects.
65 kBeginSparseJSArray = 'a',
66 // End of a sparse JS array. numProperties:uint32_t length:uint32_t
67 kEndSparseJSArray = '@',
68 // Beginning of a dense JS array. length:uint32_t
69 // |length| elements, followed by properties as key/value pairs
70 kBeginDenseJSArray = 'A',
71 // End of a dense JS array. numProperties:uint32_t length:uint32_t
72 kEndDenseJSArray = '$',
63 }; 73 };
64 74
65 ValueSerializer::ValueSerializer(Isolate* isolate) 75 ValueSerializer::ValueSerializer(Isolate* isolate)
66 : isolate_(isolate), 76 : isolate_(isolate),
67 zone_(isolate->allocator()), 77 zone_(isolate->allocator()),
68 id_map_(isolate->heap(), &zone_) {} 78 id_map_(isolate->heap(), &zone_) {}
69 79
70 ValueSerializer::~ValueSerializer() {} 80 ValueSerializer::~ValueSerializer() {}
71 81
72 void ValueSerializer::WriteHeader() { 82 void ValueSerializer::WriteHeader() {
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
246 InstanceType instance_type = receiver->map()->instance_type(); 256 InstanceType instance_type = receiver->map()->instance_type();
247 if (receiver->IsCallable() || instance_type <= LAST_SPECIAL_RECEIVER_TYPE) { 257 if (receiver->IsCallable() || instance_type <= LAST_SPECIAL_RECEIVER_TYPE) {
248 return Nothing<bool>(); 258 return Nothing<bool>();
249 } 259 }
250 260
251 // If we are at the end of the stack, abort. This function may recurse. 261 // If we are at the end of the stack, abort. This function may recurse.
252 if (StackLimitCheck(isolate_).HasOverflowed()) return Nothing<bool>(); 262 if (StackLimitCheck(isolate_).HasOverflowed()) return Nothing<bool>();
253 263
254 HandleScope scope(isolate_); 264 HandleScope scope(isolate_);
255 switch (instance_type) { 265 switch (instance_type) {
266 case JS_ARRAY_TYPE:
267 return WriteJSArray(Handle<JSArray>::cast(receiver));
256 case JS_OBJECT_TYPE: 268 case JS_OBJECT_TYPE:
257 case JS_API_OBJECT_TYPE: 269 case JS_API_OBJECT_TYPE:
258 return WriteJSObject(Handle<JSObject>::cast(receiver)); 270 return WriteJSObject(Handle<JSObject>::cast(receiver));
259 default: 271 default:
260 UNIMPLEMENTED(); 272 UNIMPLEMENTED();
261 break; 273 break;
262 } 274 }
263 return Nothing<bool>(); 275 return Nothing<bool>();
264 } 276 }
265 277
266 Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) { 278 Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) {
267 WriteTag(SerializationTag::kBeginJSObject); 279 WriteTag(SerializationTag::kBeginJSObject);
268 Handle<FixedArray> keys; 280 Handle<FixedArray> keys;
269 uint32_t properties_written; 281 uint32_t properties_written;
270 if (!KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, 282 if (!KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
271 ENUMERABLE_STRINGS) 283 ENUMERABLE_STRINGS)
272 .ToHandle(&keys) || 284 .ToHandle(&keys) ||
273 !WriteJSObjectProperties(object, keys).To(&properties_written)) { 285 !WriteJSObjectProperties(object, keys).To(&properties_written)) {
274 return Nothing<bool>(); 286 return Nothing<bool>();
275 } 287 }
276 WriteTag(SerializationTag::kEndJSObject); 288 WriteTag(SerializationTag::kEndJSObject);
277 WriteVarint<uint32_t>(properties_written); 289 WriteVarint<uint32_t>(properties_written);
278 return Just(true); 290 return Just(true);
279 } 291 }
280 292
293 Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
294 uint32_t length;
295 array->length()->ToArrayLength(&length);
296
297 // This currently does not use dense serialization for holey elements, since
298 // 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
299 // undefined and the hole. Additionally, if holey elements were used, it's
300 // possible that an element which began non-enumerable (by being a hole)
301 // became enumerable (by being assigned to).
302 const bool should_serialize_densely =
303 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
304
305 if (should_serialize_densely) {
306 // TODO(jbroman): Take fuller advantage of fast elements.
307 // TODO(jbroman): Distinguish between undefined and a hole (this can happen
308 // if serializing one of the elements deletes another). This requires wire
309 // format changes.
310 WriteTag(SerializationTag::kBeginDenseJSArray);
311 WriteVarint<uint32_t>(length);
312 for (uint32_t i = 0; i < length; i++) {
313 Handle<Object> element;
314 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.
315 if (!Object::GetProperty(&it).ToHandle(&element) ||
316 !WriteObject(element).FromMaybe(false)) {
317 return Nothing<bool>();
318 }
319 }
320 KeyAccumulator accumulator(isolate_, KeyCollectionMode::kOwnOnly,
321 ENUMERABLE_STRINGS);
322 if (!accumulator.CollectOwnPropertyNames(array, array).FromMaybe(false)) {
323 return Nothing<bool>();
324 }
325 Handle<FixedArray> keys =
326 accumulator.GetKeys(GetKeysConversion::kConvertToString);
327 uint32_t properties_written;
328 if (!WriteJSObjectProperties(array, keys).To(&properties_written)) {
329 return Nothing<bool>();
330 }
331 WriteTag(SerializationTag::kEndDenseJSArray);
332 WriteVarint<uint32_t>(properties_written);
333 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
334 } else {
335 WriteTag(SerializationTag::kBeginSparseJSArray);
336 WriteVarint<uint32_t>(length);
337 Handle<FixedArray> keys;
338 uint32_t properties_written;
339 if (!KeyAccumulator::GetKeys(array, KeyCollectionMode::kOwnOnly,
340 ENUMERABLE_STRINGS)
341 .ToHandle(&keys) ||
342 !WriteJSObjectProperties(array, keys).To(&properties_written)) {
343 return Nothing<bool>();
344 }
345 WriteTag(SerializationTag::kEndSparseJSArray);
346 WriteVarint<uint32_t>(properties_written);
347 WriteVarint<uint32_t>(length);
348 }
349 return Just(true);
350 }
351
281 Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties( 352 Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties(
282 Handle<JSObject> object, Handle<FixedArray> keys) { 353 Handle<JSObject> object, Handle<FixedArray> keys) {
283 uint32_t properties_written = 0; 354 uint32_t properties_written = 0;
284 int length = keys->length(); 355 int length = keys->length();
285 for (int i = 0; i < length; i++) { 356 for (int i = 0; i < length; i++) {
286 Handle<Object> key(keys->get(i), isolate_); 357 Handle<Object> key(keys->get(i), isolate_);
287 358
288 bool success; 359 bool success;
289 LookupIterator it = LookupIterator::PropertyOrElement( 360 LookupIterator it = LookupIterator::PropertyOrElement(
290 isolate_, object, key, &success, LookupIterator::OWN); 361 isolate_, object, key, &success, LookupIterator::OWN);
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 return ReadUtf8String(); 518 return ReadUtf8String();
448 case SerializationTag::kTwoByteString: 519 case SerializationTag::kTwoByteString:
449 return ReadTwoByteString(); 520 return ReadTwoByteString();
450 case SerializationTag::kObjectReference: { 521 case SerializationTag::kObjectReference: {
451 uint32_t id; 522 uint32_t id;
452 if (!ReadVarint<uint32_t>().To(&id)) return MaybeHandle<Object>(); 523 if (!ReadVarint<uint32_t>().To(&id)) return MaybeHandle<Object>();
453 return GetObjectWithID(id); 524 return GetObjectWithID(id);
454 } 525 }
455 case SerializationTag::kBeginJSObject: 526 case SerializationTag::kBeginJSObject:
456 return ReadJSObject(); 527 return ReadJSObject();
528 case SerializationTag::kBeginSparseJSArray:
529 return ReadSparseJSArray();
530 case SerializationTag::kBeginDenseJSArray:
531 return ReadDenseJSArray();
457 default: 532 default:
458 return MaybeHandle<Object>(); 533 return MaybeHandle<Object>();
459 } 534 }
460 } 535 }
461 536
462 MaybeHandle<String> ValueDeserializer::ReadUtf8String() { 537 MaybeHandle<String> ValueDeserializer::ReadUtf8String() {
463 uint32_t utf8_length; 538 uint32_t utf8_length;
464 Vector<const uint8_t> utf8_bytes; 539 Vector<const uint8_t> utf8_bytes;
465 if (!ReadVarint<uint32_t>().To(&utf8_length) || 540 if (!ReadVarint<uint32_t>().To(&utf8_length) ||
466 utf8_length > 541 utf8_length >
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
510 .To(&num_properties) || 585 .To(&num_properties) ||
511 !ReadVarint<uint32_t>().To(&expected_num_properties) || 586 !ReadVarint<uint32_t>().To(&expected_num_properties) ||
512 num_properties != expected_num_properties) { 587 num_properties != expected_num_properties) {
513 return MaybeHandle<JSObject>(); 588 return MaybeHandle<JSObject>();
514 } 589 }
515 590
516 DCHECK(HasObjectWithID(id)); 591 DCHECK(HasObjectWithID(id));
517 return scope.CloseAndEscape(object); 592 return scope.CloseAndEscape(object);
518 } 593 }
519 594
595 MaybeHandle<JSArray> ValueDeserializer::ReadSparseJSArray() {
596 // If we are at the end of the stack, abort. This function may recurse.
597 if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSArray>();
598
599 uint32_t length;
600 if (!ReadVarint<uint32_t>().To(&length)) return MaybeHandle<JSArray>();
601
602 uint32_t id = next_id_++;
603 HandleScope scope(isolate_);
604 Handle<JSArray> array = isolate_->factory()->NewJSArray(0);
605 JSArray::SetLength(array, length);
606 AddObjectWithID(id, array);
607
608 uint32_t num_properties;
609 uint32_t expected_num_properties;
610 uint32_t expected_length;
611 if (!ReadJSObjectProperties(array, SerializationTag::kEndSparseJSArray)
612 .To(&num_properties) ||
613 !ReadVarint<uint32_t>().To(&expected_num_properties) ||
614 !ReadVarint<uint32_t>().To(&expected_length) ||
615 num_properties != expected_num_properties || length != expected_length) {
616 return MaybeHandle<JSArray>();
617 }
618
619 DCHECK(HasObjectWithID(id));
620 return scope.CloseAndEscape(array);
621 }
622
623 MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() {
624 // If we are at the end of the stack, abort. This function may recurse.
625 if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSArray>();
626
627 uint32_t length;
628 if (!ReadVarint<uint32_t>().To(&length)) return MaybeHandle<JSArray>();
629
630 uint32_t id = next_id_++;
631 HandleScope scope(isolate_);
632 Handle<JSArray> array = isolate_->factory()->NewJSArray(
633 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
634 AddObjectWithID(id, array);
635
636 Handle<FixedArray> elements(FixedArray::cast(array->elements()), isolate_);
637 for (uint32_t i = 0; i < length; i++) {
638 Handle<Object> element;
639 if (!ReadObject().ToHandle(&element)) return MaybeHandle<JSArray>();
640 // TODO(jbroman): Distinguish between undefined and a hole.
641 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.
642 elements->set(i, *element);
643 }
644
645 uint32_t num_properties;
646 uint32_t expected_num_properties;
647 uint32_t expected_length;
648 if (!ReadJSObjectProperties(array, SerializationTag::kEndDenseJSArray)
649 .To(&num_properties) ||
650 !ReadVarint<uint32_t>().To(&expected_num_properties) ||
651 !ReadVarint<uint32_t>().To(&expected_length) ||
652 num_properties != expected_num_properties || length != expected_length) {
653 return MaybeHandle<JSArray>();
654 }
655
656 DCHECK(HasObjectWithID(id));
657 return scope.CloseAndEscape(array);
658 }
659
520 Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( 660 Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
521 Handle<JSObject> object, SerializationTag end_tag) { 661 Handle<JSObject> object, SerializationTag end_tag) {
522 for (uint32_t num_properties = 0;; num_properties++) { 662 for (uint32_t num_properties = 0;; num_properties++) {
523 SerializationTag tag; 663 SerializationTag tag;
524 if (!PeekTag().To(&tag)) return Nothing<uint32_t>(); 664 if (!PeekTag().To(&tag)) return Nothing<uint32_t>();
525 if (tag == end_tag) { 665 if (tag == end_tag) {
526 ConsumeTag(end_tag); 666 ConsumeTag(end_tag);
527 return Just(num_properties); 667 return Just(num_properties);
528 } 668 }
529 669
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
640 } 780 }
641 #endif 781 #endif
642 position_ = end_; 782 position_ = end_;
643 783
644 if (stack.size() != 1) return MaybeHandle<Object>(); 784 if (stack.size() != 1) return MaybeHandle<Object>();
645 return scope.CloseAndEscape(stack[0]); 785 return scope.CloseAndEscape(stack[0]);
646 } 786 }
647 787
648 } // namespace internal 788 } // namespace internal
649 } // namespace v8 789 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698