value-serializer.cc 77.5 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/value-serializer.h"

#include <type_traits>

9
#include "include/v8-value-serializer-version.h"
10
#include "src/api-inl.h"
11
#include "src/base/logging.h"
12
#include "src/conversions.h"
13
#include "src/flags.h"
14
#include "src/handles-inl.h"
15
#include "src/heap/factory.h"
16
#include "src/isolate.h"
17
#include "src/maybe-handles-inl.h"
18
#include "src/objects-inl.h"
19
#include "src/objects/heap-number-inl.h"
20
#include "src/objects/js-array-inl.h"
21 22
#include "src/objects/js-collection-inl.h"
#include "src/objects/js-regexp-inl.h"
23
#include "src/objects/oddball-inl.h"
24
#include "src/objects/ordered-hash-table-inl.h"
25
#include "src/objects/smi.h"
26
#include "src/snapshot/code-serializer.h"
27
#include "src/transitions.h"
28
#include "src/wasm/wasm-engine.h"
29
#include "src/wasm/wasm-objects-inl.h"
30
#include "src/wasm/wasm-result.h"
31
#include "src/wasm/wasm-serialization.h"
32 33 34 35

namespace v8 {
namespace internal {

36 37
// Version 9: (imported from Blink)
// Version 10: one-byte (Latin-1) strings
38
// Version 11: properly separate undefined from the hole in arrays
39
// Version 12: regexp and string objects share normal string encoding
40 41
// Version 13: host objects have an explicit tag (rather than handling all
//             unknown tags)
42 43 44 45 46 47 48 49
//
// WARNING: Increasing this value is a change which cannot safely be rolled
// back without breaking compatibility with data stored on disk. It is
// strongly recommended that you do not make such changes near a release
// milestone branch point.
//
// Recent changes are routinely reverted in preparation for branch, and this
// has been the cause of at least one bug in the past.
50
static const uint32_t kLatestVersion = 13;
51 52
static_assert(kLatestVersion == v8::CurrentValueSerializerFormatVersion(),
              "Exported format version must match latest version.");
53

54
static const int kPretenureThreshold = 100 * KB;
55

56 57 58 59 60 61 62 63 64 65 66 67
template <typename T>
static size_t BytesNeededForVarint(T value) {
  static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
                "Only unsigned integer types can be written as varints.");
  size_t result = 0;
  do {
    result++;
    value >>= 7;
  } while (value);
  return result;
}

68 69 70
// Note that some additional tag values are defined in Blink's
// Source/bindings/core/v8/serialization/SerializationTag.h, which must
// not clash with values defined here.
71
enum class SerializationTag : uint8_t {
72
  // version:uint32_t (if at beginning of data, sets version > 0)
73
  kVersion = 0xFF,
74
  // ignore
75
  kPadding = '\0',
76
  // refTableSize:uint32_t (previously used for sanity checks; safe to ignore)
77
  kVerifyObjectCount = '?',
78
  // Oddballs (no data).
79
  kTheHole = '-',
80 81 82 83
  kUndefined = '_',
  kNull = '0',
  kTrue = 'T',
  kFalse = 'F',
84 85 86 87 88 89 90 91 92
  // Number represented as 32-bit integer, ZigZag-encoded
  // (like sint32 in protobuf)
  kInt32 = 'I',
  // Number represented as 32-bit unsigned integer, varint-encoded
  // (like uint32 in protobuf)
  kUint32 = 'U',
  // Number represented as a 64-bit double.
  // Host byte order is used (N.B. this makes the format non-portable).
  kDouble = 'N',
93 94
  // BigInt. Bitfield:uint32_t, then raw digits storage.
  kBigInt = 'Z',
95 96
  // byteLength:uint32_t, then raw data
  kUtf8String = 'S',
97
  kOneByteString = '"',
98
  kTwoByteString = 'c',
99 100 101 102 103 104
  // Reference to a serialized object. objectID:uint32_t
  kObjectReference = '^',
  // Beginning of a JS object.
  kBeginJSObject = 'o',
  // End of a JS object. numProperties:uint32_t
  kEndJSObject = '{',
105 106 107 108 109 110 111 112 113 114
  // Beginning of a sparse JS array. length:uint32_t
  // Elements and properties are written as key/value pairs, like objects.
  kBeginSparseJSArray = 'a',
  // End of a sparse JS array. numProperties:uint32_t length:uint32_t
  kEndSparseJSArray = '@',
  // Beginning of a dense JS array. length:uint32_t
  // |length| elements, followed by properties as key/value pairs
  kBeginDenseJSArray = 'A',
  // End of a dense JS array. numProperties:uint32_t length:uint32_t
  kEndDenseJSArray = '$',
115 116
  // Date. millisSinceEpoch:double
  kDate = 'D',
117 118 119 120 121
  // Boolean object. No data.
  kTrueObject = 'y',
  kFalseObject = 'x',
  // Number object. value:double
  kNumberObject = 'n',
122 123
  // BigInt object. Bitfield:uint32_t, then raw digits storage.
  kBigIntObject = 'z',
124 125
  // String object, UTF-8 encoding. byteLength:uint32_t, then raw data.
  kStringObject = 's',
126 127 128
  // Regular expression, UTF-8 encoding. byteLength:uint32_t, raw data,
  // flags:uint32_t.
  kRegExp = 'R',
129 130 131 132 133 134 135 136
  // Beginning of a JS map.
  kBeginJSMap = ';',
  // End of a JS map. length:uint32_t.
  kEndJSMap = ':',
  // Beginning of a JS set.
  kBeginJSSet = '\'',
  // End of a JS set. length:uint32_t.
  kEndJSSet = ',',
137 138
  // Array buffer. byteLength:uint32_t, then raw data.
  kArrayBuffer = 'B',
139 140
  // Array buffer (transferred). transferID:uint32_t
  kArrayBufferTransfer = 't',
141 142 143 144 145 146 147 148
  // View into an array buffer.
  // subtag:ArrayBufferViewTag, byteOffset:uint32_t, byteLength:uint32_t
  // For typed arrays, byteOffset and byteLength must be divisible by the size
  // of the element.
  // Note: kArrayBufferView is special, and should have an ArrayBuffer (or an
  // ObjectReference to one) serialized just before it. This is a quirk arising
  // from the previous stack-based implementation.
  kArrayBufferView = 'V',
149 150
  // Shared array buffer. transferID:uint32_t
  kSharedArrayBuffer = 'u',
151 152 153 154 155
  // Compiled WebAssembly module. encodingType:(one-byte tag).
  // If encodingType == 'y' (raw bytes):
  //  wasmWireByteLength:uint32_t, then raw data
  //  compiledDataLength:uint32_t, then raw data
  kWasmModule = 'W',
156 157
  // A wasm module object transfer. next value is its index.
  kWasmModuleTransfer = 'w',
158 159 160
  // The delegate is responsible for processing all following data.
  // This "escapes" to whatever wire format the delegate chooses.
  kHostObject = '\\',
161 162 163
  // A transferred WebAssembly.Memory object. maximumPages:int32_t, then by
  // SharedArrayBuffer tag and its data.
  kWasmMemoryTransfer = 'm',
164 165
};

166 167 168 169 170 171 172 173 174 175 176 177
namespace {

enum class ArrayBufferViewTag : uint8_t {
  kInt8Array = 'b',
  kUint8Array = 'B',
  kUint8ClampedArray = 'C',
  kInt16Array = 'w',
  kUint16Array = 'W',
  kInt32Array = 'd',
  kUint32Array = 'D',
  kFloat32Array = 'f',
  kFloat64Array = 'F',
178 179
  kBigInt64Array = 'q',
  kBigUint64Array = 'Q',
180 181 182
  kDataView = '?',
};

183 184 185 186
enum class WasmEncodingTag : uint8_t {
  kRawBytes = 'y',
};

187 188
}  // namespace

189 190
ValueSerializer::ValueSerializer(Isolate* isolate,
                                 v8::ValueSerializer::Delegate* delegate)
191
    : isolate_(isolate),
192
      delegate_(delegate),
193
      zone_(isolate->allocator(), ZONE_NAME),
194 195 196
      id_map_(isolate->heap(), ZoneAllocationPolicy(&zone_)),
      array_buffer_transfer_map_(isolate->heap(),
                                 ZoneAllocationPolicy(&zone_)) {}
197

198 199 200 201 202 203 204 205 206
ValueSerializer::~ValueSerializer() {
  if (buffer_) {
    if (delegate_) {
      delegate_->FreeBufferMemory(buffer_);
    } else {
      free(buffer_);
    }
  }
}
207 208 209 210 211 212

void ValueSerializer::WriteHeader() {
  WriteTag(SerializationTag::kVersion);
  WriteVarint(kLatestVersion);
}

213 214 215 216
void ValueSerializer::SetTreatArrayBufferViewsAsHostObjects(bool mode) {
  treat_array_buffer_views_as_host_objects_ = mode;
}

217
void ValueSerializer::WriteTag(SerializationTag tag) {
218 219
  uint8_t raw_tag = static_cast<uint8_t>(tag);
  WriteRawBytes(&raw_tag, sizeof(raw_tag));
220 221 222 223 224 225 226 227 228 229 230 231 232
}

template <typename T>
void ValueSerializer::WriteVarint(T value) {
  // Writes an unsigned integer as a base-128 varint.
  // The number is written, 7 bits at a time, from the least significant to the
  // most significant 7 bits. Each byte, except the last, has the MSB set.
  // See also https://developers.google.com/protocol-buffers/docs/encoding
  static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
                "Only unsigned integer types can be written as varints.");
  uint8_t stack_buffer[sizeof(T) * 8 / 7 + 1];
  uint8_t* next_byte = &stack_buffer[0];
  do {
233
    *next_byte = (value & 0x7F) | 0x80;
234 235 236
    next_byte++;
    value >>= 7;
  } while (value);
237
  *(next_byte - 1) &= 0x7F;
238
  WriteRawBytes(stack_buffer, next_byte - stack_buffer);
239 240
}

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
template <typename T>
void ValueSerializer::WriteZigZag(T value) {
  // Writes a signed integer as a varint using ZigZag encoding (i.e. 0 is
  // encoded as 0, -1 as 1, 1 as 2, -2 as 3, and so on).
  // See also https://developers.google.com/protocol-buffers/docs/encoding
  // Note that this implementation relies on the right shift being arithmetic.
  static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
                "Only signed integer types can be written as zigzag.");
  using UnsignedT = typename std::make_unsigned<T>::type;
  WriteVarint((static_cast<UnsignedT>(value) << 1) ^
              (value >> (8 * sizeof(T) - 1)));
}

void ValueSerializer::WriteDouble(double value) {
  // Warning: this uses host endianness.
256
  WriteRawBytes(&value, sizeof(value));
257 258
}

259 260
void ValueSerializer::WriteOneByteString(Vector<const uint8_t> chars) {
  WriteVarint<uint32_t>(chars.length());
261
  WriteRawBytes(chars.begin(), chars.length() * sizeof(uint8_t));
262 263 264 265 266
}

void ValueSerializer::WriteTwoByteString(Vector<const uc16> chars) {
  // Warning: this uses host endianness.
  WriteVarint<uint32_t>(chars.length() * sizeof(uc16));
267
  WriteRawBytes(chars.begin(), chars.length() * sizeof(uc16));
268 269
}

270
void ValueSerializer::WriteBigIntContents(BigInt bigint) {
271 272 273 274 275 276 277 278 279
  uint32_t bitfield = bigint->GetBitfieldForSerialization();
  int bytelength = BigInt::DigitsByteLengthForBitfield(bitfield);
  WriteVarint<uint32_t>(bitfield);
  uint8_t* dest;
  if (ReserveRawBytes(bytelength).To(&dest)) {
    bigint->SerializeDigits(dest);
  }
}

280
void ValueSerializer::WriteRawBytes(const void* source, size_t length) {
281 282 283 284
  uint8_t* dest;
  if (ReserveRawBytes(length).To(&dest)) {
    memcpy(dest, source, length);
  }
285 286
}

287
Maybe<uint8_t*> ValueSerializer::ReserveRawBytes(size_t bytes) {
288 289
  size_t old_size = buffer_size_;
  size_t new_size = old_size + bytes;
290
  if (V8_UNLIKELY(new_size > buffer_capacity_)) {
291 292 293 294 295
    bool ok;
    if (!ExpandBuffer(new_size).To(&ok)) {
      return Nothing<uint8_t*>();
    }
  }
296
  buffer_size_ = new_size;
297
  return Just(&buffer_[old_size]);
298 299
}

300
Maybe<bool> ValueSerializer::ExpandBuffer(size_t required_capacity) {
301 302 303 304 305 306 307 308 309 310 311 312
  DCHECK_GT(required_capacity, buffer_capacity_);
  size_t requested_capacity =
      std::max(required_capacity, buffer_capacity_ * 2) + 64;
  size_t provided_capacity = 0;
  void* new_buffer = nullptr;
  if (delegate_) {
    new_buffer = delegate_->ReallocateBufferMemory(buffer_, requested_capacity,
                                                   &provided_capacity);
  } else {
    new_buffer = realloc(buffer_, requested_capacity);
    provided_capacity = requested_capacity;
  }
313 314 315 316 317 318 319 320 321
  if (new_buffer) {
    DCHECK(provided_capacity >= requested_capacity);
    buffer_ = reinterpret_cast<uint8_t*>(new_buffer);
    buffer_capacity_ = provided_capacity;
    return Just(true);
  } else {
    out_of_memory_ = true;
    return Nothing<bool>();
  }
322 323
}

324 325 326 327 328 329 330 331
void ValueSerializer::WriteUint32(uint32_t value) {
  WriteVarint<uint32_t>(value);
}

void ValueSerializer::WriteUint64(uint64_t value) {
  WriteVarint<uint64_t>(value);
}

332 333 334 335 336 337 338 339
std::pair<uint8_t*, size_t> ValueSerializer::Release() {
  auto result = std::make_pair(buffer_, buffer_size_);
  buffer_ = nullptr;
  buffer_size_ = 0;
  buffer_capacity_ = 0;
  return result;
}

340 341 342
void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id,
                                          Handle<JSArrayBuffer> array_buffer) {
  DCHECK(!array_buffer_transfer_map_.Find(array_buffer));
343
  DCHECK(!array_buffer->is_shared());
344 345 346
  array_buffer_transfer_map_.Set(array_buffer, transfer_id);
}

347
Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
348 349 350 351 352
  // There is no sense in trying to proceed if we've previously run out of
  // memory. Bail immediately, as this likely implies that some write has
  // previously failed and so the buffer is corrupt.
  if (V8_UNLIKELY(out_of_memory_)) return ThrowIfOutOfMemory();

353 354
  if (object->IsSmi()) {
    WriteSmi(Smi::cast(*object));
355
    return ThrowIfOutOfMemory();
356
  }
357 358 359 360 361

  DCHECK(object->IsHeapObject());
  switch (HeapObject::cast(*object)->map()->instance_type()) {
    case ODDBALL_TYPE:
      WriteOddball(Oddball::cast(*object));
362
      return ThrowIfOutOfMemory();
363
    case HEAP_NUMBER_TYPE:
364
      WriteHeapNumber(HeapNumber::cast(*object));
365
      return ThrowIfOutOfMemory();
366 367 368
    case MUTABLE_HEAP_NUMBER_TYPE:
      WriteMutableHeapNumber(MutableHeapNumber::cast(*object));
      return ThrowIfOutOfMemory();
369 370 371
    case BIGINT_TYPE:
      WriteBigInt(BigInt::cast(*object));
      return ThrowIfOutOfMemory();
372 373 374 375 376 377 378 379
    case JS_TYPED_ARRAY_TYPE:
    case JS_DATA_VIEW_TYPE: {
      // Despite being JSReceivers, these have their wrapped buffer serialized
      // first. That makes this logic a little quirky, because it needs to
      // happen before we assign object IDs.
      // TODO(jbroman): It may be possible to avoid materializing a typed
      // array's buffer here.
      Handle<JSArrayBufferView> view = Handle<JSArrayBufferView>::cast(object);
380
      if (!id_map_.Find(view) && !treat_array_buffer_views_as_host_objects_) {
381 382 383 384 385 386 387 388
        Handle<JSArrayBuffer> buffer(
            view->IsJSTypedArray()
                ? Handle<JSTypedArray>::cast(view)->GetBuffer()
                : handle(JSArrayBuffer::cast(view->buffer()), isolate_));
        if (!WriteJSReceiver(buffer).FromMaybe(false)) return Nothing<bool>();
      }
      return WriteJSReceiver(view);
    }
389
    default:
390 391
      if (object->IsString()) {
        WriteString(Handle<String>::cast(object));
392
        return ThrowIfOutOfMemory();
393 394
      } else if (object->IsJSReceiver()) {
        return WriteJSReceiver(Handle<JSReceiver>::cast(object));
395 396 397
      } else {
        ThrowDataCloneError(MessageTemplate::kDataCloneError, object);
        return Nothing<bool>();
398
      }
399 400 401
  }
}

402
void ValueSerializer::WriteOddball(Oddball oddball) {
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
  SerializationTag tag = SerializationTag::kUndefined;
  switch (oddball->kind()) {
    case Oddball::kUndefined:
      tag = SerializationTag::kUndefined;
      break;
    case Oddball::kFalse:
      tag = SerializationTag::kFalse;
      break;
    case Oddball::kTrue:
      tag = SerializationTag::kTrue;
      break;
    case Oddball::kNull:
      tag = SerializationTag::kNull;
      break;
    default:
      UNREACHABLE();
      break;
  }
  WriteTag(tag);
}

424
void ValueSerializer::WriteSmi(Smi smi) {
425 426 427 428 429
  static_assert(kSmiValueSize <= 32, "Expected SMI <= 32 bits.");
  WriteTag(SerializationTag::kInt32);
  WriteZigZag<int32_t>(smi->value());
}

430
void ValueSerializer::WriteHeapNumber(HeapNumber number) {
431 432 433 434
  WriteTag(SerializationTag::kDouble);
  WriteDouble(number->value());
}

435
void ValueSerializer::WriteMutableHeapNumber(MutableHeapNumber number) {
436 437 438 439
  WriteTag(SerializationTag::kDouble);
  WriteDouble(number->value());
}

440
void ValueSerializer::WriteBigInt(BigInt bigint) {
441 442 443 444
  WriteTag(SerializationTag::kBigInt);
  WriteBigIntContents(bigint);
}

445
void ValueSerializer::WriteString(Handle<String> string) {
446
  string = String::Flatten(isolate_, string);
447
  DisallowHeapAllocation no_gc;
448
  String::FlatContent flat = string->GetFlatContent(no_gc);
449 450 451
  DCHECK(flat.IsFlat());
  if (flat.IsOneByte()) {
    Vector<const uint8_t> chars = flat.ToOneByteVector();
452 453
    WriteTag(SerializationTag::kOneByteString);
    WriteOneByteString(chars);
454 455 456 457
  } else if (flat.IsTwoByte()) {
    Vector<const uc16> chars = flat.ToUC16Vector();
    uint32_t byte_length = chars.length() * sizeof(uc16);
    // The existing reading code expects 16-byte strings to be aligned.
458
    if ((buffer_size_ + 1 + BytesNeededForVarint(byte_length)) & 1)
459 460 461 462 463 464 465 466
      WriteTag(SerializationTag::kPadding);
    WriteTag(SerializationTag::kTwoByteString);
    WriteTwoByteString(chars);
  } else {
    UNREACHABLE();
  }
}

467 468 469 470 471 472
Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
  // If the object has already been serialized, just write its ID.
  uint32_t* id_map_entry = id_map_.Get(receiver);
  if (uint32_t id = *id_map_entry) {
    WriteTag(SerializationTag::kObjectReference);
    WriteVarint(id - 1);
473
    return ThrowIfOutOfMemory();
474 475 476 477 478 479 480 481
  }

  // Otherwise, allocate an ID for it.
  uint32_t id = next_id_++;
  *id_map_entry = id + 1;

  // Eliminate callable and exotic objects, which should not be serialized.
  InstanceType instance_type = receiver->map()->instance_type();
482
  if (receiver->IsCallable() || (IsSpecialReceiverInstanceType(instance_type) &&
483
                                 instance_type != JS_SPECIAL_API_OBJECT_TYPE)) {
484
    ThrowDataCloneError(MessageTemplate::kDataCloneError, receiver);
485 486 487 488
    return Nothing<bool>();
  }

  // If we are at the end of the stack, abort. This function may recurse.
489
  STACK_CHECK(isolate_, Nothing<bool>());
490 491 492

  HandleScope scope(isolate_);
  switch (instance_type) {
493 494
    case JS_ARRAY_TYPE:
      return WriteJSArray(Handle<JSArray>::cast(receiver));
495
    case JS_OBJECT_TYPE:
496 497
    case JS_API_OBJECT_TYPE: {
      Handle<JSObject> js_object = Handle<JSObject>::cast(receiver);
498
      if (JSObject::GetEmbedderFieldCount(js_object->map())) {
499 500 501 502
        return WriteHostObject(js_object);
      } else {
        return WriteJSObject(js_object);
      }
503
    }
504 505
    case JS_SPECIAL_API_OBJECT_TYPE:
      return WriteHostObject(Handle<JSObject>::cast(receiver));
506 507
    case JS_DATE_TYPE:
      WriteJSDate(JSDate::cast(*receiver));
508
      return ThrowIfOutOfMemory();
509 510
    case JS_VALUE_TYPE:
      return WriteJSValue(Handle<JSValue>::cast(receiver));
511 512
    case JS_REGEXP_TYPE:
      WriteJSRegExp(JSRegExp::cast(*receiver));
513
      return ThrowIfOutOfMemory();
514 515 516 517
    case JS_MAP_TYPE:
      return WriteJSMap(Handle<JSMap>::cast(receiver));
    case JS_SET_TYPE:
      return WriteJSSet(Handle<JSSet>::cast(receiver));
518
    case JS_ARRAY_BUFFER_TYPE:
519
      return WriteJSArrayBuffer(Handle<JSArrayBuffer>::cast(receiver));
520 521 522
    case JS_TYPED_ARRAY_TYPE:
    case JS_DATA_VIEW_TYPE:
      return WriteJSArrayBufferView(JSArrayBufferView::cast(*receiver));
523 524 525
    case WASM_MODULE_TYPE: {
      auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
      if (!FLAG_wasm_disable_structured_cloning || enabled_features.threads) {
526 527
        // Only write WebAssembly modules if not disabled by a flag.
        return WriteWasmModule(Handle<WasmModuleObject>::cast(receiver));
528
      }
529
      break;
530
    }
531 532 533
    case WASM_MEMORY_TYPE: {
      auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
      if (enabled_features.threads) {
534 535
        return WriteWasmMemory(Handle<WasmMemoryObject>::cast(receiver));
      }
536
      break;
537
    }
538
    default:
539
      break;
540
  }
541 542

  ThrowDataCloneError(MessageTemplate::kDataCloneError, receiver);
543 544 545 546
  return Nothing<bool>();
}

Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) {
547
  DCHECK(!object->map()->IsCustomElementsReceiverMap());
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
  const bool can_serialize_fast =
      object->HasFastProperties() && object->elements()->length() == 0;
  if (!can_serialize_fast) return WriteJSObjectSlow(object);

  Handle<Map> map(object->map(), isolate_);
  WriteTag(SerializationTag::kBeginJSObject);

  // Write out fast properties as long as they are only data properties and the
  // map doesn't change.
  uint32_t properties_written = 0;
  bool map_changed = false;
  for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
    Handle<Name> key(map->instance_descriptors()->GetKey(i), isolate_);
    if (!key->IsString()) continue;
    PropertyDetails details = map->instance_descriptors()->GetDetails(i);
    if (details.IsDontEnum()) continue;

    Handle<Object> value;
    if (V8_LIKELY(!map_changed)) map_changed = *map == object->map();
567 568
    if (V8_LIKELY(!map_changed && details.location() == kField)) {
      DCHECK_EQ(kData, details.kind());
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
      FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
      value = JSObject::FastPropertyAt(object, details.representation(),
                                       field_index);
    } else {
      // This logic should essentially match WriteJSObjectPropertiesSlow.
      // If the property is no longer found, do not serialize it.
      // This could happen if a getter deleted the property.
      LookupIterator it(isolate_, object, key, LookupIterator::OWN);
      if (!it.IsFound()) continue;
      if (!Object::GetProperty(&it).ToHandle(&value)) return Nothing<bool>();
    }

    if (!WriteObject(key).FromMaybe(false) ||
        !WriteObject(value).FromMaybe(false)) {
      return Nothing<bool>();
    }
    properties_written++;
  }

  WriteTag(SerializationTag::kEndJSObject);
  WriteVarint<uint32_t>(properties_written);
590
  return ThrowIfOutOfMemory();
591 592 593
}

Maybe<bool> ValueSerializer::WriteJSObjectSlow(Handle<JSObject> object) {
594 595
  WriteTag(SerializationTag::kBeginJSObject);
  Handle<FixedArray> keys;
596
  uint32_t properties_written = 0;
597 598 599
  if (!KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
                               ENUMERABLE_STRINGS)
           .ToHandle(&keys) ||
600
      !WriteJSObjectPropertiesSlow(object, keys).To(&properties_written)) {
601 602 603 604
    return Nothing<bool>();
  }
  WriteTag(SerializationTag::kEndJSObject);
  WriteVarint<uint32_t>(properties_written);
605
  return ThrowIfOutOfMemory();
606 607
}

608 609 610 611 612 613 614 615 616 617 618 619
Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
  uint32_t length = 0;
  bool valid_length = array->length()->ToArrayLength(&length);
  DCHECK(valid_length);
  USE(valid_length);

  // To keep things simple, for now we decide between dense and sparse
  // serialization based on elements kind. A more principled heuristic could
  // count the elements, but would need to take care to note which indices
  // existed (as only indices which were enumerable own properties at this point
  // should be serialized).
  const bool should_serialize_densely =
620
      array->HasFastElements() && !array->HasHoleyElements();
621 622

  if (should_serialize_densely) {
623
    DCHECK_LE(length, static_cast<uint32_t>(FixedArray::kMaxLength));
624 625
    WriteTag(SerializationTag::kBeginDenseJSArray);
    WriteVarint<uint32_t>(length);
626 627
    uint32_t i = 0;

628
    // Fast paths. Note that PACKED_ELEMENTS in particular can bail due to the
629 630
    // structure of the elements changing.
    switch (array->GetElementsKind()) {
631
      case PACKED_SMI_ELEMENTS: {
632 633 634 635 636
        Handle<FixedArray> elements(FixedArray::cast(array->elements()),
                                    isolate_);
        for (; i < length; i++) WriteSmi(Smi::cast(elements->get(i)));
        break;
      }
637
      case PACKED_DOUBLE_ELEMENTS: {
638 639 640
        // Elements are empty_fixed_array, not a FixedDoubleArray, if the array
        // is empty. No elements to encode in this case anyhow.
        if (length == 0) break;
641 642 643 644 645 646 647 648
        Handle<FixedDoubleArray> elements(
            FixedDoubleArray::cast(array->elements()), isolate_);
        for (; i < length; i++) {
          WriteTag(SerializationTag::kDouble);
          WriteDouble(elements->get_scalar(i));
        }
        break;
      }
649
      case PACKED_ELEMENTS: {
650 651 652
        Handle<Object> old_length(array->length(), isolate_);
        for (; i < length; i++) {
          if (array->length() != *old_length ||
653
              array->GetElementsKind() != PACKED_ELEMENTS) {
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
            // Fall back to slow path.
            break;
          }
          Handle<Object> element(FixedArray::cast(array->elements())->get(i),
                                 isolate_);
          if (!WriteObject(element).FromMaybe(false)) return Nothing<bool>();
        }
        break;
      }
      default:
        break;
    }

    // If there are elements remaining, serialize them slowly.
    for (; i < length; i++) {
669 670 671 672 673
      // Serializing the array's elements can have arbitrary side effects, so we
      // cannot rely on still having fast elements, even if it did to begin
      // with.
      Handle<Object> element;
      LookupIterator it(isolate_, array, i, array, LookupIterator::OWN);
674 675 676 677 678 679 680
      if (!it.IsFound()) {
        // This can happen in the case where an array that was originally dense
        // became sparse during serialization. It's too late to switch to the
        // sparse format, but we can mark the elements as absent.
        WriteTag(SerializationTag::kTheHole);
        continue;
      }
681 682 683 684 685
      if (!Object::GetProperty(&it).ToHandle(&element) ||
          !WriteObject(element).FromMaybe(false)) {
        return Nothing<bool>();
      }
    }
686

687 688 689 690 691 692 693 694
    KeyAccumulator accumulator(isolate_, KeyCollectionMode::kOwnOnly,
                               ENUMERABLE_STRINGS);
    if (!accumulator.CollectOwnPropertyNames(array, array).FromMaybe(false)) {
      return Nothing<bool>();
    }
    Handle<FixedArray> keys =
        accumulator.GetKeys(GetKeysConversion::kConvertToString);
    uint32_t properties_written;
695
    if (!WriteJSObjectPropertiesSlow(array, keys).To(&properties_written)) {
696 697 698 699 700 701 702 703 704
      return Nothing<bool>();
    }
    WriteTag(SerializationTag::kEndDenseJSArray);
    WriteVarint<uint32_t>(properties_written);
    WriteVarint<uint32_t>(length);
  } else {
    WriteTag(SerializationTag::kBeginSparseJSArray);
    WriteVarint<uint32_t>(length);
    Handle<FixedArray> keys;
705
    uint32_t properties_written = 0;
706 707 708
    if (!KeyAccumulator::GetKeys(array, KeyCollectionMode::kOwnOnly,
                                 ENUMERABLE_STRINGS)
             .ToHandle(&keys) ||
709
        !WriteJSObjectPropertiesSlow(array, keys).To(&properties_written)) {
710 711 712 713 714 715
      return Nothing<bool>();
    }
    WriteTag(SerializationTag::kEndSparseJSArray);
    WriteVarint<uint32_t>(properties_written);
    WriteVarint<uint32_t>(length);
  }
716
  return ThrowIfOutOfMemory();
717 718
}

719
void ValueSerializer::WriteJSDate(JSDate date) {
720 721 722 723
  WriteTag(SerializationTag::kDate);
  WriteDouble(date->value()->Number());
}

724 725 726 727 728 729 730 731 732
Maybe<bool> ValueSerializer::WriteJSValue(Handle<JSValue> value) {
  Object* inner_value = value->value();
  if (inner_value->IsTrue(isolate_)) {
    WriteTag(SerializationTag::kTrueObject);
  } else if (inner_value->IsFalse(isolate_)) {
    WriteTag(SerializationTag::kFalseObject);
  } else if (inner_value->IsNumber()) {
    WriteTag(SerializationTag::kNumberObject);
    WriteDouble(inner_value->Number());
733 734 735
  } else if (inner_value->IsBigInt()) {
    WriteTag(SerializationTag::kBigIntObject);
    WriteBigIntContents(BigInt::cast(inner_value));
736 737
  } else if (inner_value->IsString()) {
    WriteTag(SerializationTag::kStringObject);
738
    WriteString(handle(String::cast(inner_value), isolate_));
739 740
  } else {
    DCHECK(inner_value->IsSymbol());
741
    ThrowDataCloneError(MessageTemplate::kDataCloneError, value);
742 743
    return Nothing<bool>();
  }
744
  return ThrowIfOutOfMemory();
745 746
}

747
void ValueSerializer::WriteJSRegExp(JSRegExp regexp) {
748
  WriteTag(SerializationTag::kRegExp);
749
  WriteString(handle(regexp->Pattern(), isolate_));
750 751 752
  WriteVarint(static_cast<uint32_t>(regexp->GetFlags()));
}

753 754
Maybe<bool> ValueSerializer::WriteJSMap(Handle<JSMap> map) {
  // First copy the key-value pairs, since getters could mutate them.
755
  Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table()), isolate_);
756 757 758 759
  int length = table->NumberOfElements() * 2;
  Handle<FixedArray> entries = isolate_->factory()->NewFixedArray(length);
  {
    DisallowHeapAllocation no_gc;
760
    Oddball the_hole = ReadOnlyRoots(isolate_).the_hole_value();
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
    int capacity = table->UsedCapacity();
    int result_index = 0;
    for (int i = 0; i < capacity; i++) {
      Object* key = table->KeyAt(i);
      if (key == the_hole) continue;
      entries->set(result_index++, key);
      entries->set(result_index++, table->ValueAt(i));
    }
    DCHECK_EQ(result_index, length);
  }

  // Then write it out.
  WriteTag(SerializationTag::kBeginJSMap);
  for (int i = 0; i < length; i++) {
    if (!WriteObject(handle(entries->get(i), isolate_)).FromMaybe(false)) {
      return Nothing<bool>();
    }
  }
  WriteTag(SerializationTag::kEndJSMap);
  WriteVarint<uint32_t>(length);
781
  return ThrowIfOutOfMemory();
782 783 784 785
}

Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) {
  // First copy the element pointers, since getters could mutate them.
786
  Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()), isolate_);
787 788 789 790
  int length = table->NumberOfElements();
  Handle<FixedArray> entries = isolate_->factory()->NewFixedArray(length);
  {
    DisallowHeapAllocation no_gc;
791
    Oddball the_hole = ReadOnlyRoots(isolate_).the_hole_value();
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
    int capacity = table->UsedCapacity();
    int result_index = 0;
    for (int i = 0; i < capacity; i++) {
      Object* key = table->KeyAt(i);
      if (key == the_hole) continue;
      entries->set(result_index++, key);
    }
    DCHECK_EQ(result_index, length);
  }

  // Then write it out.
  WriteTag(SerializationTag::kBeginJSSet);
  for (int i = 0; i < length; i++) {
    if (!WriteObject(handle(entries->get(i), isolate_)).FromMaybe(false)) {
      return Nothing<bool>();
    }
  }
  WriteTag(SerializationTag::kEndJSSet);
  WriteVarint<uint32_t>(length);
811
  return ThrowIfOutOfMemory();
812 813
}

814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
Maybe<bool> ValueSerializer::WriteJSArrayBuffer(
    Handle<JSArrayBuffer> array_buffer) {
  if (array_buffer->is_shared()) {
    if (!delegate_) {
      ThrowDataCloneError(MessageTemplate::kDataCloneError, array_buffer);
      return Nothing<bool>();
    }

    v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
    Maybe<uint32_t> index = delegate_->GetSharedArrayBufferId(
        v8_isolate, Utils::ToLocalShared(array_buffer));
    RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>());

    WriteTag(SerializationTag::kSharedArrayBuffer);
    WriteVarint(index.FromJust());
829
    return ThrowIfOutOfMemory();
830 831
  }

832 833
  uint32_t* transfer_entry = array_buffer_transfer_map_.Find(array_buffer);
  if (transfer_entry) {
834
    WriteTag(SerializationTag::kArrayBufferTransfer);
835
    WriteVarint(*transfer_entry);
836
    return ThrowIfOutOfMemory();
837
  }
838 839
  if (array_buffer->was_detached()) {
    ThrowDataCloneError(MessageTemplate::kDataCloneErrorDetachedArrayBuffer);
840 841
    return Nothing<bool>();
  }
842
  double byte_length = array_buffer->byte_length();
843
  if (byte_length > std::numeric_limits<uint32_t>::max()) {
844
    ThrowDataCloneError(MessageTemplate::kDataCloneError, array_buffer);
845 846 847 848 849
    return Nothing<bool>();
  }
  WriteTag(SerializationTag::kArrayBuffer);
  WriteVarint<uint32_t>(byte_length);
  WriteRawBytes(array_buffer->backing_store(), byte_length);
850
  return ThrowIfOutOfMemory();
851 852
}

853
Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView view) {
854 855 856
  if (treat_array_buffer_views_as_host_objects_) {
    return WriteHostObject(handle(view, isolate_));
  }
857 858 859 860
  WriteTag(SerializationTag::kArrayBufferView);
  ArrayBufferViewTag tag = ArrayBufferViewTag::kInt8Array;
  if (view->IsJSTypedArray()) {
    switch (JSTypedArray::cast(view)->type()) {
861 862 863
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
  case kExternal##Type##Array:                    \
    tag = ArrayBufferViewTag::k##Type##Array;     \
864 865 866 867 868 869 870 871 872
    break;
      TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
    }
  } else {
    DCHECK(view->IsJSDataView());
    tag = ArrayBufferViewTag::kDataView;
  }
  WriteVarint(static_cast<uint8_t>(tag));
873 874
  WriteVarint(static_cast<uint32_t>(view->byte_offset()));
  WriteVarint(static_cast<uint32_t>(view->byte_length()));
875
  return ThrowIfOutOfMemory();
876 877
}

878
Maybe<bool> ValueSerializer::WriteWasmModule(Handle<WasmModuleObject> object) {
879
  if (delegate_ != nullptr) {
880
    // TODO(titzer): introduce a Utils::ToLocal for WasmModuleObject.
881 882
    Maybe<uint32_t> transfer_id = delegate_->GetWasmModuleTransferId(
        reinterpret_cast<v8::Isolate*>(isolate_),
883
        v8::Local<v8::WasmModuleObject>::Cast(
884
            Utils::ToLocal(Handle<JSObject>::cast(object))));
885 886 887 888 889 890 891 892 893
    RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>());
    uint32_t id = 0;
    if (transfer_id.To(&id)) {
      WriteTag(SerializationTag::kWasmModuleTransfer);
      WriteVarint<uint32_t>(id);
      return Just(true);
    }
  }

894 895 896 897
  WasmEncodingTag encoding_tag = WasmEncodingTag::kRawBytes;
  WriteTag(SerializationTag::kWasmModule);
  WriteRawBytes(&encoding_tag, sizeof(encoding_tag));

898 899 900
  wasm::NativeModule* native_module = object->native_module();
  Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
  WriteVarint<uint32_t>(static_cast<uint32_t>(wire_bytes.size()));
901
  uint8_t* destination;
902 903
  if (ReserveRawBytes(wire_bytes.size()).To(&destination)) {
    memcpy(destination, wire_bytes.start(), wire_bytes.size());
904
  }
905

906
  wasm::WasmSerializer wasm_serializer(native_module);
907
  size_t module_size = wasm_serializer.GetSerializedNativeModuleSize();
908 909 910 911
  CHECK_GE(std::numeric_limits<uint32_t>::max(), module_size);
  WriteVarint<uint32_t>(static_cast<uint32_t>(module_size));
  uint8_t* module_buffer;
  if (ReserveRawBytes(module_size).To(&module_buffer)) {
912
    if (!wasm_serializer.SerializeNativeModule({module_buffer, module_size})) {
913 914 915
      return Nothing<bool>();
    }
  }
916
  return ThrowIfOutOfMemory();
917 918
}

919 920 921 922 923 924 925 926 927 928 929
Maybe<bool> ValueSerializer::WriteWasmMemory(Handle<WasmMemoryObject> object) {
  if (!object->array_buffer()->is_shared()) {
    ThrowDataCloneError(MessageTemplate::kDataCloneError, object);
    return Nothing<bool>();
  }

  WriteTag(SerializationTag::kWasmMemoryTransfer);
  WriteZigZag<int32_t>(object->maximum_pages());
  return WriteJSReceiver(Handle<JSReceiver>(object->array_buffer(), isolate_));
}

930
Maybe<bool> ValueSerializer::WriteHostObject(Handle<JSObject> object) {
931
  WriteTag(SerializationTag::kHostObject);
932 933 934 935 936 937 938 939 940
  if (!delegate_) {
    isolate_->Throw(*isolate_->factory()->NewError(
        isolate_->error_function(), MessageTemplate::kDataCloneError, object));
    return Nothing<bool>();
  }
  v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
  Maybe<bool> result =
      delegate_->WriteHostObject(v8_isolate, Utils::ToLocal(object));
  RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>());
941
  USE(result);
942
  DCHECK(!result.IsNothing());
943 944
  DCHECK(result.ToChecked());
  return ThrowIfOutOfMemory();
945 946
}

947
Maybe<uint32_t> ValueSerializer::WriteJSObjectPropertiesSlow(
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
    Handle<JSObject> object, Handle<FixedArray> keys) {
  uint32_t properties_written = 0;
  int length = keys->length();
  for (int i = 0; i < length; i++) {
    Handle<Object> key(keys->get(i), isolate_);

    bool success;
    LookupIterator it = LookupIterator::PropertyOrElement(
        isolate_, object, key, &success, LookupIterator::OWN);
    DCHECK(success);
    Handle<Object> value;
    if (!Object::GetProperty(&it).ToHandle(&value)) return Nothing<uint32_t>();

    // If the property is no longer found, do not serialize it.
    // This could happen if a getter deleted the property.
    if (!it.IsFound()) continue;

    if (!WriteObject(key).FromMaybe(false) ||
        !WriteObject(value).FromMaybe(false)) {
      return Nothing<uint32_t>();
    }

    properties_written++;
  }
  return Just(properties_written);
}

975
void ValueSerializer::ThrowDataCloneError(MessageTemplate template_index) {
976 977 978 979
  return ThrowDataCloneError(template_index,
                             isolate_->factory()->empty_string());
}

980 981 982 983 984 985 986 987
Maybe<bool> ValueSerializer::ThrowIfOutOfMemory() {
  if (out_of_memory_) {
    ThrowDataCloneError(MessageTemplate::kDataCloneErrorOutOfMemory);
    return Nothing<bool>();
  }
  return Just(true);
}

988 989
void ValueSerializer::ThrowDataCloneError(MessageTemplate index,
                                          Handle<Object> arg0) {
990
  Handle<String> message =
991
      MessageFormatter::FormatMessage(isolate_, index, arg0);
992 993 994 995 996 997
  if (delegate_) {
    delegate_->ThrowDataCloneError(Utils::ToLocal(message));
  } else {
    isolate_->Throw(
        *isolate_->factory()->NewError(isolate_->error_function(), message));
  }
998 999 1000
  if (isolate_->has_scheduled_exception()) {
    isolate_->PromoteScheduledException();
  }
1001 1002
}

1003
ValueDeserializer::ValueDeserializer(Isolate* isolate,
1004 1005
                                     Vector<const uint8_t> data,
                                     v8::ValueDeserializer::Delegate* delegate)
1006
    : isolate_(isolate),
1007
      delegate_(delegate),
1008
      position_(data.start()),
1009
      end_(data.start() + data.length()),
1010
      pretenure_(data.length() > kPretenureThreshold ? TENURED : NOT_TENURED),
1011 1012
      id_map_(isolate->global_handles()->Create(
          ReadOnlyRoots(isolate_).empty_fixed_array())) {}
1013

1014
ValueDeserializer::~ValueDeserializer() {
1015
  GlobalHandles::Destroy(id_map_.location());
1016 1017 1018 1019 1020

  Handle<Object> transfer_map_handle;
  if (array_buffer_transfer_map_.ToHandle(&transfer_map_handle)) {
    GlobalHandles::Destroy(transfer_map_handle.location());
  }
1021
}
1022 1023 1024 1025 1026

Maybe<bool> ValueDeserializer::ReadHeader() {
  if (position_ < end_ &&
      *position_ == static_cast<uint8_t>(SerializationTag::kVersion)) {
    ReadTag().ToChecked();
1027 1028 1029 1030 1031
    if (!ReadVarint<uint32_t>().To(&version_) || version_ > kLatestVersion) {
      isolate_->Throw(*isolate_->factory()->NewError(
          MessageTemplate::kDataCloneDeserializationVersionError));
      return Nothing<bool>();
    }
1032 1033 1034 1035
  }
  return Just(true);
}

1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
Maybe<SerializationTag> ValueDeserializer::PeekTag() const {
  const uint8_t* peek_position = position_;
  SerializationTag tag;
  do {
    if (peek_position >= end_) return Nothing<SerializationTag>();
    tag = static_cast<SerializationTag>(*peek_position);
    peek_position++;
  } while (tag == SerializationTag::kPadding);
  return Just(tag);
}

1047 1048 1049 1050 1051 1052
void ValueDeserializer::ConsumeTag(SerializationTag peeked_tag) {
  SerializationTag actual_tag = ReadTag().ToChecked();
  DCHECK(actual_tag == peeked_tag);
  USE(actual_tag);
}

1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
Maybe<SerializationTag> ValueDeserializer::ReadTag() {
  SerializationTag tag;
  do {
    if (position_ >= end_) return Nothing<SerializationTag>();
    tag = static_cast<SerializationTag>(*position_);
    position_++;
  } while (tag == SerializationTag::kPadding);
  return Just(tag);
}

template <typename T>
Maybe<T> ValueDeserializer::ReadVarint() {
  // Reads an unsigned integer as a base-128 varint.
  // The number is written, 7 bits at a time, from the least significant to the
  // most significant 7 bits. Each byte, except the last, has the MSB set.
  // If the varint is larger than T, any more significant bits are discarded.
  // See also https://developers.google.com/protocol-buffers/docs/encoding
  static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
                "Only unsigned integer types can be read as varints.");
  T value = 0;
  unsigned shift = 0;
  bool has_another_byte;
  do {
    if (position_ >= end_) return Nothing<T>();
    uint8_t byte = *position_;
    if (V8_LIKELY(shift < sizeof(T) * 8)) {
1079
      value |= static_cast<T>(byte & 0x7F) << shift;
1080 1081 1082 1083 1084 1085 1086 1087
      shift += 7;
    }
    has_another_byte = byte & 0x80;
    position_++;
  } while (has_another_byte);
  return Just(value);
}

1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
template <typename T>
Maybe<T> ValueDeserializer::ReadZigZag() {
  // Writes a signed integer as a varint using ZigZag encoding (i.e. 0 is
  // encoded as 0, -1 as 1, 1 as 2, -2 as 3, and so on).
  // See also https://developers.google.com/protocol-buffers/docs/encoding
  static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
                "Only signed integer types can be read as zigzag.");
  using UnsignedT = typename std::make_unsigned<T>::type;
  UnsignedT unsigned_value;
  if (!ReadVarint<UnsignedT>().To(&unsigned_value)) return Nothing<T>();
  return Just(static_cast<T>((unsigned_value >> 1) ^
                             -static_cast<T>(unsigned_value & 1)));
}

Maybe<double> ValueDeserializer::ReadDouble() {
  // Warning: this uses host endianness.
  if (position_ > end_ - sizeof(double)) return Nothing<double>();
  double value;
  memcpy(&value, position_, sizeof(double));
  position_ += sizeof(double);
  if (std::isnan(value)) value = std::numeric_limits<double>::quiet_NaN();
  return Just(value);
}

1112 1113 1114 1115 1116 1117 1118
Maybe<Vector<const uint8_t>> ValueDeserializer::ReadRawBytes(int size) {
  if (size > end_ - position_) return Nothing<Vector<const uint8_t>>();
  const uint8_t* start = position_;
  position_ += size;
  return Just(Vector<const uint8_t>(start, size));
}

1119 1120 1121 1122 1123 1124
bool ValueDeserializer::ReadUint32(uint32_t* value) {
  return ReadVarint<uint32_t>().To(value);
}

bool ValueDeserializer::ReadUint64(uint64_t* value) {
  return ReadVarint<uint64_t>().To(value);
1125 1126 1127 1128
}

bool ValueDeserializer::ReadDouble(double* value) {
  return ReadDouble().To(value);
1129 1130 1131 1132 1133 1134 1135 1136 1137
}

bool ValueDeserializer::ReadRawBytes(size_t length, const void** data) {
  if (length > static_cast<size_t>(end_ - position_)) return false;
  *data = position_;
  position_ += length;
  return true;
}

1138 1139 1140
void ValueDeserializer::TransferArrayBuffer(
    uint32_t transfer_id, Handle<JSArrayBuffer> array_buffer) {
  if (array_buffer_transfer_map_.is_null()) {
1141 1142
    array_buffer_transfer_map_ = isolate_->global_handles()->Create(
        *SimpleNumberDictionary::New(isolate_, 0));
1143
  }
1144
  Handle<SimpleNumberDictionary> dictionary =
1145
      array_buffer_transfer_map_.ToHandleChecked();
1146 1147
  Handle<SimpleNumberDictionary> new_dictionary = SimpleNumberDictionary::Set(
      isolate_, dictionary, transfer_id, array_buffer);
1148
  if (!new_dictionary.is_identical_to(dictionary)) {
1149
    GlobalHandles::Destroy(dictionary.location());
1150 1151
    array_buffer_transfer_map_ =
        isolate_->global_handles()->Create(*new_dictionary);
1152 1153 1154
  }
}

1155
MaybeHandle<Object> ValueDeserializer::ReadObject() {
1156
  DisallowJavascriptExecution no_js(isolate_);
1157 1158 1159
  // If we are at the end of the stack, abort. This function may recurse.
  STACK_CHECK(isolate_, MaybeHandle<Object>());

1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
  MaybeHandle<Object> result = ReadObjectInternal();

  // ArrayBufferView is special in that it consumes the value before it, even
  // after format version 0.
  Handle<Object> object;
  SerializationTag tag;
  if (result.ToHandle(&object) && V8_UNLIKELY(object->IsJSArrayBuffer()) &&
      PeekTag().To(&tag) && tag == SerializationTag::kArrayBufferView) {
    ConsumeTag(SerializationTag::kArrayBufferView);
    result = ReadJSArrayBufferView(Handle<JSArrayBuffer>::cast(object));
  }

1172 1173 1174 1175 1176
  if (result.is_null() && !isolate_->has_pending_exception()) {
    isolate_->Throw(*isolate_->factory()->NewError(
        MessageTemplate::kDataCloneDeserializationError));
  }

1177 1178 1179 1180
  return result;
}

MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
  SerializationTag tag;
  if (!ReadTag().To(&tag)) return MaybeHandle<Object>();
  switch (tag) {
    case SerializationTag::kVerifyObjectCount:
      // Read the count and ignore it.
      if (ReadVarint<uint32_t>().IsNothing()) return MaybeHandle<Object>();
      return ReadObject();
    case SerializationTag::kUndefined:
      return isolate_->factory()->undefined_value();
    case SerializationTag::kNull:
      return isolate_->factory()->null_value();
    case SerializationTag::kTrue:
      return isolate_->factory()->true_value();
    case SerializationTag::kFalse:
      return isolate_->factory()->false_value();
1196 1197 1198
    case SerializationTag::kInt32: {
      Maybe<int32_t> number = ReadZigZag<int32_t>();
      if (number.IsNothing()) return MaybeHandle<Object>();
1199 1200
      return isolate_->factory()->NewNumberFromInt(number.FromJust(),
                                                   pretenure_);
1201 1202 1203 1204
    }
    case SerializationTag::kUint32: {
      Maybe<uint32_t> number = ReadVarint<uint32_t>();
      if (number.IsNothing()) return MaybeHandle<Object>();
1205 1206
      return isolate_->factory()->NewNumberFromUint(number.FromJust(),
                                                    pretenure_);
1207 1208 1209 1210
    }
    case SerializationTag::kDouble: {
      Maybe<double> number = ReadDouble();
      if (number.IsNothing()) return MaybeHandle<Object>();
1211
      return isolate_->factory()->NewNumber(number.FromJust(), pretenure_);
1212
    }
1213 1214
    case SerializationTag::kBigInt:
      return ReadBigInt();
1215 1216
    case SerializationTag::kUtf8String:
      return ReadUtf8String();
1217 1218
    case SerializationTag::kOneByteString:
      return ReadOneByteString();
1219 1220
    case SerializationTag::kTwoByteString:
      return ReadTwoByteString();
1221 1222 1223 1224 1225 1226 1227
    case SerializationTag::kObjectReference: {
      uint32_t id;
      if (!ReadVarint<uint32_t>().To(&id)) return MaybeHandle<Object>();
      return GetObjectWithID(id);
    }
    case SerializationTag::kBeginJSObject:
      return ReadJSObject();
1228 1229 1230 1231
    case SerializationTag::kBeginSparseJSArray:
      return ReadSparseJSArray();
    case SerializationTag::kBeginDenseJSArray:
      return ReadDenseJSArray();
1232 1233
    case SerializationTag::kDate:
      return ReadJSDate();
1234 1235 1236
    case SerializationTag::kTrueObject:
    case SerializationTag::kFalseObject:
    case SerializationTag::kNumberObject:
1237
    case SerializationTag::kBigIntObject:
1238 1239
    case SerializationTag::kStringObject:
      return ReadJSValue(tag);
1240 1241
    case SerializationTag::kRegExp:
      return ReadJSRegExp();
1242 1243 1244 1245
    case SerializationTag::kBeginJSMap:
      return ReadJSMap();
    case SerializationTag::kBeginJSSet:
      return ReadJSSet();
1246
    case SerializationTag::kArrayBuffer: {
1247
      const bool is_shared = false;
1248 1249 1250 1251
      return ReadJSArrayBuffer(is_shared);
    }
    case SerializationTag::kArrayBufferTransfer: {
      return ReadTransferredJSArrayBuffer();
1252
    }
1253
    case SerializationTag::kSharedArrayBuffer: {
1254
      const bool is_shared = true;
1255
      return ReadJSArrayBuffer(is_shared);
1256
    }
1257 1258
    case SerializationTag::kWasmModule:
      return ReadWasmModule();
1259 1260
    case SerializationTag::kWasmModuleTransfer:
      return ReadWasmModuleTransfer();
1261 1262
    case SerializationTag::kWasmMemoryTransfer:
      return ReadWasmMemory();
1263
    case SerializationTag::kHostObject:
1264
      return ReadHostObject();
1265 1266 1267 1268 1269 1270 1271 1272
    default:
      // Before there was an explicit tag for host objects, all unknown tags
      // were delegated to the host.
      if (version_ < 13) {
        position_--;
        return ReadHostObject();
      }
      return MaybeHandle<Object>();
1273 1274 1275
  }
}

1276 1277 1278 1279 1280 1281 1282 1283 1284
MaybeHandle<String> ValueDeserializer::ReadString() {
  if (version_ < 12) return ReadUtf8String();
  Handle<Object> object;
  if (!ReadObject().ToHandle(&object) || !object->IsString()) {
    return MaybeHandle<String>();
  }
  return Handle<String>::cast(object);
}

1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
MaybeHandle<BigInt> ValueDeserializer::ReadBigInt() {
  uint32_t bitfield;
  if (!ReadVarint<uint32_t>().To(&bitfield)) return MaybeHandle<BigInt>();
  int bytelength = BigInt::DigitsByteLengthForBitfield(bitfield);
  Vector<const uint8_t> digits_storage;
  if (!ReadRawBytes(bytelength).To(&digits_storage)) {
    return MaybeHandle<BigInt>();
  }
  return BigInt::FromSerializedDigits(isolate_, bitfield, digits_storage,
                                      pretenure_);
}

1297 1298 1299 1300 1301 1302
MaybeHandle<String> ValueDeserializer::ReadUtf8String() {
  uint32_t utf8_length;
  Vector<const uint8_t> utf8_bytes;
  if (!ReadVarint<uint32_t>().To(&utf8_length) ||
      utf8_length >
          static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
1303
      !ReadRawBytes(utf8_length).To(&utf8_bytes)) {
1304
    return MaybeHandle<String>();
1305
  }
1306
  return isolate_->factory()->NewStringFromUtf8(
1307
      Vector<const char>::cast(utf8_bytes), pretenure_);
1308 1309
}

1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
MaybeHandle<String> ValueDeserializer::ReadOneByteString() {
  uint32_t byte_length;
  Vector<const uint8_t> bytes;
  if (!ReadVarint<uint32_t>().To(&byte_length) ||
      byte_length >
          static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
      !ReadRawBytes(byte_length).To(&bytes)) {
    return MaybeHandle<String>();
  }
  return isolate_->factory()->NewStringFromOneByte(bytes, pretenure_);
}

1322 1323 1324 1325 1326 1327
MaybeHandle<String> ValueDeserializer::ReadTwoByteString() {
  uint32_t byte_length;
  Vector<const uint8_t> bytes;
  if (!ReadVarint<uint32_t>().To(&byte_length) ||
      byte_length >
          static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
1328 1329
      byte_length % sizeof(uc16) != 0 ||
      !ReadRawBytes(byte_length).To(&bytes)) {
1330
    return MaybeHandle<String>();
1331
  }
1332 1333 1334

  // Allocate an uninitialized string so that we can do a raw memcpy into the
  // string on the heap (regardless of alignment).
1335
  if (byte_length == 0) return isolate_->factory()->empty_string();
1336 1337
  Handle<SeqTwoByteString> string;
  if (!isolate_->factory()
1338
           ->NewRawTwoByteString(byte_length / sizeof(uc16), pretenure_)
1339
           .ToHandle(&string)) {
1340
    return MaybeHandle<String>();
1341
  }
1342 1343 1344

  // Copy the bytes directly into the new string.
  // Warning: this uses host endianness.
1345
  DisallowHeapAllocation no_gc;
1346
  memcpy(string->GetChars(no_gc), bytes.begin(), bytes.length());
1347 1348 1349
  return string;
}

