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 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 47 kInt32 = 'I', | 47 kInt32 = 'I', |
| 48 // Number represented as 32-bit unsigned integer, varint-encoded | 48 // Number represented as 32-bit unsigned integer, varint-encoded |
| 49 // (like uint32 in protobuf) | 49 // (like uint32 in protobuf) |
| 50 kUint32 = 'U', | 50 kUint32 = 'U', |
| 51 // Number represented as a 64-bit double. | 51 // Number represented as a 64-bit double. |
| 52 // Host byte order is used (N.B. this makes the format non-portable). | 52 // Host byte order is used (N.B. this makes the format non-portable). |
| 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 | |
| 58 kObjectReference = '^', | |
| 59 // Beginning of a JS object. | |
| 60 kBeginJSObject = 'o', | |
| 61 // End of a JS object. numProperties:uint32_t | |
| 62 kEndJSObject = '{', | |
| 57 }; | 63 }; |
| 58 | 64 |
| 59 ValueSerializer::ValueSerializer() {} | 65 ValueSerializer::ValueSerializer(Isolate* isolate) |
| 66 : isolate_(isolate), | |
| 67 zone_(isolate->allocator()), | |
| 68 id_map_(isolate->heap(), &zone_) {} | |
| 60 | 69 |
| 61 ValueSerializer::~ValueSerializer() {} | 70 ValueSerializer::~ValueSerializer() {} |
| 62 | 71 |
| 63 void ValueSerializer::WriteHeader() { | 72 void ValueSerializer::WriteHeader() { |
| 64 WriteTag(SerializationTag::kVersion); | 73 WriteTag(SerializationTag::kVersion); |
| 65 WriteVarint(kLatestVersion); | 74 WriteVarint(kLatestVersion); |
| 66 } | 75 } |
| 67 | 76 |
| 68 void ValueSerializer::WriteTag(SerializationTag tag) { | 77 void ValueSerializer::WriteTag(SerializationTag tag) { |
| 69 buffer_.push_back(static_cast<uint8_t>(tag)); | 78 buffer_.push_back(static_cast<uint8_t>(tag)); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 137 WriteOddball(Oddball::cast(*object)); | 146 WriteOddball(Oddball::cast(*object)); |
| 138 return Just(true); | 147 return Just(true); |
| 139 case HEAP_NUMBER_TYPE: | 148 case HEAP_NUMBER_TYPE: |
| 140 case MUTABLE_HEAP_NUMBER_TYPE: | 149 case MUTABLE_HEAP_NUMBER_TYPE: |
| 141 WriteHeapNumber(HeapNumber::cast(*object)); | 150 WriteHeapNumber(HeapNumber::cast(*object)); |
| 142 return Just(true); | 151 return Just(true); |
| 143 default: | 152 default: |
| 144 if (object->IsString()) { | 153 if (object->IsString()) { |
| 145 WriteString(Handle<String>::cast(object)); | 154 WriteString(Handle<String>::cast(object)); |
| 146 return Just(true); | 155 return Just(true); |
| 156 } else if (object->IsJSReceiver()) { | |
| 157 return WriteJSReceiver(Handle<JSReceiver>::cast(object)); | |
| 147 } | 158 } |
| 148 UNIMPLEMENTED(); | 159 UNIMPLEMENTED(); |
| 149 return Nothing<bool>(); | 160 return Nothing<bool>(); |
| 150 } | 161 } |
| 151 } | 162 } |
| 152 | 163 |
| 153 void ValueSerializer::WriteOddball(Oddball* oddball) { | 164 void ValueSerializer::WriteOddball(Oddball* oddball) { |
| 154 SerializationTag tag = SerializationTag::kUndefined; | 165 SerializationTag tag = SerializationTag::kUndefined; |
| 155 switch (oddball->kind()) { | 166 switch (oddball->kind()) { |
| 156 case Oddball::kUndefined: | 167 case Oddball::kUndefined: |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 211 // The existing reading code expects 16-byte strings to be aligned. | 222 // The existing reading code expects 16-byte strings to be aligned. |
| 212 if ((buffer_.size() + 1 + BytesNeededForVarint(byte_length)) & 1) | 223 if ((buffer_.size() + 1 + BytesNeededForVarint(byte_length)) & 1) |
| 213 WriteTag(SerializationTag::kPadding); | 224 WriteTag(SerializationTag::kPadding); |
| 214 WriteTag(SerializationTag::kTwoByteString); | 225 WriteTag(SerializationTag::kTwoByteString); |
| 215 WriteTwoByteString(chars); | 226 WriteTwoByteString(chars); |
| 216 } else { | 227 } else { |
| 217 UNREACHABLE(); | 228 UNREACHABLE(); |
| 218 } | 229 } |
| 219 } | 230 } |
| 220 | 231 |
| 232 Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) { | |
| 233 // If the object has already been serialized, just write its ID. | |
| 234 uint32_t* id_map_entry = id_map_.Get(receiver); | |
| 235 if (uint32_t id = *id_map_entry) { | |
| 236 WriteTag(SerializationTag::kObjectReference); | |
| 237 WriteVarint(id - 1); | |
| 238 return Just(true); | |
| 239 } | |
| 240 | |
| 241 // Otherwise, allocate an ID for it. | |
| 242 uint32_t id = next_id_++; | |
| 243 *id_map_entry = id + 1; | |
| 244 | |
| 245 // Eliminate callable and exotic objects, which should not be serialized. | |
| 246 auto instance_type = receiver->map()->instance_type(); | |
|
Camillo Bruni
2016/08/16 11:26:57
nit: we don't use the auto type in V8
jbroman
2016/08/16 18:16:29
Done.
| |
| 247 if (receiver->IsCallable() || instance_type <= LAST_SPECIAL_RECEIVER_TYPE) | |
| 248 return Nothing<bool>(); | |
|
Camillo Bruni
2016/08/16 11:26:57
nit: missing braces
| |
| 249 | |
| 250 // If we are at the end of the stack, abort. This function may recurse. | |
| 251 if (StackLimitCheck(isolate_).HasOverflowed()) return Nothing<bool>(); | |
| 252 | |
| 253 HandleScope scope(isolate_); | |
| 254 switch (instance_type) { | |
| 255 case JS_OBJECT_TYPE: | |
| 256 case JS_API_OBJECT_TYPE: | |
| 257 return WriteJSObject(Handle<JSObject>::cast(receiver)); | |
| 258 default: | |
| 259 UNIMPLEMENTED(); | |
| 260 break; | |
| 261 } | |
| 262 return Nothing<bool>(); | |
| 263 } | |
| 264 | |
| 265 Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) { | |
| 266 WriteTag(SerializationTag::kBeginJSObject); | |
| 267 Handle<FixedArray> keys; | |
| 268 if (!KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, | |
|
Camillo Bruni
2016/08/16 11:26:56
I'm sure you already had a look at JsonStringifier
jbroman
2016/08/16 18:16:29
I've definitely considered it, though I figured I'
jbroman
2016/08/16 18:22:13
Ah, this is partially answered by your comment on
| |
| 269 ENUMERABLE_STRINGS) | |
| 270 .ToHandle(&keys) || | |
| 271 !WriteJSObjectProperties(object, keys).FromMaybe(false)) | |
| 272 return Nothing<bool>(); | |
|
Camillo Bruni
2016/08/16 11:26:57
nit: missing braces
jbroman
2016/08/16 18:16:29
Done.
| |
| 273 WriteTag(SerializationTag::kEndJSObject); | |
| 274 WriteVarint<uint32_t>(keys->length()); | |
| 275 return Just(true); | |
| 276 } | |
| 277 | |
| 278 Maybe<bool> ValueSerializer::WriteJSObjectProperties(Handle<JSObject> object, | |
| 279 Handle<FixedArray> keys) { | |
| 280 int length = keys->length(); | |
| 281 for (int i = 0; i < length; i++) { | |
| 282 Handle<Object> key(keys->get(i), isolate_); | |
| 283 if (!WriteObject(key).FromMaybe(false)) return Nothing<bool>(); | |
|
Camillo Bruni
2016/08/16 11:26:57
I don't know the details of the post message seria
jbroman
2016/08/16 18:16:29
The structured clone algorithm reproduces properti
| |
| 284 bool success; | |
| 285 LookupIterator it = LookupIterator::PropertyOrElement( | |
| 286 isolate_, object, key, &success, LookupIterator::OWN); | |
| 287 DCHECK(success); | |
| 288 Handle<Object> value; | |
| 289 if (!Object::GetProperty(&it).ToHandle(&value) || | |
| 290 !WriteObject(value).FromMaybe(false)) | |
| 291 return Nothing<bool>(); | |
| 292 } | |
| 293 return Just(true); | |
| 294 } | |
| 295 | |
| 221 ValueDeserializer::ValueDeserializer(Isolate* isolate, | 296 ValueDeserializer::ValueDeserializer(Isolate* isolate, |
| 222 Vector<const uint8_t> data) | 297 Vector<const uint8_t> data) |
| 223 : isolate_(isolate), | 298 : isolate_(isolate), |
| 224 position_(data.start()), | 299 position_(data.start()), |
| 225 end_(data.start() + data.length()) {} | 300 end_(data.start() + data.length()), |
| 301 id_map_(Handle<SeededNumberDictionary>::cast( | |
| 302 isolate->global_handles()->Create( | |
| 303 *SeededNumberDictionary::New(isolate, 0)))) {} | |
| 226 | 304 |
| 227 ValueDeserializer::~ValueDeserializer() {} | 305 ValueDeserializer::~ValueDeserializer() { |
| 306 GlobalHandles::Destroy(Handle<Object>::cast(id_map_).location()); | |
|
jbroman
2016/08/15 22:27:06
This seems to be how such handles are currently ma
Camillo Bruni
2016/08/16 11:26:56
If I see this correctly, I think you can get rid o
jbroman
2016/08/16 18:16:28
I couldn't figure out how to do this. We might nee
Jakob Kummerow
2016/08/17 13:00:14
I think your understanding is correct, and GlobalH
| |
| 307 } | |
| 228 | 308 |
| 229 Maybe<bool> ValueDeserializer::ReadHeader() { | 309 Maybe<bool> ValueDeserializer::ReadHeader() { |
| 230 if (position_ < end_ && | 310 if (position_ < end_ && |
| 231 *position_ == static_cast<uint8_t>(SerializationTag::kVersion)) { | 311 *position_ == static_cast<uint8_t>(SerializationTag::kVersion)) { |
| 232 ReadTag().ToChecked(); | 312 ReadTag().ToChecked(); |
| 233 if (!ReadVarint<uint32_t>().To(&version_)) return Nothing<bool>(); | 313 if (!ReadVarint<uint32_t>().To(&version_)) return Nothing<bool>(); |
| 234 if (version_ > kLatestVersion) return Nothing<bool>(); | 314 if (version_ > kLatestVersion) return Nothing<bool>(); |
| 235 } | 315 } |
| 236 return Just(true); | 316 return Just(true); |
| 237 } | 317 } |
| 238 | 318 |
| 319 Maybe<SerializationTag> ValueDeserializer::PeekTag() const { | |
| 320 const uint8_t* peek_position = position_; | |
| 321 SerializationTag tag; | |
| 322 do { | |
| 323 if (peek_position >= end_) return Nothing<SerializationTag>(); | |
| 324 tag = static_cast<SerializationTag>(*peek_position); | |
| 325 peek_position++; | |
| 326 } while (tag == SerializationTag::kPadding); | |
| 327 return Just(tag); | |
| 328 } | |
| 329 | |
| 239 Maybe<SerializationTag> ValueDeserializer::ReadTag() { | 330 Maybe<SerializationTag> ValueDeserializer::ReadTag() { |
| 240 SerializationTag tag; | 331 SerializationTag tag; |
| 241 do { | 332 do { |
| 242 if (position_ >= end_) return Nothing<SerializationTag>(); | 333 if (position_ >= end_) return Nothing<SerializationTag>(); |
| 243 tag = static_cast<SerializationTag>(*position_); | 334 tag = static_cast<SerializationTag>(*position_); |
| 244 position_++; | 335 position_++; |
| 245 } while (tag == SerializationTag::kPadding); | 336 } while (tag == SerializationTag::kPadding); |
| 246 return Just(tag); | 337 return Just(tag); |
| 247 } | 338 } |
| 248 | 339 |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 330 } | 421 } |
| 331 case SerializationTag::kDouble: { | 422 case SerializationTag::kDouble: { |
| 332 Maybe<double> number = ReadDouble(); | 423 Maybe<double> number = ReadDouble(); |
| 333 if (number.IsNothing()) return MaybeHandle<Object>(); | 424 if (number.IsNothing()) return MaybeHandle<Object>(); |
| 334 return isolate_->factory()->NewNumber(number.FromJust()); | 425 return isolate_->factory()->NewNumber(number.FromJust()); |
| 335 } | 426 } |
| 336 case SerializationTag::kUtf8String: | 427 case SerializationTag::kUtf8String: |
| 337 return ReadUtf8String(); | 428 return ReadUtf8String(); |
| 338 case SerializationTag::kTwoByteString: | 429 case SerializationTag::kTwoByteString: |
| 339 return ReadTwoByteString(); | 430 return ReadTwoByteString(); |
| 431 case SerializationTag::kObjectReference: { | |
| 432 uint32_t id; | |
| 433 if (!ReadVarint<uint32_t>().To(&id)) return MaybeHandle<Object>(); | |
| 434 return GetObjectWithID(id); | |
| 435 } | |
| 436 case SerializationTag::kBeginJSObject: | |
| 437 return ReadJSObject(); | |
| 340 default: | 438 default: |
| 341 return MaybeHandle<Object>(); | 439 return MaybeHandle<Object>(); |
| 342 } | 440 } |
| 343 } | 441 } |
| 344 | 442 |
| 345 MaybeHandle<String> ValueDeserializer::ReadUtf8String() { | 443 MaybeHandle<String> ValueDeserializer::ReadUtf8String() { |
| 346 uint32_t utf8_length; | 444 uint32_t utf8_length; |
| 347 Vector<const uint8_t> utf8_bytes; | 445 Vector<const uint8_t> utf8_bytes; |
| 348 if (!ReadVarint<uint32_t>().To(&utf8_length) || | 446 if (!ReadVarint<uint32_t>().To(&utf8_length) || |
| 349 utf8_length > | 447 utf8_length > |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 370 ->NewRawTwoByteString(byte_length / sizeof(uc16)) | 468 ->NewRawTwoByteString(byte_length / sizeof(uc16)) |
| 371 .ToHandle(&string)) | 469 .ToHandle(&string)) |
| 372 return MaybeHandle<String>(); | 470 return MaybeHandle<String>(); |
| 373 | 471 |
| 374 // Copy the bytes directly into the new string. | 472 // Copy the bytes directly into the new string. |
| 375 // Warning: this uses host endianness. | 473 // Warning: this uses host endianness. |
| 376 memcpy(string->GetChars(), bytes.begin(), bytes.length()); | 474 memcpy(string->GetChars(), bytes.begin(), bytes.length()); |
| 377 return string; | 475 return string; |
| 378 } | 476 } |
| 379 | 477 |
| 478 MaybeHandle<JSObject> ValueDeserializer::ReadJSObject() { | |
| 479 return ReadJSReceiver<JSObject>([this](uint32_t id) -> MaybeHandle<JSObject> { | |
|
Camillo Bruni
2016/08/16 11:26:56
nit: I think V8 is not too fond of functors (call
jbroman
2016/08/16 18:16:28
My only reservation is making sure that all JSRece
| |
| 480 Handle<JSObject> object = | |
| 481 isolate_->factory()->NewJSObject(isolate_->object_function()); | |
| 482 AddObjectWithID(id, object); | |
| 483 | |
| 484 uint32_t num_properties; | |
| 485 uint32_t expected_num_properties; | |
| 486 if (!ReadJSObjectProperties(object, SerializationTag::kEndJSObject) | |
| 487 .To(&num_properties) || | |
| 488 !ReadVarint<uint32_t>().To(&expected_num_properties) || | |
| 489 num_properties != expected_num_properties) | |
| 490 return MaybeHandle<JSObject>(); | |
|
Camillo Bruni
2016/08/16 11:26:57
nit: missing braces
jbroman
2016/08/16 18:16:29
Done.
| |
| 491 | |
| 492 return object; | |
| 493 }); | |
| 494 } | |
| 495 | |
| 496 template <typename T, typename Functor> | |
| 497 MaybeHandle<T> ValueDeserializer::ReadJSReceiver(const Functor& functor) { | |
| 498 // If we are at the end of the stack, abort. This function may recurse. | |
| 499 if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<T>(); | |
| 500 | |
| 501 uint32_t id = next_id_++; | |
| 502 HandleScope scope(isolate_); | |
| 503 Handle<T> handle; | |
| 504 if (!functor(id).ToHandle(&handle)) return MaybeHandle<T>(); | |
| 505 DCHECK(HasObjectWithID(id)); | |
| 506 return scope.CloseAndEscape(handle); | |
| 507 } | |
| 508 | |
| 509 Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( | |
| 510 Handle<JSObject> object, SerializationTag end_tag) { | |
| 511 for (uint32_t num_properties = 0;; num_properties++) { | |
| 512 SerializationTag tag; | |
| 513 if (!PeekTag().To(&tag)) return Nothing<uint32_t>(); | |
| 514 if (tag == end_tag) { | |
| 515 SerializationTag consumed_tag = ReadTag().ToChecked(); | |
| 516 (void)consumed_tag; | |
| 517 DCHECK(tag == consumed_tag); | |
| 518 return Just(num_properties); | |
| 519 } | |
| 520 | |
| 521 Handle<Object> key; | |
| 522 if (!ReadObject().ToHandle(&key)) return Nothing<uint32_t>(); | |
| 523 Handle<Object> value; | |
| 524 if (!ReadObject().ToHandle(&value)) return Nothing<uint32_t>(); | |
| 525 | |
| 526 bool success; | |
| 527 LookupIterator it = LookupIterator::PropertyOrElement( | |
| 528 isolate_, object, key, &success, LookupIterator::OWN); | |
| 529 if (!success || | |
| 530 JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE).is_null()) | |
| 531 return Nothing<uint32_t>(); | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 bool ValueDeserializer::HasObjectWithID(uint32_t id) { | |
| 536 return id_map_->Has(isolate_, id); | |
| 537 } | |
| 538 | |
| 539 MaybeHandle<JSReceiver> ValueDeserializer::GetObjectWithID(uint32_t id) { | |
| 540 int index = id_map_->FindEntry(isolate_, id); | |
| 541 if (index == SeededNumberDictionary::kNotFound) | |
| 542 return MaybeHandle<JSReceiver>(); | |
|
Camillo Bruni
2016/08/16 11:26:57
nit: missing braces
jbroman
2016/08/16 18:16:29
Done.
The other cases you've pointed out had mult
| |
| 543 Object* value = id_map_->ValueAt(index); | |
| 544 DCHECK(value->IsJSReceiver()); | |
| 545 return Handle<JSReceiver>(JSReceiver::cast(value), isolate_); | |
| 546 } | |
| 547 | |
| 548 void ValueDeserializer::AddObjectWithID(uint32_t id, | |
| 549 Handle<JSReceiver> object) { | |
| 550 DCHECK(!HasObjectWithID(id)); | |
| 551 const bool used_as_prototype = false; | |
| 552 Handle<SeededNumberDictionary> new_dictionary = | |
| 553 SeededNumberDictionary::AtNumberPut(id_map_, id, object, | |
| 554 used_as_prototype); | |
| 555 | |
| 556 // If the dictionary was reallocated, update the global handle. | |
| 557 if (!new_dictionary.is_identical_to(id_map_)) { | |
| 558 GlobalHandles::Destroy(Handle<Object>::cast(id_map_).location()); | |
| 559 id_map_ = Handle<SeededNumberDictionary>::cast( | |
| 560 isolate_->global_handles()->Create(*new_dictionary)); | |
| 561 } | |
|
Camillo Bruni
2016/08/16 11:26:57
nit: I'd drop the global handle (as I outlined abo
jbroman
2016/08/16 18:16:29
Replied above. Happy to eliminate it if there's a
| |
| 562 } | |
| 563 | |
| 380 } // namespace internal | 564 } // namespace internal |
| 381 } // namespace v8 | 565 } // namespace v8 |
| OLD | NEW |