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 InstanceType instance_type = receiver->map()->instance_type(); |
| 247 if (receiver->IsCallable() || instance_type <= LAST_SPECIAL_RECEIVER_TYPE) { |
| 248 return Nothing<bool>(); |
| 249 } |
| 250 |
| 251 // If we are at the end of the stack, abort. This function may recurse. |
| 252 if (StackLimitCheck(isolate_).HasOverflowed()) return Nothing<bool>(); |
| 253 |
| 254 HandleScope scope(isolate_); |
| 255 switch (instance_type) { |
| 256 case JS_OBJECT_TYPE: |
| 257 case JS_API_OBJECT_TYPE: |
| 258 return WriteJSObject(Handle<JSObject>::cast(receiver)); |
| 259 default: |
| 260 UNIMPLEMENTED(); |
| 261 break; |
| 262 } |
| 263 return Nothing<bool>(); |
| 264 } |
| 265 |
| 266 Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) { |
| 267 WriteTag(SerializationTag::kBeginJSObject); |
| 268 Handle<FixedArray> keys; |
| 269 uint32_t properties_written; |
| 270 if (!KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, |
| 271 ENUMERABLE_STRINGS) |
| 272 .ToHandle(&keys) || |
| 273 !WriteJSObjectProperties(object, keys).To(&properties_written)) { |
| 274 return Nothing<bool>(); |
| 275 } |
| 276 WriteTag(SerializationTag::kEndJSObject); |
| 277 WriteVarint<uint32_t>(properties_written); |
| 278 return Just(true); |
| 279 } |
| 280 |
| 281 Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties( |
| 282 Handle<JSObject> object, Handle<FixedArray> keys) { |
| 283 uint32_t properties_written = 0; |
| 284 int length = keys->length(); |
| 285 for (int i = 0; i < length; i++) { |
| 286 Handle<Object> key(keys->get(i), isolate_); |
| 287 |
| 288 bool success; |
| 289 LookupIterator it = LookupIterator::PropertyOrElement( |
| 290 isolate_, object, key, &success, LookupIterator::OWN); |
| 291 DCHECK(success); |
| 292 Handle<Object> value; |
| 293 if (!Object::GetProperty(&it).ToHandle(&value)) return Nothing<uint32_t>(); |
| 294 |
| 295 // If the property is no longer found, do not serialize it. |
| 296 // This could happen if a getter deleted the property. |
| 297 if (!it.IsFound()) continue; |
| 298 |
| 299 if (!WriteObject(key).FromMaybe(false) || |
| 300 !WriteObject(value).FromMaybe(false)) { |
| 301 return Nothing<uint32_t>(); |
| 302 } |
| 303 |
| 304 properties_written++; |
| 305 } |
| 306 return Just(properties_written); |
| 307 } |
| 308 |
221 ValueDeserializer::ValueDeserializer(Isolate* isolate, | 309 ValueDeserializer::ValueDeserializer(Isolate* isolate, |
222 Vector<const uint8_t> data) | 310 Vector<const uint8_t> data) |
223 : isolate_(isolate), | 311 : isolate_(isolate), |
224 position_(data.start()), | 312 position_(data.start()), |
225 end_(data.start() + data.length()) {} | 313 end_(data.start() + data.length()), |
| 314 id_map_(Handle<SeededNumberDictionary>::cast( |
| 315 isolate->global_handles()->Create( |
| 316 *SeededNumberDictionary::New(isolate, 0)))) {} |
226 | 317 |
227 ValueDeserializer::~ValueDeserializer() {} | 318 ValueDeserializer::~ValueDeserializer() { |
| 319 GlobalHandles::Destroy(Handle<Object>::cast(id_map_).location()); |
| 320 } |
228 | 321 |
229 Maybe<bool> ValueDeserializer::ReadHeader() { | 322 Maybe<bool> ValueDeserializer::ReadHeader() { |
230 if (position_ < end_ && | 323 if (position_ < end_ && |
231 *position_ == static_cast<uint8_t>(SerializationTag::kVersion)) { | 324 *position_ == static_cast<uint8_t>(SerializationTag::kVersion)) { |
232 ReadTag().ToChecked(); | 325 ReadTag().ToChecked(); |
233 if (!ReadVarint<uint32_t>().To(&version_)) return Nothing<bool>(); | 326 if (!ReadVarint<uint32_t>().To(&version_)) return Nothing<bool>(); |
234 if (version_ > kLatestVersion) return Nothing<bool>(); | 327 if (version_ > kLatestVersion) return Nothing<bool>(); |
235 } | 328 } |
236 return Just(true); | 329 return Just(true); |
237 } | 330 } |
238 | 331 |
| 332 Maybe<SerializationTag> ValueDeserializer::PeekTag() const { |
| 333 const uint8_t* peek_position = position_; |
| 334 SerializationTag tag; |
| 335 do { |
| 336 if (peek_position >= end_) return Nothing<SerializationTag>(); |
| 337 tag = static_cast<SerializationTag>(*peek_position); |
| 338 peek_position++; |
| 339 } while (tag == SerializationTag::kPadding); |
| 340 return Just(tag); |
| 341 } |
| 342 |
239 Maybe<SerializationTag> ValueDeserializer::ReadTag() { | 343 Maybe<SerializationTag> ValueDeserializer::ReadTag() { |
240 SerializationTag tag; | 344 SerializationTag tag; |
241 do { | 345 do { |
242 if (position_ >= end_) return Nothing<SerializationTag>(); | 346 if (position_ >= end_) return Nothing<SerializationTag>(); |
243 tag = static_cast<SerializationTag>(*position_); | 347 tag = static_cast<SerializationTag>(*position_); |
244 position_++; | 348 position_++; |
245 } while (tag == SerializationTag::kPadding); | 349 } while (tag == SerializationTag::kPadding); |
246 return Just(tag); | 350 return Just(tag); |
247 } | 351 } |
248 | 352 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
330 } | 434 } |
331 case SerializationTag::kDouble: { | 435 case SerializationTag::kDouble: { |
332 Maybe<double> number = ReadDouble(); | 436 Maybe<double> number = ReadDouble(); |
333 if (number.IsNothing()) return MaybeHandle<Object>(); | 437 if (number.IsNothing()) return MaybeHandle<Object>(); |
334 return isolate_->factory()->NewNumber(number.FromJust()); | 438 return isolate_->factory()->NewNumber(number.FromJust()); |
335 } | 439 } |
336 case SerializationTag::kUtf8String: | 440 case SerializationTag::kUtf8String: |
337 return ReadUtf8String(); | 441 return ReadUtf8String(); |
338 case SerializationTag::kTwoByteString: | 442 case SerializationTag::kTwoByteString: |
339 return ReadTwoByteString(); | 443 return ReadTwoByteString(); |
| 444 case SerializationTag::kObjectReference: { |
| 445 uint32_t id; |
| 446 if (!ReadVarint<uint32_t>().To(&id)) return MaybeHandle<Object>(); |
| 447 return GetObjectWithID(id); |
| 448 } |
| 449 case SerializationTag::kBeginJSObject: |
| 450 return ReadJSObject(); |
340 default: | 451 default: |
341 return MaybeHandle<Object>(); | 452 return MaybeHandle<Object>(); |
342 } | 453 } |
343 } | 454 } |
344 | 455 |
345 MaybeHandle<String> ValueDeserializer::ReadUtf8String() { | 456 MaybeHandle<String> ValueDeserializer::ReadUtf8String() { |
346 uint32_t utf8_length; | 457 uint32_t utf8_length; |
347 Vector<const uint8_t> utf8_bytes; | 458 Vector<const uint8_t> utf8_bytes; |
348 if (!ReadVarint<uint32_t>().To(&utf8_length) || | 459 if (!ReadVarint<uint32_t>().To(&utf8_length) || |
349 utf8_length > | 460 utf8_length > |
(...skipping 20 matching lines...) Expand all Loading... |
370 ->NewRawTwoByteString(byte_length / sizeof(uc16)) | 481 ->NewRawTwoByteString(byte_length / sizeof(uc16)) |
371 .ToHandle(&string)) | 482 .ToHandle(&string)) |
372 return MaybeHandle<String>(); | 483 return MaybeHandle<String>(); |
373 | 484 |
374 // Copy the bytes directly into the new string. | 485 // Copy the bytes directly into the new string. |
375 // Warning: this uses host endianness. | 486 // Warning: this uses host endianness. |
376 memcpy(string->GetChars(), bytes.begin(), bytes.length()); | 487 memcpy(string->GetChars(), bytes.begin(), bytes.length()); |
377 return string; | 488 return string; |
378 } | 489 } |
379 | 490 |
| 491 MaybeHandle<JSObject> ValueDeserializer::ReadJSObject() { |
| 492 // If we are at the end of the stack, abort. This function may recurse. |
| 493 if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSObject>(); |
| 494 |
| 495 uint32_t id = next_id_++; |
| 496 HandleScope scope(isolate_); |
| 497 Handle<JSObject> object = |
| 498 isolate_->factory()->NewJSObject(isolate_->object_function()); |
| 499 AddObjectWithID(id, object); |
| 500 |
| 501 uint32_t num_properties; |
| 502 uint32_t expected_num_properties; |
| 503 if (!ReadJSObjectProperties(object, SerializationTag::kEndJSObject) |
| 504 .To(&num_properties) || |
| 505 !ReadVarint<uint32_t>().To(&expected_num_properties) || |
| 506 num_properties != expected_num_properties) { |
| 507 return MaybeHandle<JSObject>(); |
| 508 } |
| 509 |
| 510 DCHECK(HasObjectWithID(id)); |
| 511 return scope.CloseAndEscape(object); |
| 512 } |
| 513 |
| 514 Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( |
| 515 Handle<JSObject> object, SerializationTag end_tag) { |
| 516 for (uint32_t num_properties = 0;; num_properties++) { |
| 517 SerializationTag tag; |
| 518 if (!PeekTag().To(&tag)) return Nothing<uint32_t>(); |
| 519 if (tag == end_tag) { |
| 520 SerializationTag consumed_tag = ReadTag().ToChecked(); |
| 521 USE(consumed_tag); |
| 522 DCHECK(tag == consumed_tag); |
| 523 return Just(num_properties); |
| 524 } |
| 525 |
| 526 Handle<Object> key; |
| 527 if (!ReadObject().ToHandle(&key)) return Nothing<uint32_t>(); |
| 528 Handle<Object> value; |
| 529 if (!ReadObject().ToHandle(&value)) return Nothing<uint32_t>(); |
| 530 |
| 531 bool success; |
| 532 LookupIterator it = LookupIterator::PropertyOrElement( |
| 533 isolate_, object, key, &success, LookupIterator::OWN); |
| 534 if (!success || |
| 535 JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE) |
| 536 .is_null()) { |
| 537 return Nothing<uint32_t>(); |
| 538 } |
| 539 } |
| 540 } |
| 541 |
| 542 bool ValueDeserializer::HasObjectWithID(uint32_t id) { |
| 543 return id_map_->Has(isolate_, id); |
| 544 } |
| 545 |
| 546 MaybeHandle<JSReceiver> ValueDeserializer::GetObjectWithID(uint32_t id) { |
| 547 int index = id_map_->FindEntry(isolate_, id); |
| 548 if (index == SeededNumberDictionary::kNotFound) { |
| 549 return MaybeHandle<JSReceiver>(); |
| 550 } |
| 551 Object* value = id_map_->ValueAt(index); |
| 552 DCHECK(value->IsJSReceiver()); |
| 553 return Handle<JSReceiver>(JSReceiver::cast(value), isolate_); |
| 554 } |
| 555 |
| 556 void ValueDeserializer::AddObjectWithID(uint32_t id, |
| 557 Handle<JSReceiver> object) { |
| 558 DCHECK(!HasObjectWithID(id)); |
| 559 const bool used_as_prototype = false; |
| 560 Handle<SeededNumberDictionary> new_dictionary = |
| 561 SeededNumberDictionary::AtNumberPut(id_map_, id, object, |
| 562 used_as_prototype); |
| 563 |
| 564 // If the dictionary was reallocated, update the global handle. |
| 565 if (!new_dictionary.is_identical_to(id_map_)) { |
| 566 GlobalHandles::Destroy(Handle<Object>::cast(id_map_).location()); |
| 567 id_map_ = Handle<SeededNumberDictionary>::cast( |
| 568 isolate_->global_handles()->Create(*new_dictionary)); |
| 569 } |
| 570 } |
| 571 |
380 } // namespace internal | 572 } // namespace internal |
381 } // namespace v8 | 573 } // namespace v8 |
OLD | NEW |