1350
bool ValueDeserializer::ReadExpectedString(Handle<String> expected) {
1351
  DisallowHeapAllocation no_gc;
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365
  // In the case of failure, the position in the stream is reset.
  const uint8_t* original_position = position_;

  SerializationTag tag;
  uint32_t byte_length;
  Vector<const uint8_t> bytes;
  if (!ReadTag().To(&tag) || !ReadVarint<uint32_t>().To(&byte_length) ||
      byte_length >
          static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
      !ReadRawBytes(byte_length).To(&bytes)) {
    position_ = original_position;
    return false;
  }

1366
  String::FlatContent flat = expected->GetFlatContent(no_gc);
1367 1368 1369

  // If the bytes are verbatim what is in the flattened string, then the string
  // is successfully consumed.
1370
  if (tag == SerializationTag::kOneByteString && flat.IsOneByte()) {
1371
    Vector<const uint8_t> chars = flat.ToOneByteVector();
1372
    if (byte_length == static_cast<size_t>(chars.length()) &&
1373 1374 1375 1376 1377 1378 1379 1380
        memcmp(bytes.begin(), chars.begin(), byte_length) == 0) {
      return true;
    }
  } else if (tag == SerializationTag::kTwoByteString && flat.IsTwoByte()) {
    Vector<const uc16> chars = flat.ToUC16Vector();
    if (byte_length == static_cast<unsigned>(chars.length()) * sizeof(uc16) &&
        memcmp(bytes.begin(), chars.begin(), byte_length) == 0) {
      return true;
1381 1382 1383 1384 1385 1386 1387
    }
  } else if (tag == SerializationTag::kUtf8String && flat.IsOneByte()) {
    Vector<const uint8_t> chars = flat.ToOneByteVector();
    if (byte_length == static_cast<size_t>(chars.length()) &&
        String::IsAscii(chars.begin(), chars.length()) &&
        memcmp(bytes.begin(), chars.begin(), byte_length) == 0) {
      return true;
1388 1389 1390 1391 1392 1393 1394
    }
  }

  position_ = original_position;
  return false;
}

1395 1396
MaybeHandle<JSObject> ValueDeserializer::ReadJSObject() {
  // If we are at the end of the stack, abort. This function may recurse.
1397
  STACK_CHECK(isolate_, MaybeHandle<JSObject>());
1398 1399 1400 1401

  uint32_t id = next_id_++;
  HandleScope scope(isolate_);
  Handle<JSObject> object =
1402
      isolate_->factory()->NewJSObject(isolate_->object_function(), pretenure_);
1403 1404 1405 1406
  AddObjectWithID(id, object);

  uint32_t num_properties;
  uint32_t expected_num_properties;
1407
  if (!ReadJSObjectProperties(object, SerializationTag::kEndJSObject, true)
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417
           .To(&num_properties) ||
      !ReadVarint<uint32_t>().To(&expected_num_properties) ||
      num_properties != expected_num_properties) {
    return MaybeHandle<JSObject>();
  }

  DCHECK(HasObjectWithID(id));
  return scope.CloseAndEscape(object);
}

1418 1419
MaybeHandle<JSArray> ValueDeserializer::ReadSparseJSArray() {
  // If we are at the end of the stack, abort. This function may recurse.
1420
  STACK_CHECK(isolate_, MaybeHandle<JSArray>());
1421 1422 1423 1424 1425 1426

  uint32_t length;
  if (!ReadVarint<uint32_t>().To(&length)) return MaybeHandle<JSArray>();

  uint32_t id = next_id_++;
  HandleScope scope(isolate_);
1427 1428
  Handle<JSArray> array = isolate_->factory()->NewJSArray(
      0, TERMINAL_FAST_ELEMENTS_KIND, pretenure_);
1429 1430 1431 1432 1433 1434
  JSArray::SetLength(array, length);
  AddObjectWithID(id, array);

  uint32_t num_properties;
  uint32_t expected_num_properties;
  uint32_t expected_length;
1435
  if (!ReadJSObjectProperties(array, SerializationTag::kEndSparseJSArray, false)
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448
           .To(&num_properties) ||
      !ReadVarint<uint32_t>().To(&expected_num_properties) ||
      !ReadVarint<uint32_t>().To(&expected_length) ||
      num_properties != expected_num_properties || length != expected_length) {
    return MaybeHandle<JSArray>();
  }

  DCHECK(HasObjectWithID(id));
  return scope.CloseAndEscape(array);
}

MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() {
  // If we are at the end of the stack, abort. This function may recurse.
1449
  STACK_CHECK(isolate_, MaybeHandle<JSArray>());
1450

1451 1452 1453
  // We shouldn't permit an array larger than the biggest we can request from
  // V8. As an additional sanity check, since each entry will take at least one
  // byte to encode, if there are fewer bytes than that we can also fail fast.
1454
  uint32_t length;
1455 1456 1457 1458 1459
  if (!ReadVarint<uint32_t>().To(&length) ||
      length > static_cast<uint32_t>(FixedArray::kMaxLength) ||
      length > static_cast<size_t>(end_ - position_)) {
    return MaybeHandle<JSArray>();
  }
1460 1461 1462 1463

  uint32_t id = next_id_++;
  HandleScope scope(isolate_);
  Handle<JSArray> array = isolate_->factory()->NewJSArray(
1464
      HOLEY_ELEMENTS, length, length, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE,
1465
      pretenure_);
1466 1467 1468 1469
  AddObjectWithID(id, array);

  Handle<FixedArray> elements(FixedArray::cast(array->elements()), isolate_);
  for (uint32_t i = 0; i < length; i++) {
1470 1471 1472 1473 1474 1475
    SerializationTag tag;
    if (PeekTag().To(&tag) && tag == SerializationTag::kTheHole) {
      ConsumeTag(SerializationTag::kTheHole);
      continue;
    }

1476 1477
    Handle<Object> element;
    if (!ReadObject().ToHandle(&element)) return MaybeHandle<JSArray>();
1478 1479 1480 1481 1482 1483

    // Serialization versions less than 11 encode the hole the same as
    // undefined. For consistency with previous behavior, store these as the
    // hole. Past version 11, undefined means undefined.
    if (version_ < 11 && element->IsUndefined(isolate_)) continue;

1484
    // Safety check.
1485 1486 1487
    if (i >= static_cast<uint32_t>(elements->length())) {
      return MaybeHandle<JSArray>();
    }
1488

1489 1490 1491 1492 1493 1494
    elements->set(i, *element);
  }

  uint32_t num_properties;
  uint32_t expected_num_properties;
  uint32_t expected_length;
1495
  if (!ReadJSObjectProperties(array, SerializationTag::kEndDenseJSArray, false)
1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506
           .To(&num_properties) ||
      !ReadVarint<uint32_t>().To(&expected_num_properties) ||
      !ReadVarint<uint32_t>().To(&expected_length) ||
      num_properties != expected_num_properties || length != expected_length) {
    return MaybeHandle<JSArray>();
  }

  DCHECK(HasObjectWithID(id));
  return scope.CloseAndEscape(array);
}

1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519
MaybeHandle<JSDate> ValueDeserializer::ReadJSDate() {
  double value;
  if (!ReadDouble().To(&value)) return MaybeHandle<JSDate>();
  uint32_t id = next_id_++;
  Handle<JSDate> date;
  if (!JSDate::New(isolate_->date_function(), isolate_->date_function(), value)
           .ToHandle(&date)) {
    return MaybeHandle<JSDate>();
  }
  AddObjectWithID(id, date);
  return date;
}

1520 1521 1522 1523 1524
MaybeHandle<JSValue> ValueDeserializer::ReadJSValue(SerializationTag tag) {
  uint32_t id = next_id_++;
  Handle<JSValue> value;
  switch (tag) {
    case SerializationTag::kTrueObject:
1525 1526
      value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
          isolate_->boolean_function(), pretenure_));
1527
      value->set_value(ReadOnlyRoots(isolate_).true_value());
1528 1529
      break;
    case SerializationTag::kFalseObject:
1530 1531
      value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
          isolate_->boolean_function(), pretenure_));
1532
      value->set_value(ReadOnlyRoots(isolate_).false_value());
1533 1534 1535 1536
      break;
    case SerializationTag::kNumberObject: {
      double number;
      if (!ReadDouble().To(&number)) return MaybeHandle<JSValue>();
1537 1538 1539 1540
      value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
          isolate_->number_function(), pretenure_));
      Handle<Object> number_object =
          isolate_->factory()->NewNumber(number, pretenure_);
1541 1542 1543
      value->set_value(*number_object);
      break;
    }
1544 1545 1546 1547 1548 1549 1550 1551
    case SerializationTag::kBigIntObject: {
      Handle<BigInt> bigint;
      if (!ReadBigInt().ToHandle(&bigint)) return MaybeHandle<JSValue>();
      value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
          isolate_->bigint_function(), pretenure_));
      value->set_value(*bigint);
      break;
    }
1552 1553
    case SerializationTag::kStringObject: {
      Handle<String> string;
1554
      if (!ReadString().ToHandle(&string)) return MaybeHandle<JSValue>();
1555 1556
      value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
          isolate_->string_function(), pretenure_));
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566
      value->set_value(*string);
      break;
    }
    default:
      UNREACHABLE();
  }
  AddObjectWithID(id, value);
  return value;
}

1567 1568 1569 1570 1571
MaybeHandle<JSRegExp> ValueDeserializer::ReadJSRegExp() {
  uint32_t id = next_id_++;
  Handle<String> pattern;
  uint32_t raw_flags;
  Handle<JSRegExp> regexp;
1572
  if (!ReadString().ToHandle(&pattern) ||
1573 1574 1575 1576
      !ReadVarint<uint32_t>().To(&raw_flags)) {
    return MaybeHandle<JSRegExp>();
  }

1577 1578
  // Ensure the deserialized flags are valid.
  // TODO(adamk): Can we remove this check now that dotAll is always-on?
1579 1580
  uint32_t flags_mask = static_cast<uint32_t>(-1) << JSRegExp::FlagCount();
  if ((raw_flags & flags_mask) ||
1581
      !JSRegExp::New(isolate_, pattern, static_cast<JSRegExp::Flags>(raw_flags))
1582 1583 1584
           .ToHandle(&regexp)) {
    return MaybeHandle<JSRegExp>();
  }
1585

1586 1587 1588 1589
  AddObjectWithID(id, regexp);
  return regexp;
}

1590 1591
MaybeHandle<JSMap> ValueDeserializer::ReadJSMap() {
  // If we are at the end of the stack, abort. This function may recurse.
1592
  STACK_CHECK(isolate_, MaybeHandle<JSMap>());
1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609

  HandleScope scope(isolate_);
  uint32_t id = next_id_++;
  Handle<JSMap> map = isolate_->factory()->NewJSMap();
  AddObjectWithID(id, map);

  Handle<JSFunction> map_set = isolate_->map_set();
  uint32_t length = 0;
  while (true) {
    SerializationTag tag;
    if (!PeekTag().To(&tag)) return MaybeHandle<JSMap>();
    if (tag == SerializationTag::kEndJSMap) {
      ConsumeTag(SerializationTag::kEndJSMap);
      break;
    }

    Handle<Object> argv[2];
1610 1611 1612 1613 1614 1615
    if (!ReadObject().ToHandle(&argv[0]) || !ReadObject().ToHandle(&argv[1])) {
      return MaybeHandle<JSMap>();
    }

    AllowJavascriptExecution allow_js(isolate_);
    if (Execution::Call(isolate_, map_set, map, arraysize(argv), argv)
1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632
            .is_null()) {
      return MaybeHandle<JSMap>();
    }
    length += 2;
  }

  uint32_t expected_length;
  if (!ReadVarint<uint32_t>().To(&expected_length) ||
      length != expected_length) {
    return MaybeHandle<JSMap>();
  }
  DCHECK(HasObjectWithID(id));
  return scope.CloseAndEscape(map);
}

