OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |