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 = '$', | |
73 }; | 63 }; |
74 | 64 |
75 ValueSerializer::ValueSerializer(Isolate* isolate) | 65 ValueSerializer::ValueSerializer(Isolate* isolate) |
76 : isolate_(isolate), | 66 : isolate_(isolate), |
77 zone_(isolate->allocator()), | 67 zone_(isolate->allocator()), |
78 id_map_(isolate->heap(), &zone_) {} | 68 id_map_(isolate->heap(), &zone_) {} |
79 | 69 |
80 ValueSerializer::~ValueSerializer() {} | 70 ValueSerializer::~ValueSerializer() {} |
81 | 71 |
82 void ValueSerializer::WriteHeader() { | 72 void ValueSerializer::WriteHeader() { |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
256 InstanceType instance_type = receiver->map()->instance_type(); | 246 InstanceType instance_type = receiver->map()->instance_type(); |
257 if (receiver->IsCallable() || instance_type <= LAST_SPECIAL_RECEIVER_TYPE) { | 247 if (receiver->IsCallable() || instance_type <= LAST_SPECIAL_RECEIVER_TYPE) { |
258 return Nothing<bool>(); | 248 return Nothing<bool>(); |
259 } | 249 } |
260 | 250 |
261 // If we are at the end of the stack, abort. This function may recurse. | 251 // If we are at the end of the stack, abort. This function may recurse. |
262 if (StackLimitCheck(isolate_).HasOverflowed()) return Nothing<bool>(); | 252 if (StackLimitCheck(isolate_).HasOverflowed()) return Nothing<bool>(); |
263 | 253 |
264 HandleScope scope(isolate_); | 254 HandleScope scope(isolate_); |
265 switch (instance_type) { | 255 switch (instance_type) { |
266 case JS_ARRAY_TYPE: | |
267 return WriteJSArray(Handle<JSArray>::cast(receiver)); | |
268 case JS_OBJECT_TYPE: | 256 case JS_OBJECT_TYPE: |
269 case JS_API_OBJECT_TYPE: | 257 case JS_API_OBJECT_TYPE: |
270 return WriteJSObject(Handle<JSObject>::cast(receiver)); | 258 return WriteJSObject(Handle<JSObject>::cast(receiver)); |
271 default: | 259 default: |
272 UNIMPLEMENTED(); | 260 UNIMPLEMENTED(); |
273 break; | 261 break; |
274 } | 262 } |
275 return Nothing<bool>(); | 263 return Nothing<bool>(); |
276 } | 264 } |
277 | 265 |
278 Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) { | 266 Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) { |
279 WriteTag(SerializationTag::kBeginJSObject); | 267 WriteTag(SerializationTag::kBeginJSObject); |
280 Handle<FixedArray> keys; | 268 Handle<FixedArray> keys; |
281 uint32_t properties_written; | 269 uint32_t properties_written; |
282 if (!KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, | 270 if (!KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, |
283 ENUMERABLE_STRINGS) | 271 ENUMERABLE_STRINGS) |
284 .ToHandle(&keys) || | 272 .ToHandle(&keys) || |
285 !WriteJSObjectProperties(object, keys).To(&properties_written)) { | 273 !WriteJSObjectProperties(object, keys).To(&properties_written)) { |
286 return Nothing<bool>(); | 274 return Nothing<bool>(); |
287 } | 275 } |
288 WriteTag(SerializationTag::kEndJSObject); | 276 WriteTag(SerializationTag::kEndJSObject); |
289 WriteVarint<uint32_t>(properties_written); | 277 WriteVarint<uint32_t>(properties_written); |
290 return Just(true); | 278 return Just(true); |
291 } | 279 } |
292 | 280 |
293 Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) { | |
294 uint32_t length; | |
295 array->length()->ToArrayLength(&length); | |
296 | |
297 // To keep things simple, for now we decide between dense and sparse | |
298 // serialization based on elements kind. A more principled heuristic could | |
299 // count the elements, but would need to take care to note which indices | |
300 // existed (as only indices which were enumerable own properties at this point | |
301 // should be serialized). | |
302 const bool should_serialize_densely = | |
303 array->HasFastElements() && !array->HasFastHoleyElements(); | |
304 | |
305 if (should_serialize_densely) { | |
306 // TODO(jbroman): Distinguish between undefined and a hole (this can happen | |
307 // if serializing one of the elements deletes another). This requires wire | |
308 // format changes. | |
309 WriteTag(SerializationTag::kBeginDenseJSArray); | |
310 WriteVarint<uint32_t>(length); | |
311 for (uint32_t i = 0; i < length; i++) { | |
312 // Serializing the array's elements can have arbitrary side effects, so we | |
313 // cannot rely on still having fast elements, even if it did to begin | |
314 // with. | |
315 Handle<Object> element; | |
316 LookupIterator it(isolate_, array, i, array, LookupIterator::OWN); | |
317 if (!Object::GetProperty(&it).ToHandle(&element) || | |
318 !WriteObject(element).FromMaybe(false)) { | |
319 return Nothing<bool>(); | |
320 } | |
321 } | |
322 KeyAccumulator accumulator(isolate_, KeyCollectionMode::kOwnOnly, | |
323 ENUMERABLE_STRINGS); | |
324 if (!accumulator.CollectOwnPropertyNames(array, array).FromMaybe(false)) { | |
325 return Nothing<bool>(); | |
326 } | |
327 Handle<FixedArray> keys = | |
328 accumulator.GetKeys(GetKeysConversion::kConvertToString); | |
329 uint32_t properties_written; | |
330 if (!WriteJSObjectProperties(array, keys).To(&properties_written)) { | |
331 return Nothing<bool>(); | |
332 } | |
333 WriteTag(SerializationTag::kEndDenseJSArray); | |
334 WriteVarint<uint32_t>(properties_written); | |
335 WriteVarint<uint32_t>(length); | |
336 } else { | |
337 WriteTag(SerializationTag::kBeginSparseJSArray); | |
338 WriteVarint<uint32_t>(length); | |
339 Handle<FixedArray> keys; | |
340 uint32_t properties_written; | |
341 if (!KeyAccumulator::GetKeys(array, KeyCollectionMode::kOwnOnly, | |
342 ENUMERABLE_STRINGS) | |
343 .ToHandle(&keys) || | |
344 !WriteJSObjectProperties(array, keys).To(&properties_written)) { | |
345 return Nothing<bool>(); | |
346 } | |
347 WriteTag(SerializationTag::kEndSparseJSArray); | |
348 WriteVarint<uint32_t>(properties_written); | |
349 WriteVarint<uint32_t>(length); | |
350 } | |
351 return Just(true); | |
352 } | |
353 | |
354 Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties( | 281 Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties( |
355 Handle<JSObject> object, Handle<FixedArray> keys) { | 282 Handle<JSObject> object, Handle<FixedArray> keys) { |
356 uint32_t properties_written = 0; | 283 uint32_t properties_written = 0; |
357 int length = keys->length(); | 284 int length = keys->length(); |
358 for (int i = 0; i < length; i++) { | 285 for (int i = 0; i < length; i++) { |
359 Handle<Object> key(keys->get(i), isolate_); | 286 Handle<Object> key(keys->get(i), isolate_); |
360 | 287 |
361 bool success; | 288 bool success; |
362 LookupIterator it = LookupIterator::PropertyOrElement( | 289 LookupIterator it = LookupIterator::PropertyOrElement( |
363 isolate_, object, key, &success, LookupIterator::OWN); | 290 isolate_, object, key, &success, LookupIterator::OWN); |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
520 return ReadUtf8String(); | 447 return ReadUtf8String(); |
521 case SerializationTag::kTwoByteString: | 448 case SerializationTag::kTwoByteString: |
522 return ReadTwoByteString(); | 449 return ReadTwoByteString(); |
523 case SerializationTag::kObjectReference: { | 450 case SerializationTag::kObjectReference: { |
524 uint32_t id; | 451 uint32_t id; |
525 if (!ReadVarint<uint32_t>().To(&id)) return MaybeHandle<Object>(); | 452 if (!ReadVarint<uint32_t>().To(&id)) return MaybeHandle<Object>(); |
526 return GetObjectWithID(id); | 453 return GetObjectWithID(id); |
527 } | 454 } |
528 case SerializationTag::kBeginJSObject: | 455 case SerializationTag::kBeginJSObject: |
529 return ReadJSObject(); | 456 return ReadJSObject(); |
530 case SerializationTag::kBeginSparseJSArray: | |
531 return ReadSparseJSArray(); | |
532 case SerializationTag::kBeginDenseJSArray: | |
533 return ReadDenseJSArray(); | |
534 default: | 457 default: |
535 return MaybeHandle<Object>(); | 458 return MaybeHandle<Object>(); |
536 } | 459 } |
537 } | 460 } |
538 | 461 |
539 MaybeHandle<String> ValueDeserializer::ReadUtf8String() { | 462 MaybeHandle<String> ValueDeserializer::ReadUtf8String() { |
540 uint32_t utf8_length; | 463 uint32_t utf8_length; |
541 Vector<const uint8_t> utf8_bytes; | 464 Vector<const uint8_t> utf8_bytes; |
542 if (!ReadVarint<uint32_t>().To(&utf8_length) || | 465 if (!ReadVarint<uint32_t>().To(&utf8_length) || |
543 utf8_length > | 466 utf8_length > |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
587 .To(&num_properties) || | 510 .To(&num_properties) || |
588 !ReadVarint<uint32_t>().To(&expected_num_properties) || | 511 !ReadVarint<uint32_t>().To(&expected_num_properties) || |
589 num_properties != expected_num_properties) { | 512 num_properties != expected_num_properties) { |
590 return MaybeHandle<JSObject>(); | 513 return MaybeHandle<JSObject>(); |
591 } | 514 } |
592 | 515 |
593 DCHECK(HasObjectWithID(id)); | 516 DCHECK(HasObjectWithID(id)); |
594 return scope.CloseAndEscape(object); | 517 return scope.CloseAndEscape(object); |
595 } | 518 } |
596 | 519 |
597 MaybeHandle<JSArray> ValueDeserializer::ReadSparseJSArray() { | |
598 // If we are at the end of the stack, abort. This function may recurse. | |
599 if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSArray>(); | |
600 | |
601 uint32_t length; | |
602 if (!ReadVarint<uint32_t>().To(&length)) return MaybeHandle<JSArray>(); | |
603 | |
604 uint32_t id = next_id_++; | |
605 HandleScope scope(isolate_); | |
606 Handle<JSArray> array = isolate_->factory()->NewJSArray(0); | |
607 JSArray::SetLength(array, length); | |
608 AddObjectWithID(id, array); | |
609 | |
610 uint32_t num_properties; | |
611 uint32_t expected_num_properties; | |
612 uint32_t expected_length; | |
613 if (!ReadJSObjectProperties(array, SerializationTag::kEndSparseJSArray) | |
614 .To(&num_properties) || | |
615 !ReadVarint<uint32_t>().To(&expected_num_properties) || | |
616 !ReadVarint<uint32_t>().To(&expected_length) || | |
617 num_properties != expected_num_properties || length != expected_length) { | |
618 return MaybeHandle<JSArray>(); | |
619 } | |
620 | |
621 DCHECK(HasObjectWithID(id)); | |
622 return scope.CloseAndEscape(array); | |
623 } | |
624 | |
625 MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() { | |
626 // If we are at the end of the stack, abort. This function may recurse. | |
627 if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSArray>(); | |
628 | |
629 uint32_t length; | |
630 if (!ReadVarint<uint32_t>().To(&length)) return MaybeHandle<JSArray>(); | |
631 | |
632 uint32_t id = next_id_++; | |
633 HandleScope scope(isolate_); | |
634 Handle<JSArray> array = isolate_->factory()->NewJSArray( | |
635 FAST_HOLEY_ELEMENTS, length, length, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); | |
636 AddObjectWithID(id, array); | |
637 | |
638 Handle<FixedArray> elements(FixedArray::cast(array->elements()), isolate_); | |
639 for (uint32_t i = 0; i < length; i++) { | |
640 Handle<Object> element; | |
641 if (!ReadObject().ToHandle(&element)) return MaybeHandle<JSArray>(); | |
642 // TODO(jbroman): Distinguish between undefined and a hole. | |
643 if (element->IsUndefined(isolate_)) continue; | |
644 elements->set(i, *element); | |
645 } | |
646 | |
647 uint32_t num_properties; | |
648 uint32_t expected_num_properties; | |
649 uint32_t expected_length; | |
650 if (!ReadJSObjectProperties(array, SerializationTag::kEndDenseJSArray) | |
651 .To(&num_properties) || | |
652 !ReadVarint<uint32_t>().To(&expected_num_properties) || | |
653 !ReadVarint<uint32_t>().To(&expected_length) || | |
654 num_properties != expected_num_properties || length != expected_length) { | |
655 return MaybeHandle<JSArray>(); | |
656 } | |
657 | |
658 DCHECK(HasObjectWithID(id)); | |
659 return scope.CloseAndEscape(array); | |
660 } | |
661 | |
662 Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( | 520 Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( |
663 Handle<JSObject> object, SerializationTag end_tag) { | 521 Handle<JSObject> object, SerializationTag end_tag) { |
664 for (uint32_t num_properties = 0;; num_properties++) { | 522 for (uint32_t num_properties = 0;; num_properties++) { |
665 SerializationTag tag; | 523 SerializationTag tag; |
666 if (!PeekTag().To(&tag)) return Nothing<uint32_t>(); | 524 if (!PeekTag().To(&tag)) return Nothing<uint32_t>(); |
667 if (tag == end_tag) { | 525 if (tag == end_tag) { |
668 ConsumeTag(end_tag); | 526 ConsumeTag(end_tag); |
669 return Just(num_properties); | 527 return Just(num_properties); |
670 } | 528 } |
671 | 529 |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
782 } | 640 } |
783 #endif | 641 #endif |
784 position_ = end_; | 642 position_ = end_; |
785 | 643 |
786 if (stack.size() != 1) return MaybeHandle<Object>(); | 644 if (stack.size() != 1) return MaybeHandle<Object>(); |
787 return scope.CloseAndEscape(stack[0]); | 645 return scope.CloseAndEscape(stack[0]); |
788 } | 646 } |
789 | 647 |
790 } // namespace internal | 648 } // namespace internal |
791 } // namespace v8 | 649 } // namespace v8 |
OLD | NEW |