MaybeHandle<JSSet> ValueDeserializer::ReadJSSet() {
  // If we are at the end of the stack, abort. This function may recurse.
1633
  STACK_CHECK(isolate_, MaybeHandle<JSSet>());
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649

  HandleScope scope(isolate_);
  uint32_t id = next_id_++;
  Handle<JSSet> set = isolate_->factory()->NewJSSet();
  AddObjectWithID(id, set);
  Handle<JSFunction> set_add = isolate_->set_add();
  uint32_t length = 0;
  while (true) {
    SerializationTag tag;
    if (!PeekTag().To(&tag)) return MaybeHandle<JSSet>();
    if (tag == SerializationTag::kEndJSSet) {
      ConsumeTag(SerializationTag::kEndJSSet);
      break;
    }

    Handle<Object> argv[1];
1650 1651 1652 1653
    if (!ReadObject().ToHandle(&argv[0])) return MaybeHandle<JSSet>();

    AllowJavascriptExecution allow_js(isolate_);
    if (Execution::Call(isolate_, set_add, set, arraysize(argv), argv)
1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668
            .is_null()) {
      return MaybeHandle<JSSet>();
    }
    length++;
  }

  uint32_t expected_length;
  if (!ReadVarint<uint32_t>().To(&expected_length) ||
      length != expected_length) {
    return MaybeHandle<JSSet>();
  }
  DCHECK(HasObjectWithID(id));
  return scope.CloseAndEscape(set);
}

1669 1670
MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadJSArrayBuffer(
    bool is_shared) {
1671
  uint32_t id = next_id_++;
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687
  if (is_shared) {
    uint32_t clone_id;
    Local<SharedArrayBuffer> sab_value;
    if (!ReadVarint<uint32_t>().To(&clone_id) || delegate_ == nullptr ||
        !delegate_
             ->GetSharedArrayBufferFromId(
                 reinterpret_cast<v8::Isolate*>(isolate_), clone_id)
             .ToLocal(&sab_value)) {
      RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSArrayBuffer);
      return MaybeHandle<JSArrayBuffer>();
    }
    Handle<JSArrayBuffer> array_buffer = Utils::OpenHandle(*sab_value);
    DCHECK_EQ(is_shared, array_buffer->is_shared());
    AddObjectWithID(id, array_buffer);
    return array_buffer;
  }
1688 1689 1690 1691 1692 1693
  uint32_t byte_length;
  if (!ReadVarint<uint32_t>().To(&byte_length) ||
      byte_length > static_cast<size_t>(end_ - position_)) {
    return MaybeHandle<JSArrayBuffer>();
  }
  const bool should_initialize = false;
1694 1695
  Handle<JSArrayBuffer> array_buffer =
      isolate_->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, pretenure_);
1696 1697 1698 1699
  if (!JSArrayBuffer::SetupAllocatingData(array_buffer, isolate_, byte_length,
                                          should_initialize)) {
    return MaybeHandle<JSArrayBuffer>();
  }
1700 1701 1702 1703 1704 1705
  memcpy(array_buffer->backing_store(), position_, byte_length);
  position_ += byte_length;
  AddObjectWithID(id, array_buffer);
  return array_buffer;
}

1706
MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer() {
1707 1708
  uint32_t id = next_id_++;
  uint32_t transfer_id;
1709
  Handle<SimpleNumberDictionary> transfer_map;
1710 1711 1712 1713 1714
  if (!ReadVarint<uint32_t>().To(&transfer_id) ||
      !array_buffer_transfer_map_.ToHandle(&transfer_map)) {
    return MaybeHandle<JSArrayBuffer>();
  }
  int index = transfer_map->FindEntry(isolate_, transfer_id);
1715
  if (index == SimpleNumberDictionary::kNotFound) {
1716 1717 1718 1719 1720 1721 1722 1723
    return MaybeHandle<JSArrayBuffer>();
  }
  Handle<JSArrayBuffer> array_buffer(
      JSArrayBuffer::cast(transfer_map->ValueAt(index)), isolate_);
  AddObjectWithID(id, array_buffer);
  return array_buffer;
}

1724 1725
MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
    Handle<JSArrayBuffer> buffer) {
1726
  uint32_t buffer_byte_length = static_cast<uint32_t>(buffer->byte_length());
1727 1728 1729
  uint8_t tag = 0;
  uint32_t byte_offset = 0;
  uint32_t byte_length = 0;
1730 1731 1732 1733 1734 1735 1736 1737 1738 1739
  if (!ReadVarint<uint8_t>().To(&tag) ||
      !ReadVarint<uint32_t>().To(&byte_offset) ||
      !ReadVarint<uint32_t>().To(&byte_length) ||
      byte_offset > buffer_byte_length ||
      byte_length > buffer_byte_length - byte_offset) {
    return MaybeHandle<JSArrayBufferView>();
  }
  uint32_t id = next_id_++;
  ExternalArrayType external_array_type = kExternalInt8Array;
  unsigned element_size = 0;
1740

1741 1742 1743 1744 1745 1746 1747
  switch (static_cast<ArrayBufferViewTag>(tag)) {
    case ArrayBufferViewTag::kDataView: {
      Handle<JSDataView> data_view =
          isolate_->factory()->NewJSDataView(buffer, byte_offset, byte_length);
      AddObjectWithID(id, data_view);
      return data_view;
    }
1748 1749 1750 1751
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
  case ArrayBufferViewTag::k##Type##Array:        \
    external_array_type = kExternal##Type##Array; \
    element_size = sizeof(ctype);                 \
1752 1753 1754 1755
    break;
      TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
  }
1756 1757
  if (element_size == 0 || byte_offset % element_size != 0 ||
      byte_length % element_size != 0) {
1758 1759 1760
    return MaybeHandle<JSArrayBufferView>();
  }
  Handle<JSTypedArray> typed_array = isolate_->factory()->NewJSTypedArray(
1761 1762
      external_array_type, buffer, byte_offset, byte_length / element_size,
      pretenure_);
1763 1764 1765 1766
  AddObjectWithID(id, typed_array);
  return typed_array;
}

1767
MaybeHandle<JSObject> ValueDeserializer::ReadWasmModuleTransfer() {
1768 1769 1770
  auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
  if ((FLAG_wasm_disable_structured_cloning && !enabled_features.threads) ||
      expect_inline_wasm()) {
1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790
    return MaybeHandle<JSObject>();
  }

  uint32_t transfer_id = 0;
  Local<Value> module_value;
  if (!ReadVarint<uint32_t>().To(&transfer_id) || delegate_ == nullptr ||
      !delegate_
           ->GetWasmModuleFromId(reinterpret_cast<v8::Isolate*>(isolate_),
                                 transfer_id)
           .ToLocal(&module_value)) {
    RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSObject);
    return MaybeHandle<JSObject>();
  }
  uint32_t id = next_id_++;
  Handle<JSObject> module =
      Handle<JSObject>::cast(Utils::OpenHandle(*module_value));
  AddObjectWithID(id, module);
  return module;
}

1791
MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
1792 1793 1794
  auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
  if ((FLAG_wasm_disable_structured_cloning && !enabled_features.threads) ||
      !expect_inline_wasm()) {
1795 1796
    return MaybeHandle<JSObject>();
  }
1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822

  Vector<const uint8_t> encoding_tag;
  if (!ReadRawBytes(sizeof(WasmEncodingTag)).To(&encoding_tag) ||
      encoding_tag[0] != static_cast<uint8_t>(WasmEncodingTag::kRawBytes)) {
    return MaybeHandle<JSObject>();
  }

  // Extract the data from the buffer: wasm wire bytes, followed by V8 compiled
  // script data.
  static_assert(sizeof(int) <= sizeof(uint32_t),
                "max int must fit in uint32_t");
  const uint32_t max_valid_size = std::numeric_limits<int>::max();
  uint32_t wire_bytes_length = 0;
  Vector<const uint8_t> wire_bytes;
  uint32_t compiled_bytes_length = 0;
  Vector<const uint8_t> compiled_bytes;
  if (!ReadVarint<uint32_t>().To(&wire_bytes_length) ||
      wire_bytes_length > max_valid_size ||
      !ReadRawBytes(wire_bytes_length).To(&wire_bytes) ||
      !ReadVarint<uint32_t>().To(&compiled_bytes_length) ||
      compiled_bytes_length > max_valid_size ||
      !ReadRawBytes(compiled_bytes_length).To(&compiled_bytes)) {
    return MaybeHandle<JSObject>();
  }

  // Try to deserialize the compiled module first.
1823 1824
  MaybeHandle<WasmModuleObject> result =
      wasm::DeserializeNativeModule(isolate_, compiled_bytes, wire_bytes);
1825
  if (result.is_null()) {
1826
    wasm::ErrorThrower thrower(isolate_, "ValueDeserializer::ReadWasmModule");
1827 1828
    // TODO(titzer): are the current features appropriate for deserializing?
    auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
1829
    result = isolate_->wasm_engine()->SyncCompile(
1830 1831
        isolate_, enabled_features, &thrower,
        wasm::ModuleWireBytes(wire_bytes));
1832
  }
1833 1834 1835 1836
  uint32_t id = next_id_++;
  if (!result.is_null()) {
    AddObjectWithID(id, result.ToHandleChecked());
  }
1837
  return result;
1838 1839
}

1840 1841 1842
MaybeHandle<WasmMemoryObject> ValueDeserializer::ReadWasmMemory() {
  uint32_t id = next_id_++;

1843 1844
  auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
  if (!enabled_features.threads) {
1845
    return MaybeHandle<WasmMemoryObject>();
1846 1847 1848 1849
  }

  int32_t maximum_pages;
  if (!ReadZigZag<int32_t>().To(&maximum_pages)) {
1850
    return MaybeHandle<WasmMemoryObject>();
1851 1852 1853 1854
  }

  SerializationTag tag;
  if (!ReadTag().To(&tag) || tag != SerializationTag::kSharedArrayBuffer) {
1855
    return MaybeHandle<WasmMemoryObject>();
1856 1857 1858 1859
  }

  const bool is_shared = true;
  Handle<JSArrayBuffer> buffer;
1860
  if (!ReadJSArrayBuffer(is_shared).ToHandle(&buffer)) {
1861
    return MaybeHandle<WasmMemoryObject>();
1862 1863
  }

1864 1865 1866 1867 1868
  Handle<WasmMemoryObject> result =
      WasmMemoryObject::New(isolate_, buffer, maximum_pages);

  AddObjectWithID(id, result);
  return result;
1869 1870
}

1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886
MaybeHandle<JSObject> ValueDeserializer::ReadHostObject() {
  if (!delegate_) return MaybeHandle<JSObject>();
  STACK_CHECK(isolate_, MaybeHandle<JSObject>());
  uint32_t id = next_id_++;
  v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
  v8::Local<v8::Object> object;
  if (!delegate_->ReadHostObject(v8_isolate).ToLocal(&object)) {
    RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSObject);
    return MaybeHandle<JSObject>();
  }
  Handle<JSObject> js_object =
      Handle<JSObject>::cast(Utils::OpenHandle(*object));
  AddObjectWithID(id, js_object);
  return js_object;
}

1887 1888 1889 1890 1891 1892 1893 1894
// Copies a vector of property values into an object, given the map that should
// be used.
static void CommitProperties(Handle<JSObject> object, Handle<Map> map,
                             const std::vector<Handle<Object>>& properties) {
  JSObject::AllocateStorageForMap(object, map);
  DCHECK(!object->map()->is_dictionary_map());

  DisallowHeapAllocation no_gc;
1895
  DescriptorArray descriptors = object->map()->instance_descriptors();
1896
  for (unsigned i = 0; i < properties.size(); i++) {
1897
    // Initializing store.
1898 1899 1900 1901
    object->WriteToField(i, descriptors->GetDetails(i), *properties[i]);
  }
}

1902 1903 1904 1905
static bool IsValidObjectKey(Handle<Object> value) {
  return value->IsName() || value->IsNumber();
}

