Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Side by Side Diff: src/value-serializer.cc

Issue 2246093003: Blink-compatible serialization of dictionary-like objects. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: cleanup and clang-format Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698