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 |