1906
Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
1907 1908 1909 1910 1911 1912 1913 1914 1915
    Handle<JSObject> object, SerializationTag end_tag,
    bool can_use_transitions) {
  uint32_t num_properties = 0;

  // Fast path (following map transitions).
  if (can_use_transitions) {
    bool transitioning = true;
    Handle<Map> map(object->map(), isolate_);
    DCHECK(!map->is_dictionary_map());
1916
    DCHECK_EQ(0, map->instance_descriptors()->number_of_descriptors());
1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935
    std::vector<Handle<Object>> properties;
    properties.reserve(8);

    while (transitioning) {
      // If there are no more properties, finish.
      SerializationTag tag;
      if (!PeekTag().To(&tag)) return Nothing<uint32_t>();
      if (tag == end_tag) {
        ConsumeTag(end_tag);
        CommitProperties(object, map, properties);
        CHECK_LT(properties.size(), std::numeric_limits<uint32_t>::max());
        return Just(static_cast<uint32_t>(properties.size()));
      }

      // Determine the key to be used and the target map to transition to, if
      // possible. Transitioning may abort if the key is not a string, or if no
      // transition was found.
      Handle<Object> key;
      Handle<Map> target;
1936
      TransitionsAccessor transitions(isolate_, map);
1937
      Handle<String> expected_key = transitions.ExpectedTransitionKey();
1938 1939
      if (!expected_key.is_null() && ReadExpectedString(expected_key)) {
        key = expected_key;
1940
        target = transitions.ExpectedTransitionTarget();
1941
      } else {
1942 1943 1944
        if (!ReadObject().ToHandle(&key) || !IsValidObjectKey(key)) {
          return Nothing<uint32_t>();
        }
1945 1946 1947
        if (key->IsString()) {
          key =
              isolate_->factory()->InternalizeString(Handle<String>::cast(key));
1948
          // Don't reuse |transitions| because it could be stale.
1949
          transitioning = TransitionsAccessor(isolate_, map)
1950 1951
                              .FindTransitionToField(Handle<String>::cast(key))
                              .ToHandle(&target);
1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975
        } else {
          transitioning = false;
        }
      }

      // Read the value that corresponds to it.
      Handle<Object> value;
      if (!ReadObject().ToHandle(&value)) return Nothing<uint32_t>();

      // If still transitioning and the value fits the field representation
      // (though generalization may be required), store the property value so
      // that we can copy them all at once. Otherwise, stop transitioning.
      if (transitioning) {
        int descriptor = static_cast<int>(properties.size());
        PropertyDetails details =
            target->instance_descriptors()->GetDetails(descriptor);
        Representation expected_representation = details.representation();
        if (value->FitsRepresentation(expected_representation)) {
          if (expected_representation.IsHeapObject() &&
              !target->instance_descriptors()
                   ->GetFieldType(descriptor)
                   ->NowContains(value)) {
            Handle<FieldType> value_type =
                value->OptimalType(isolate_, expected_representation);
1976 1977 1978
            Map::GeneralizeField(isolate_, target, descriptor,
                                 details.constness(), expected_representation,
                                 value_type);
1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000
          }
          DCHECK(target->instance_descriptors()
                     ->GetFieldType(descriptor)
                     ->NowContains(value));
          properties.push_back(value);
          map = target;
          continue;
        } else {
          transitioning = false;
        }
      }

      // Fell out of transitioning fast path. Commit the properties gathered so
      // far, and then start setting properties slowly instead.
      DCHECK(!transitioning);
      CHECK_LT(properties.size(), std::numeric_limits<uint32_t>::max());
      CommitProperties(object, map, properties);
      num_properties = static_cast<uint32_t>(properties.size());

      bool success;
      LookupIterator it = LookupIterator::PropertyOrElement(
          isolate_, object, key, &success, LookupIterator::OWN);
2001
      if (!success || it.state() != LookupIterator::NOT_FOUND ||
2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017
          JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE)
              .is_null()) {
        return Nothing<uint32_t>();
      }
      num_properties++;
    }

    // At this point, transitioning should be done, but at least one property
    // should have been written (in the zero-property case, there is an early
    // return).
    DCHECK(!transitioning);
    DCHECK_GE(num_properties, 1u);
  }

  // Slow path.
  for (;; num_properties++) {
2018 2019 2020
    SerializationTag tag;
    if (!PeekTag().To(&tag)) return Nothing<uint32_t>();
    if (tag == end_tag) {
2021
      ConsumeTag(end_tag);
2022 2023 2024 2025
      return Just(num_properties);
    }

    Handle<Object> key;
2026 2027 2028
    if (!ReadObject().ToHandle(&key) || !IsValidObjectKey(key)) {
      return Nothing<uint32_t>();
    }
2029 2030 2031 2032 2033 2034
    Handle<Object> value;
    if (!ReadObject().ToHandle(&value)) return Nothing<uint32_t>();

    bool success;
    LookupIterator it = LookupIterator::PropertyOrElement(
        isolate_, object, key, &success, LookupIterator::OWN);
2035
    if (!success || it.state() != LookupIterator::NOT_FOUND ||
2036 2037 2038 2039 2040 2041 2042 2043
        JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE)
            .is_null()) {
      return Nothing<uint32_t>();
    }
  }
}

bool ValueDeserializer::HasObjectWithID(uint32_t id) {
2044 2045
  return id < static_cast<unsigned>(id_map_->length()) &&
         !id_map_->get(id)->IsTheHole(isolate_);
2046 2047 2048
}

MaybeHandle<JSReceiver> ValueDeserializer::GetObjectWithID(uint32_t id) {
2049
  if (id >= static_cast<unsigned>(id_map_->length())) {
2050 2051
    return MaybeHandle<JSReceiver>();
  }
2052 2053
  Object* value = id_map_->get(id);
  if (value->IsTheHole(isolate_)) return MaybeHandle<JSReceiver>();
2054 2055 2056 2057 2058 2059 2060
  DCHECK(value->IsJSReceiver());
  return Handle<JSReceiver>(JSReceiver::cast(value), isolate_);
}

void ValueDeserializer::AddObjectWithID(uint32_t id,
                                        Handle<JSReceiver> object) {
  DCHECK(!HasObjectWithID(id));
2061 2062
  Handle<FixedArray> new_array =
      FixedArray::SetAndGrow(isolate_, id_map_, id, object);
2063 2064

  // If the dictionary was reallocated, update the global handle.
2065
  if (!new_array.is_identical_to(id_map_)) {
2066
    GlobalHandles::Destroy(id_map_.location());
2067
    id_map_ = isolate_->global_handles()->Create(*new_array);
2068 2069 2070
  }
}

2071 2072 2073 2074
static Maybe<bool> SetPropertiesFromKeyValuePairs(Isolate* isolate,
                                                  Handle<JSObject> object,
                                                  Handle<Object>* data,
                                                  uint32_t num_properties) {
2075 2076
  for (unsigned i = 0; i < 2 * num_properties; i += 2) {
    Handle<Object> key = data[i];
2077
    if (!IsValidObjectKey(key)) return Nothing<bool>();
2078 2079 2080 2081
    Handle<Object> value = data[i + 1];
    bool success;
    LookupIterator it = LookupIterator::PropertyOrElement(
        isolate, object, key, &success, LookupIterator::OWN);
2082
    if (!success || it.state() != LookupIterator::NOT_FOUND ||
2083 2084
        JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE)
            .is_null()) {
2085
      return Nothing<bool>();
2086 2087
    }
  }
2088
  return Just(true);
2089 2090
}

2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104
namespace {

// Throws a generic "deserialization failed" exception by default, unless a more
// specific exception has already been thrown.
void ThrowDeserializationExceptionIfNonePending(Isolate* isolate) {
  if (!isolate->has_pending_exception()) {
    isolate->Throw(*isolate->factory()->NewError(
        MessageTemplate::kDataCloneDeserializationError));
  }
  DCHECK(isolate->has_pending_exception());
}

}  // namespace

2105 2106
MaybeHandle<Object>
ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() {
2107
  DCHECK_EQ(version_, 0u);
2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123
  HandleScope scope(isolate_);
  std::vector<Handle<Object>> stack;
  while (position_ < end_) {
    SerializationTag tag;
    if (!PeekTag().To(&tag)) break;

    Handle<Object> new_object;
    switch (tag) {
      case SerializationTag::kEndJSObject: {
        ConsumeTag(SerializationTag::kEndJSObject);

        // JS Object: Read the last 2*n values from the stack and use them as
        // key-value pairs.
        uint32_t num_properties;
        if (!ReadVarint<uint32_t>().To(&num_properties) ||
            stack.size() / 2 < num_properties) {
2124 2125
          isolate_->Throw(*isolate_->factory()->NewError(
              MessageTemplate::kDataCloneDeserializationError));
2126 2127 2128
          return MaybeHandle<Object>();
        }

2129 2130
        size_t begin_properties =
            stack.size() - 2 * static_cast<size_t>(num_properties);
2131 2132
        Handle<JSObject> js_object = isolate_->factory()->NewJSObject(
            isolate_->object_function(), pretenure_);
2133 2134 2135 2136
        if (num_properties &&
            !SetPropertiesFromKeyValuePairs(
                 isolate_, js_object, &stack[begin_properties], num_properties)
                 .FromMaybe(false)) {
2137
          ThrowDeserializationExceptionIfNonePending(isolate_);
2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153
          return MaybeHandle<Object>();
        }

        stack.resize(begin_properties);
        new_object = js_object;
        break;
      }
      case SerializationTag::kEndSparseJSArray: {
        ConsumeTag(SerializationTag::kEndSparseJSArray);

        // Sparse JS Array: Read the last 2*|num_properties| from the stack.
        uint32_t num_properties;
        uint32_t length;
        if (!ReadVarint<uint32_t>().To(&num_properties) ||
            !ReadVarint<uint32_t>().To(&length) ||
            stack.size() / 2 < num_properties) {
2154 2155
          isolate_->Throw(*isolate_->factory()->NewError(
              MessageTemplate::kDataCloneDeserializationError));
2156 2157 2158
          return MaybeHandle<Object>();
        }

2159 2160
        Handle<JSArray> js_array = isolate_->factory()->NewJSArray(
            0, TERMINAL_FAST_ELEMENTS_KIND, pretenure_);
2161 2162 2163 2164 2165 2166 2167
        JSArray::SetLength(js_array, length);
        size_t begin_properties =
            stack.size() - 2 * static_cast<size_t>(num_properties);
        if (num_properties &&
            !SetPropertiesFromKeyValuePairs(
                 isolate_, js_array, &stack[begin_properties], num_properties)
                 .FromMaybe(false)) {
2168
          ThrowDeserializationExceptionIfNonePending(isolate_);
2169 2170 2171 2172
          return MaybeHandle<Object>();
        }

        stack.resize(begin_properties);
2173
        new_object = js_array;
2174 2175
        break;
      }
2176
      case SerializationTag::kEndDenseJSArray: {
2177
        // This was already broken in Chromium, and apparently wasn't missed.
2178 2179
        isolate_->Throw(*isolate_->factory()->NewError(
            MessageTemplate::kDataCloneDeserializationError));
2180
        return MaybeHandle<Object>();
2181
      }
2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196
      default:
        if (!ReadObject().ToHandle(&new_object)) return MaybeHandle<Object>();
        break;
    }
    stack.push_back(new_object);
  }

// Nothing remains but padding.
#ifdef DEBUG
  while (position_ < end_) {
    DCHECK(*position_++ == static_cast<uint8_t>(SerializationTag::kPadding));
  }
#endif
  position_ = end_;

2197 2198 2199 2200 2201
  if (stack.size() != 1) {
    isolate_->Throw(*isolate_->factory()->NewError(
        MessageTemplate::kDataCloneDeserializationError));
    return MaybeHandle<Object>();
  }
2202 2203 2204
  return scope.CloseAndEscape(stack[0]);
}

2205 2206
}  // namespace internal
}  // namespace v8