Chromium Code Reviews| 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 |