aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/objects/value-serializer.cc
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2019-08-01 08:38:30 +0200
committerMichaël Zasso <targos@protonmail.com>2019-08-01 12:53:56 +0200
commit2dcc3665abf57c3607cebffdeeca062f5894885d (patch)
tree4f560748132edcfb4c22d6f967a7e80d23d7ea2c /deps/v8/src/objects/value-serializer.cc
parent1ee47d550c6de132f06110aa13eceb7551d643b3 (diff)
downloadandroid-node-v8-2dcc3665abf57c3607cebffdeeca062f5894885d.tar.gz
android-node-v8-2dcc3665abf57c3607cebffdeeca062f5894885d.tar.bz2
android-node-v8-2dcc3665abf57c3607cebffdeeca062f5894885d.zip
deps: update V8 to 7.6.303.28
PR-URL: https://github.com/nodejs/node/pull/28016 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Refael Ackermann (רפאל פלחי) <refack@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Diffstat (limited to 'deps/v8/src/objects/value-serializer.cc')
-rw-r--r--deps/v8/src/objects/value-serializer.cc2213
1 files changed, 2213 insertions, 0 deletions
diff --git a/deps/v8/src/objects/value-serializer.cc b/deps/v8/src/objects/value-serializer.cc
new file mode 100644
index 0000000000..331a12b157
--- /dev/null
+++ b/deps/v8/src/objects/value-serializer.cc
@@ -0,0 +1,2213 @@
+// 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/objects/value-serializer.h"
+
+#include <type_traits>
+
+#include "include/v8-value-serializer-version.h"
+#include "src/api/api-inl.h"
+#include "src/base/logging.h"
+#include "src/execution/isolate.h"
+#include "src/flags/flags.h"
+#include "src/handles/handles-inl.h"
+#include "src/handles/maybe-handles-inl.h"
+#include "src/heap/factory.h"
+#include "src/numbers/conversions.h"
+#include "src/objects/heap-number-inl.h"
+#include "src/objects/js-array-inl.h"
+#include "src/objects/js-collection-inl.h"
+#include "src/objects/js-regexp-inl.h"
+#include "src/objects/objects-inl.h"
+#include "src/objects/oddball-inl.h"
+#include "src/objects/ordered-hash-table-inl.h"
+#include "src/objects/smi.h"
+#include "src/objects/transitions-inl.h"
+#include "src/snapshot/code-serializer.h"
+#include "src/wasm/wasm-engine.h"
+#include "src/wasm/wasm-objects-inl.h"
+#include "src/wasm/wasm-result.h"
+#include "src/wasm/wasm-serialization.h"
+
+namespace v8 {
+namespace internal {
+
+// Version 9: (imported from Blink)
+// Version 10: one-byte (Latin-1) strings
+// Version 11: properly separate undefined from the hole in arrays
+// Version 12: regexp and string objects share normal string encoding
+// Version 13: host objects have an explicit tag (rather than handling all
+// unknown tags)
+//
+// 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.
+static const uint32_t kLatestVersion = 13;
+static_assert(kLatestVersion == v8::CurrentValueSerializerFormatVersion(),
+ "Exported format version must match latest version.");
+
+static const int kPretenureThreshold = 100 * KB;
+
+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;
+}
+
+// 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.
+enum class SerializationTag : uint8_t {
+ // version:uint32_t (if at beginning of data, sets version > 0)
+ kVersion = 0xFF,
+ // ignore
+ kPadding = '\0',
+ // refTableSize:uint32_t (previously used for sanity checks; safe to ignore)
+ kVerifyObjectCount = '?',
+ // Oddballs (no data).
+ kTheHole = '-',
+ kUndefined = '_',
+ kNull = '0',
+ kTrue = 'T',
+ kFalse = 'F',
+ // 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',
+ // BigInt. Bitfield:uint32_t, then raw digits storage.
+ kBigInt = 'Z',
+ // byteLength:uint32_t, then raw data
+ kUtf8String = 'S',
+ kOneByteString = '"',
+ kTwoByteString = 'c',
+ // 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 = '{',
+ // 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 = '$',
+ // Date. millisSinceEpoch:double
+ kDate = 'D',
+ // Boolean object. No data.
+ kTrueObject = 'y',
+ kFalseObject = 'x',
+ // Number object. value:double
+ kNumberObject = 'n',
+ // BigInt object. Bitfield:uint32_t, then raw digits storage.
+ kBigIntObject = 'z',
+ // String object, UTF-8 encoding. byteLength:uint32_t, then raw data.
+ kStringObject = 's',
+ // Regular expression, UTF-8 encoding. byteLength:uint32_t, raw data,
+ // flags:uint32_t.
+ kRegExp = 'R',
+ // 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 = ',',
+ // Array buffer. byteLength:uint32_t, then raw data.
+ kArrayBuffer = 'B',
+ // Array buffer (transferred). transferID:uint32_t
+ kArrayBufferTransfer = 't',
+ // 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',
+ // Shared array buffer. transferID:uint32_t
+ kSharedArrayBuffer = 'u',
+ // 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',
+ // A wasm module object transfer. next value is its index.
+ kWasmModuleTransfer = 'w',
+ // The delegate is responsible for processing all following data.
+ // This "escapes" to whatever wire format the delegate chooses.
+ kHostObject = '\\',
+ // A transferred WebAssembly.Memory object. maximumPages:int32_t, then by
+ // SharedArrayBuffer tag and its data.
+ kWasmMemoryTransfer = 'm',
+};
+
+namespace {
+
+enum class ArrayBufferViewTag : uint8_t {
+ kInt8Array = 'b',
+ kUint8Array = 'B',
+ kUint8ClampedArray = 'C',
+ kInt16Array = 'w',
+ kUint16Array = 'W',
+ kInt32Array = 'd',
+ kUint32Array = 'D',
+ kFloat32Array = 'f',
+ kFloat64Array = 'F',
+ kBigInt64Array = 'q',
+ kBigUint64Array = 'Q',
+ kDataView = '?',
+};
+
+enum class WasmEncodingTag : uint8_t {
+ kRawBytes = 'y',
+};
+
+} // namespace
+
+ValueSerializer::ValueSerializer(Isolate* isolate,
+ v8::ValueSerializer::Delegate* delegate)
+ : isolate_(isolate),
+ delegate_(delegate),
+ zone_(isolate->allocator(), ZONE_NAME),
+ id_map_(isolate->heap(), ZoneAllocationPolicy(&zone_)),
+ array_buffer_transfer_map_(isolate->heap(),
+ ZoneAllocationPolicy(&zone_)) {}
+
+ValueSerializer::~ValueSerializer() {
+ if (buffer_) {
+ if (delegate_) {
+ delegate_->FreeBufferMemory(buffer_);
+ } else {
+ free(buffer_);
+ }
+ }
+}
+
+void ValueSerializer::WriteHeader() {
+ WriteTag(SerializationTag::kVersion);
+ WriteVarint(kLatestVersion);
+}
+
+void ValueSerializer::SetTreatArrayBufferViewsAsHostObjects(bool mode) {
+ treat_array_buffer_views_as_host_objects_ = mode;
+}
+
+void ValueSerializer::WriteTag(SerializationTag tag) {
+ uint8_t raw_tag = static_cast<uint8_t>(tag);
+ WriteRawBytes(&raw_tag, sizeof(raw_tag));
+}
+
+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 {
+ *next_byte = (value & 0x7F) | 0x80;
+ next_byte++;
+ value >>= 7;
+ } while (value);
+ *(next_byte - 1) &= 0x7F;
+ WriteRawBytes(stack_buffer, next_byte - stack_buffer);
+}
+
+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.
+ WriteRawBytes(&value, sizeof(value));
+}
+
+void ValueSerializer::WriteOneByteString(Vector<const uint8_t> chars) {
+ WriteVarint<uint32_t>(chars.length());
+ WriteRawBytes(chars.begin(), chars.length() * sizeof(uint8_t));
+}
+
+void ValueSerializer::WriteTwoByteString(Vector<const uc16> chars) {
+ // Warning: this uses host endianness.
+ WriteVarint<uint32_t>(chars.length() * sizeof(uc16));
+ WriteRawBytes(chars.begin(), chars.length() * sizeof(uc16));
+}
+
+void ValueSerializer::WriteBigIntContents(BigInt bigint) {
+ 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);
+ }
+}
+
+void ValueSerializer::WriteRawBytes(const void* source, size_t length) {
+ uint8_t* dest;
+ if (ReserveRawBytes(length).To(&dest) && length > 0) {
+ memcpy(dest, source, length);
+ }
+}
+
+Maybe<uint8_t*> ValueSerializer::ReserveRawBytes(size_t bytes) {
+ size_t old_size = buffer_size_;
+ size_t new_size = old_size + bytes;
+ if (V8_UNLIKELY(new_size > buffer_capacity_)) {
+ bool ok;
+ if (!ExpandBuffer(new_size).To(&ok)) {
+ return Nothing<uint8_t*>();
+ }
+ }
+ buffer_size_ = new_size;
+ return Just(&buffer_[old_size]);
+}
+
+Maybe<bool> ValueSerializer::ExpandBuffer(size_t required_capacity) {
+ 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;
+ }
+ 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>();
+ }
+}
+
+void ValueSerializer::WriteUint32(uint32_t value) {
+ WriteVarint<uint32_t>(value);
+}
+
+void ValueSerializer::WriteUint64(uint64_t value) {
+ WriteVarint<uint64_t>(value);
+}
+
+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;
+}
+
+void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id,
+ Handle<JSArrayBuffer> array_buffer) {
+ DCHECK(!array_buffer_transfer_map_.Find(array_buffer));
+ DCHECK(!array_buffer->is_shared());
+ array_buffer_transfer_map_.Set(array_buffer, transfer_id);
+}
+
+Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
+ // 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();
+
+ if (object->IsSmi()) {
+ WriteSmi(Smi::cast(*object));
+ return ThrowIfOutOfMemory();
+ }
+
+ DCHECK(object->IsHeapObject());
+ switch (HeapObject::cast(*object).map().instance_type()) {
+ case ODDBALL_TYPE:
+ WriteOddball(Oddball::cast(*object));
+ return ThrowIfOutOfMemory();
+ case HEAP_NUMBER_TYPE:
+ WriteHeapNumber(HeapNumber::cast(*object));
+ return ThrowIfOutOfMemory();
+ case MUTABLE_HEAP_NUMBER_TYPE:
+ WriteMutableHeapNumber(MutableHeapNumber::cast(*object));
+ return ThrowIfOutOfMemory();
+ case BIGINT_TYPE:
+ WriteBigInt(BigInt::cast(*object));
+ return ThrowIfOutOfMemory();
+ 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);
+ if (!id_map_.Find(view) && !treat_array_buffer_views_as_host_objects_) {
+ 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);
+ }
+ default:
+ if (object->IsString()) {
+ WriteString(Handle<String>::cast(object));
+ return ThrowIfOutOfMemory();
+ } else if (object->IsJSReceiver()) {
+ return WriteJSReceiver(Handle<JSReceiver>::cast(object));
+ } else {
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, object);
+ return Nothing<bool>();
+ }
+ }
+}
+
+void ValueSerializer::WriteOddball(Oddball oddball) {
+ 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();
+ }
+ WriteTag(tag);
+}
+
+void ValueSerializer::WriteSmi(Smi smi) {
+ static_assert(kSmiValueSize <= 32, "Expected SMI <= 32 bits.");
+ WriteTag(SerializationTag::kInt32);
+ WriteZigZag<int32_t>(smi.value());
+}
+
+void ValueSerializer::WriteHeapNumber(HeapNumber number) {
+ WriteTag(SerializationTag::kDouble);
+ WriteDouble(number.value());
+}
+
+void ValueSerializer::WriteMutableHeapNumber(MutableHeapNumber number) {
+ WriteTag(SerializationTag::kDouble);
+ WriteDouble(number.value());
+}
+
+void ValueSerializer::WriteBigInt(BigInt bigint) {
+ WriteTag(SerializationTag::kBigInt);
+ WriteBigIntContents(bigint);
+}
+
+void ValueSerializer::WriteString(Handle<String> string) {
+ string = String::Flatten(isolate_, string);
+ DisallowHeapAllocation no_gc;
+ String::FlatContent flat = string->GetFlatContent(no_gc);
+ DCHECK(flat.IsFlat());
+ if (flat.IsOneByte()) {
+ Vector<const uint8_t> chars = flat.ToOneByteVector();
+ WriteTag(SerializationTag::kOneByteString);
+ WriteOneByteString(chars);
+ } 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.
+ if ((buffer_size_ + 1 + BytesNeededForVarint(byte_length)) & 1)
+ WriteTag(SerializationTag::kPadding);
+ WriteTag(SerializationTag::kTwoByteString);
+ WriteTwoByteString(chars);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+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);
+ return ThrowIfOutOfMemory();
+ }
+
+ // 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();
+ if (receiver->IsCallable() || (IsSpecialReceiverInstanceType(instance_type) &&
+ instance_type != JS_SPECIAL_API_OBJECT_TYPE)) {
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, receiver);
+ return Nothing<bool>();
+ }
+
+ // If we are at the end of the stack, abort. This function may recurse.
+ STACK_CHECK(isolate_, Nothing<bool>());
+
+ HandleScope scope(isolate_);
+ switch (instance_type) {
+ case JS_ARRAY_TYPE:
+ return WriteJSArray(Handle<JSArray>::cast(receiver));
+ case JS_OBJECT_TYPE:
+ case JS_API_OBJECT_TYPE: {
+ Handle<JSObject> js_object = Handle<JSObject>::cast(receiver);
+ if (JSObject::GetEmbedderFieldCount(js_object->map())) {
+ return WriteHostObject(js_object);
+ } else {
+ return WriteJSObject(js_object);
+ }
+ }
+ case JS_SPECIAL_API_OBJECT_TYPE:
+ return WriteHostObject(Handle<JSObject>::cast(receiver));
+ case JS_DATE_TYPE:
+ WriteJSDate(JSDate::cast(*receiver));
+ return ThrowIfOutOfMemory();
+ case JS_VALUE_TYPE:
+ return WriteJSValue(Handle<JSValue>::cast(receiver));
+ case JS_REGEXP_TYPE:
+ WriteJSRegExp(JSRegExp::cast(*receiver));
+ return ThrowIfOutOfMemory();
+ case JS_MAP_TYPE:
+ return WriteJSMap(Handle<JSMap>::cast(receiver));
+ case JS_SET_TYPE:
+ return WriteJSSet(Handle<JSSet>::cast(receiver));
+ case JS_ARRAY_BUFFER_TYPE:
+ return WriteJSArrayBuffer(Handle<JSArrayBuffer>::cast(receiver));
+ case JS_TYPED_ARRAY_TYPE:
+ case JS_DATA_VIEW_TYPE:
+ return WriteJSArrayBufferView(JSArrayBufferView::cast(*receiver));
+ case WASM_MODULE_TYPE: {
+ auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
+ if (!FLAG_wasm_disable_structured_cloning || enabled_features.threads) {
+ // Only write WebAssembly modules if not disabled by a flag.
+ return WriteWasmModule(Handle<WasmModuleObject>::cast(receiver));
+ }
+ break;
+ }
+ case WASM_MEMORY_TYPE: {
+ auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
+ if (enabled_features.threads) {
+ return WriteWasmMemory(Handle<WasmMemoryObject>::cast(receiver));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, receiver);
+ return Nothing<bool>();
+}
+
+Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) {
+ DCHECK(!object->map().IsCustomElementsReceiverMap());
+ 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();
+ if (V8_LIKELY(!map_changed && details.location() == kField)) {
+ DCHECK_EQ(kData, details.kind());
+ 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);
+ return ThrowIfOutOfMemory();
+}
+
+Maybe<bool> ValueSerializer::WriteJSObjectSlow(Handle<JSObject> object) {
+ WriteTag(SerializationTag::kBeginJSObject);
+ Handle<FixedArray> keys;
+ uint32_t properties_written = 0;
+ if (!KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
+ ENUMERABLE_STRINGS)
+ .ToHandle(&keys) ||
+ !WriteJSObjectPropertiesSlow(object, keys).To(&properties_written)) {
+ return Nothing<bool>();
+ }
+ WriteTag(SerializationTag::kEndJSObject);
+ WriteVarint<uint32_t>(properties_written);
+ return ThrowIfOutOfMemory();
+}
+
+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 =
+ array->HasFastElements() && !array->HasHoleyElements();
+
+ if (should_serialize_densely) {
+ DCHECK_LE(length, static_cast<uint32_t>(FixedArray::kMaxLength));
+ WriteTag(SerializationTag::kBeginDenseJSArray);
+ WriteVarint<uint32_t>(length);
+ uint32_t i = 0;
+
+ // Fast paths. Note that PACKED_ELEMENTS in particular can bail due to the
+ // structure of the elements changing.
+ switch (array->GetElementsKind()) {
+ case PACKED_SMI_ELEMENTS: {
+ Handle<FixedArray> elements(FixedArray::cast(array->elements()),
+ isolate_);
+ for (; i < length; i++) WriteSmi(Smi::cast(elements->get(i)));
+ break;
+ }
+ case PACKED_DOUBLE_ELEMENTS: {
+ // 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;
+ Handle<FixedDoubleArray> elements(
+ FixedDoubleArray::cast(array->elements()), isolate_);
+ for (; i < length; i++) {
+ WriteTag(SerializationTag::kDouble);
+ WriteDouble(elements->get_scalar(i));
+ }
+ break;
+ }
+ case PACKED_ELEMENTS: {
+ Handle<Object> old_length(array->length(), isolate_);
+ for (; i < length; i++) {
+ if (array->length() != *old_length ||
+ array->GetElementsKind() != PACKED_ELEMENTS) {
+ // 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++) {
+ // 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);
+ 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;
+ }
+ if (!Object::GetProperty(&it).ToHandle(&element) ||
+ !WriteObject(element).FromMaybe(false)) {
+ return Nothing<bool>();
+ }
+ }
+
+ 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;
+ if (!WriteJSObjectPropertiesSlow(array, keys).To(&properties_written)) {
+ 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;
+ uint32_t properties_written = 0;
+ if (!KeyAccumulator::GetKeys(array, KeyCollectionMode::kOwnOnly,
+ ENUMERABLE_STRINGS)
+ .ToHandle(&keys) ||
+ !WriteJSObjectPropertiesSlow(array, keys).To(&properties_written)) {
+ return Nothing<bool>();
+ }
+ WriteTag(SerializationTag::kEndSparseJSArray);
+ WriteVarint<uint32_t>(properties_written);
+ WriteVarint<uint32_t>(length);
+ }
+ return ThrowIfOutOfMemory();
+}
+
+void ValueSerializer::WriteJSDate(JSDate date) {
+ WriteTag(SerializationTag::kDate);
+ WriteDouble(date.value().Number());
+}
+
+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());
+ } else if (inner_value.IsBigInt()) {
+ WriteTag(SerializationTag::kBigIntObject);
+ WriteBigIntContents(BigInt::cast(inner_value));
+ } else if (inner_value.IsString()) {
+ WriteTag(SerializationTag::kStringObject);
+ WriteString(handle(String::cast(inner_value), isolate_));
+ } else {
+ DCHECK(inner_value.IsSymbol());
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, value);
+ return Nothing<bool>();
+ }
+ return ThrowIfOutOfMemory();
+}
+
+void ValueSerializer::WriteJSRegExp(JSRegExp regexp) {
+ WriteTag(SerializationTag::kRegExp);
+ WriteString(handle(regexp.Pattern(), isolate_));
+ WriteVarint(static_cast<uint32_t>(regexp.GetFlags()));
+}
+
+Maybe<bool> ValueSerializer::WriteJSMap(Handle<JSMap> map) {
+ // First copy the key-value pairs, since getters could mutate them.
+ Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table()), isolate_);
+ int length = table->NumberOfElements() * 2;
+ Handle<FixedArray> entries = isolate_->factory()->NewFixedArray(length);
+ {
+ DisallowHeapAllocation no_gc;
+ Oddball the_hole = ReadOnlyRoots(isolate_).the_hole_value();
+ 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);
+ return ThrowIfOutOfMemory();
+}
+
+Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) {
+ // First copy the element pointers, since getters could mutate them.
+ Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()), isolate_);
+ int length = table->NumberOfElements();
+ Handle<FixedArray> entries = isolate_->factory()->NewFixedArray(length);
+ {
+ DisallowHeapAllocation no_gc;
+ Oddball the_hole = ReadOnlyRoots(isolate_).the_hole_value();
+ 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);
+ return ThrowIfOutOfMemory();
+}
+
+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());
+ return ThrowIfOutOfMemory();
+ }
+
+ uint32_t* transfer_entry = array_buffer_transfer_map_.Find(array_buffer);
+ if (transfer_entry) {
+ WriteTag(SerializationTag::kArrayBufferTransfer);
+ WriteVarint(*transfer_entry);
+ return ThrowIfOutOfMemory();
+ }
+ if (array_buffer->was_detached()) {
+ ThrowDataCloneError(MessageTemplate::kDataCloneErrorDetachedArrayBuffer);
+ return Nothing<bool>();
+ }
+ double byte_length = array_buffer->byte_length();
+ if (byte_length > std::numeric_limits<uint32_t>::max()) {
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, array_buffer);
+ return Nothing<bool>();
+ }
+ WriteTag(SerializationTag::kArrayBuffer);
+ WriteVarint<uint32_t>(byte_length);
+ WriteRawBytes(array_buffer->backing_store(), byte_length);
+ return ThrowIfOutOfMemory();
+}
+
+Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView view) {
+ if (treat_array_buffer_views_as_host_objects_) {
+ return WriteHostObject(handle(view, isolate_));
+ }
+ WriteTag(SerializationTag::kArrayBufferView);
+ ArrayBufferViewTag tag = ArrayBufferViewTag::kInt8Array;
+ if (view.IsJSTypedArray()) {
+ switch (JSTypedArray::cast(view).type()) {
+#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
+ case kExternal##Type##Array: \
+ tag = ArrayBufferViewTag::k##Type##Array; \
+ break;
+ TYPED_ARRAYS(TYPED_ARRAY_CASE)
+#undef TYPED_ARRAY_CASE
+ }
+ } else {
+ DCHECK(view.IsJSDataView());
+ tag = ArrayBufferViewTag::kDataView;
+ }
+ WriteVarint(static_cast<uint8_t>(tag));
+ WriteVarint(static_cast<uint32_t>(view.byte_offset()));
+ WriteVarint(static_cast<uint32_t>(view.byte_length()));
+ return ThrowIfOutOfMemory();
+}
+
+Maybe<bool> ValueSerializer::WriteWasmModule(Handle<WasmModuleObject> object) {
+ if (delegate_ != nullptr) {
+ // TODO(titzer): introduce a Utils::ToLocal for WasmModuleObject.
+ Maybe<uint32_t> transfer_id = delegate_->GetWasmModuleTransferId(
+ reinterpret_cast<v8::Isolate*>(isolate_),
+ v8::Local<v8::WasmModuleObject>::Cast(
+ Utils::ToLocal(Handle<JSObject>::cast(object))));
+ 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);
+ }
+ }
+
+ WasmEncodingTag encoding_tag = WasmEncodingTag::kRawBytes;
+ WriteTag(SerializationTag::kWasmModule);
+ WriteRawBytes(&encoding_tag, sizeof(encoding_tag));
+
+ 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()));
+ uint8_t* destination;
+ if (ReserveRawBytes(wire_bytes.size()).To(&destination)) {
+ memcpy(destination, wire_bytes.begin(), wire_bytes.size());
+ }
+
+ wasm::WasmSerializer wasm_serializer(native_module);
+ size_t module_size = wasm_serializer.GetSerializedNativeModuleSize();
+ 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)) {
+ if (!wasm_serializer.SerializeNativeModule({module_buffer, module_size})) {
+ return Nothing<bool>();
+ }
+ }
+ return ThrowIfOutOfMemory();
+}
+
+Maybe<bool> ValueSerializer::WriteWasmMemory(Handle<WasmMemoryObject> object) {
+ if (!object->array_buffer().is_shared()) {
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, object);
+ return Nothing<bool>();
+ }
+
+ isolate_->wasm_engine()->memory_tracker()->RegisterWasmMemoryAsShared(
+ object, isolate_);
+
+ WriteTag(SerializationTag::kWasmMemoryTransfer);
+ WriteZigZag<int32_t>(object->maximum_pages());
+ return WriteJSReceiver(Handle<JSReceiver>(object->array_buffer(), isolate_));
+}
+
+Maybe<bool> ValueSerializer::WriteHostObject(Handle<JSObject> object) {
+ WriteTag(SerializationTag::kHostObject);
+ 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>());
+ USE(result);
+ DCHECK(!result.IsNothing());
+ DCHECK(result.ToChecked());
+ return ThrowIfOutOfMemory();
+}
+
+Maybe<uint32_t> ValueSerializer::WriteJSObjectPropertiesSlow(
+ 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);
+}
+
+void ValueSerializer::ThrowDataCloneError(MessageTemplate template_index) {
+ return ThrowDataCloneError(template_index,
+ isolate_->factory()->empty_string());
+}
+
+Maybe<bool> ValueSerializer::ThrowIfOutOfMemory() {
+ if (out_of_memory_) {
+ ThrowDataCloneError(MessageTemplate::kDataCloneErrorOutOfMemory);
+ return Nothing<bool>();
+ }
+ return Just(true);
+}
+
+void ValueSerializer::ThrowDataCloneError(MessageTemplate index,
+ Handle<Object> arg0) {
+ Handle<String> message = MessageFormatter::Format(isolate_, index, arg0);
+ if (delegate_) {
+ delegate_->ThrowDataCloneError(Utils::ToLocal(message));
+ } else {
+ isolate_->Throw(
+ *isolate_->factory()->NewError(isolate_->error_function(), message));
+ }
+ if (isolate_->has_scheduled_exception()) {
+ isolate_->PromoteScheduledException();
+ }
+}
+
+ValueDeserializer::ValueDeserializer(Isolate* isolate,
+ Vector<const uint8_t> data,
+ v8::ValueDeserializer::Delegate* delegate)
+ : isolate_(isolate),
+ delegate_(delegate),
+ position_(data.begin()),
+ end_(data.begin() + data.length()),
+ allocation_(data.length() > kPretenureThreshold ? AllocationType::kOld
+ : AllocationType::kYoung),
+ id_map_(isolate->global_handles()->Create(
+ ReadOnlyRoots(isolate_).empty_fixed_array())) {}
+
+ValueDeserializer::~ValueDeserializer() {
+ GlobalHandles::Destroy(id_map_.location());
+
+ Handle<Object> transfer_map_handle;
+ if (array_buffer_transfer_map_.ToHandle(&transfer_map_handle)) {
+ GlobalHandles::Destroy(transfer_map_handle.location());
+ }
+}
+
+Maybe<bool> ValueDeserializer::ReadHeader() {
+ if (position_ < end_ &&
+ *position_ == static_cast<uint8_t>(SerializationTag::kVersion)) {
+ ReadTag().ToChecked();
+ if (!ReadVarint<uint32_t>().To(&version_) || version_ > kLatestVersion) {
+ isolate_->Throw(*isolate_->factory()->NewError(
+ MessageTemplate::kDataCloneDeserializationVersionError));
+ return Nothing<bool>();
+ }
+ }
+ return Just(true);
+}
+
+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);
+}
+
+void ValueDeserializer::ConsumeTag(SerializationTag peeked_tag) {
+ SerializationTag actual_tag = ReadTag().ToChecked();
+ DCHECK(actual_tag == peeked_tag);
+ USE(actual_tag);
+}
+
+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)) {
+ value |= static_cast<T>(byte & 0x7F) << shift;
+ shift += 7;
+ }
+ has_another_byte = byte & 0x80;
+ position_++;
+ } while (has_another_byte);
+ return Just(value);
+}
+
+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);
+}
+
+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));
+}
+
+bool ValueDeserializer::ReadUint32(uint32_t* value) {
+ return ReadVarint<uint32_t>().To(value);
+}
+
+bool ValueDeserializer::ReadUint64(uint64_t* value) {
+ return ReadVarint<uint64_t>().To(value);
+}
+
+bool ValueDeserializer::ReadDouble(double* value) {
+ return ReadDouble().To(value);
+}
+
+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;
+}
+
+void ValueDeserializer::TransferArrayBuffer(
+ uint32_t transfer_id, Handle<JSArrayBuffer> array_buffer) {
+ if (array_buffer_transfer_map_.is_null()) {
+ array_buffer_transfer_map_ = isolate_->global_handles()->Create(
+ *SimpleNumberDictionary::New(isolate_, 0));
+ }
+ Handle<SimpleNumberDictionary> dictionary =
+ array_buffer_transfer_map_.ToHandleChecked();
+ Handle<SimpleNumberDictionary> new_dictionary = SimpleNumberDictionary::Set(
+ isolate_, dictionary, transfer_id, array_buffer);
+ if (!new_dictionary.is_identical_to(dictionary)) {
+ GlobalHandles::Destroy(dictionary.location());
+ array_buffer_transfer_map_ =
+ isolate_->global_handles()->Create(*new_dictionary);
+ }
+}
+
+MaybeHandle<Object> ValueDeserializer::ReadObject() {
+ DisallowJavascriptExecution no_js(isolate_);
+ // If we are at the end of the stack, abort. This function may recurse.
+ STACK_CHECK(isolate_, MaybeHandle<Object>());
+
+ 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));
+ }
+
+ if (result.is_null() && !isolate_->has_pending_exception()) {
+ isolate_->Throw(*isolate_->factory()->NewError(
+ MessageTemplate::kDataCloneDeserializationError));
+ }
+
+ return result;
+}
+
+MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
+ 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();
+ case SerializationTag::kInt32: {
+ Maybe<int32_t> number = ReadZigZag<int32_t>();
+ if (number.IsNothing()) return MaybeHandle<Object>();
+ return isolate_->factory()->NewNumberFromInt(number.FromJust(),
+ allocation_);
+ }
+ case SerializationTag::kUint32: {
+ Maybe<uint32_t> number = ReadVarint<uint32_t>();
+ if (number.IsNothing()) return MaybeHandle<Object>();
+ return isolate_->factory()->NewNumberFromUint(number.FromJust(),
+ allocation_);
+ }
+ case SerializationTag::kDouble: {
+ Maybe<double> number = ReadDouble();
+ if (number.IsNothing()) return MaybeHandle<Object>();
+ return isolate_->factory()->NewNumber(number.FromJust(), allocation_);
+ }
+ case SerializationTag::kBigInt:
+ return ReadBigInt();
+ case SerializationTag::kUtf8String:
+ return ReadUtf8String();
+ case SerializationTag::kOneByteString:
+ return ReadOneByteString();
+ case SerializationTag::kTwoByteString:
+ return ReadTwoByteString();
+ case SerializationTag::kObjectReference: {
+ uint32_t id;
+ if (!ReadVarint<uint32_t>().To(&id)) return MaybeHandle<Object>();
+ return GetObjectWithID(id);
+ }
+ case SerializationTag::kBeginJSObject:
+ return ReadJSObject();
+ case SerializationTag::kBeginSparseJSArray:
+ return ReadSparseJSArray();
+ case SerializationTag::kBeginDenseJSArray:
+ return ReadDenseJSArray();
+ case SerializationTag::kDate:
+ return ReadJSDate();
+ case SerializationTag::kTrueObject:
+ case SerializationTag::kFalseObject:
+ case SerializationTag::kNumberObject:
+ case SerializationTag::kBigIntObject:
+ case SerializationTag::kStringObject:
+ return ReadJSValue(tag);
+ case SerializationTag::kRegExp:
+ return ReadJSRegExp();
+ case SerializationTag::kBeginJSMap:
+ return ReadJSMap();
+ case SerializationTag::kBeginJSSet:
+ return ReadJSSet();
+ case SerializationTag::kArrayBuffer: {
+ const bool is_shared = false;
+ return ReadJSArrayBuffer(is_shared);
+ }
+ case SerializationTag::kArrayBufferTransfer: {
+ return ReadTransferredJSArrayBuffer();
+ }
+ case SerializationTag::kSharedArrayBuffer: {
+ const bool is_shared = true;
+ return ReadJSArrayBuffer(is_shared);
+ }
+ case SerializationTag::kWasmModule:
+ return ReadWasmModule();
+ case SerializationTag::kWasmModuleTransfer:
+ return ReadWasmModuleTransfer();
+ case SerializationTag::kWasmMemoryTransfer:
+ return ReadWasmMemory();
+ case SerializationTag::kHostObject:
+ return ReadHostObject();
+ 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>();
+ }
+}
+
+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);
+}
+
+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,
+ allocation_);
+}
+
+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()) ||
+ !ReadRawBytes(utf8_length).To(&utf8_bytes)) {
+ return MaybeHandle<String>();
+ }
+ return isolate_->factory()->NewStringFromUtf8(
+ Vector<const char>::cast(utf8_bytes), allocation_);
+}
+
+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, allocation_);
+}
+
+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()) ||
+ byte_length % sizeof(uc16) != 0 ||
+ !ReadRawBytes(byte_length).To(&bytes)) {
+ return MaybeHandle<String>();
+ }
+
+ // Allocate an uninitialized string so that we can do a raw memcpy into the
+ // string on the heap (regardless of alignment).
+ if (byte_length == 0) return isolate_->factory()->empty_string();
+ Handle<SeqTwoByteString> string;
+ if (!isolate_->factory()
+ ->NewRawTwoByteString(byte_length / sizeof(uc16), allocation_)
+ .ToHandle(&string)) {
+ return MaybeHandle<String>();
+ }
+
+ // Copy the bytes directly into the new string.
+ // Warning: this uses host endianness.
+ DisallowHeapAllocation no_gc;
+ memcpy(string->GetChars(no_gc), bytes.begin(), bytes.length());
+ return string;
+}
+
+bool ValueDeserializer::ReadExpectedString(Handle<String> expected) {
+ DisallowHeapAllocation no_gc;
+ // 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;
+ }
+
+ String::FlatContent flat = expected->GetFlatContent(no_gc);
+
+ // If the bytes are verbatim what is in the flattened string, then the string
+ // is successfully consumed.
+ if (tag == SerializationTag::kOneByteString && flat.IsOneByte()) {
+ Vector<const uint8_t> chars = flat.ToOneByteVector();
+ if (byte_length == static_cast<size_t>(chars.length()) &&
+ 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;
+ }
+ } 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;
+ }
+ }
+
+ position_ = original_position;
+ return false;
+}
+
+MaybeHandle<JSObject> ValueDeserializer::ReadJSObject() {
+ // If we are at the end of the stack, abort. This function may recurse.
+ STACK_CHECK(isolate_, MaybeHandle<JSObject>());
+
+ uint32_t id = next_id_++;
+ HandleScope scope(isolate_);
+ Handle<JSObject> object = isolate_->factory()->NewJSObject(
+ isolate_->object_function(), allocation_);
+ AddObjectWithID(id, object);
+
+ uint32_t num_properties;
+ uint32_t expected_num_properties;
+ if (!ReadJSObjectProperties(object, SerializationTag::kEndJSObject, true)
+ .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);
+}
+
+MaybeHandle<JSArray> ValueDeserializer::ReadSparseJSArray() {
+ // If we are at the end of the stack, abort. This function may recurse.
+ STACK_CHECK(isolate_, MaybeHandle<JSArray>());
+
+ uint32_t length;
+ if (!ReadVarint<uint32_t>().To(&length)) return MaybeHandle<JSArray>();
+
+ uint32_t id = next_id_++;
+ HandleScope scope(isolate_);
+ Handle<JSArray> array = isolate_->factory()->NewJSArray(
+ 0, TERMINAL_FAST_ELEMENTS_KIND, allocation_);
+ JSArray::SetLength(array, length);
+ AddObjectWithID(id, array);
+
+ uint32_t num_properties;
+ uint32_t expected_num_properties;
+ uint32_t expected_length;
+ if (!ReadJSObjectProperties(array, SerializationTag::kEndSparseJSArray, false)
+ .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.
+ STACK_CHECK(isolate_, MaybeHandle<JSArray>());
+
+ // 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.
+ uint32_t length;
+ if (!ReadVarint<uint32_t>().To(&length) ||
+ length > static_cast<uint32_t>(FixedArray::kMaxLength) ||
+ length > static_cast<size_t>(end_ - position_)) {
+ return MaybeHandle<JSArray>();
+ }
+
+ uint32_t id = next_id_++;
+ HandleScope scope(isolate_);
+ Handle<JSArray> array = isolate_->factory()->NewJSArray(
+ HOLEY_ELEMENTS, length, length, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE,
+ allocation_);
+ AddObjectWithID(id, array);
+
+ Handle<FixedArray> elements(FixedArray::cast(array->elements()), isolate_);
+ for (uint32_t i = 0; i < length; i++) {
+ SerializationTag tag;
+ if (PeekTag().To(&tag) && tag == SerializationTag::kTheHole) {
+ ConsumeTag(SerializationTag::kTheHole);
+ continue;
+ }
+
+ Handle<Object> element;
+ if (!ReadObject().ToHandle(&element)) return MaybeHandle<JSArray>();
+
+ // 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;
+
+ // Safety check.
+ if (i >= static_cast<uint32_t>(elements->length())) {
+ return MaybeHandle<JSArray>();
+ }
+
+ elements->set(i, *element);
+ }
+
+ uint32_t num_properties;
+ uint32_t expected_num_properties;
+ uint32_t expected_length;
+ if (!ReadJSObjectProperties(array, SerializationTag::kEndDenseJSArray, false)
+ .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<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;
+}
+
+MaybeHandle<JSValue> ValueDeserializer::ReadJSValue(SerializationTag tag) {
+ uint32_t id = next_id_++;
+ Handle<JSValue> value;
+ switch (tag) {
+ case SerializationTag::kTrueObject:
+ value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
+ isolate_->boolean_function(), allocation_));
+ value->set_value(ReadOnlyRoots(isolate_).true_value());
+ break;
+ case SerializationTag::kFalseObject:
+ value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
+ isolate_->boolean_function(), allocation_));
+ value->set_value(ReadOnlyRoots(isolate_).false_value());
+ break;
+ case SerializationTag::kNumberObject: {
+ double number;
+ if (!ReadDouble().To(&number)) return MaybeHandle<JSValue>();
+ value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
+ isolate_->number_function(), allocation_));
+ Handle<Object> number_object =
+ isolate_->factory()->NewNumber(number, allocation_);
+ value->set_value(*number_object);
+ break;
+ }
+ case SerializationTag::kBigIntObject: {
+ Handle<BigInt> bigint;
+ if (!ReadBigInt().ToHandle(&bigint)) return MaybeHandle<JSValue>();
+ value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
+ isolate_->bigint_function(), allocation_));
+ value->set_value(*bigint);
+ break;
+ }
+ case SerializationTag::kStringObject: {
+ Handle<String> string;
+ if (!ReadString().ToHandle(&string)) return MaybeHandle<JSValue>();
+ value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
+ isolate_->string_function(), allocation_));
+ value->set_value(*string);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ AddObjectWithID(id, value);
+ return value;
+}
+
+MaybeHandle<JSRegExp> ValueDeserializer::ReadJSRegExp() {
+ uint32_t id = next_id_++;
+ Handle<String> pattern;
+ uint32_t raw_flags;
+ Handle<JSRegExp> regexp;
+ if (!ReadString().ToHandle(&pattern) ||
+ !ReadVarint<uint32_t>().To(&raw_flags)) {
+ return MaybeHandle<JSRegExp>();
+ }
+
+ // Ensure the deserialized flags are valid.
+ // TODO(adamk): Can we remove this check now that dotAll is always-on?
+ uint32_t flags_mask = static_cast<uint32_t>(-1) << JSRegExp::FlagCount();
+ if ((raw_flags & flags_mask) ||
+ !JSRegExp::New(isolate_, pattern, static_cast<JSRegExp::Flags>(raw_flags))
+ .ToHandle(&regexp)) {
+ return MaybeHandle<JSRegExp>();
+ }
+
+ AddObjectWithID(id, regexp);
+ return regexp;
+}
+
+MaybeHandle<JSMap> ValueDeserializer::ReadJSMap() {
+ // If we are at the end of the stack, abort. This function may recurse.
+ STACK_CHECK(isolate_, MaybeHandle<JSMap>());
+
+ 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];
+ 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)
+ .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.
+ STACK_CHECK(isolate_, MaybeHandle<JSSet>());
+
+ 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];
+ if (!ReadObject().ToHandle(&argv[0])) return MaybeHandle<JSSet>();
+
+ AllowJavascriptExecution allow_js(isolate_);
+ if (Execution::Call(isolate_, set_add, set, arraysize(argv), argv)
+ .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);
+}
+
+MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadJSArrayBuffer(
+ bool is_shared) {
+ uint32_t id = next_id_++;
+ 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;
+ }
+ 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;
+ Handle<JSArrayBuffer> array_buffer = isolate_->factory()->NewJSArrayBuffer(
+ SharedFlag::kNotShared, allocation_);
+ if (!JSArrayBuffer::SetupAllocatingData(array_buffer, isolate_, byte_length,
+ should_initialize)) {
+ return MaybeHandle<JSArrayBuffer>();
+ }
+ if (byte_length > 0) {
+ memcpy(array_buffer->backing_store(), position_, byte_length);
+ }
+ position_ += byte_length;
+ AddObjectWithID(id, array_buffer);
+ return array_buffer;
+}
+
+MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer() {
+ uint32_t id = next_id_++;
+ uint32_t transfer_id;
+ Handle<SimpleNumberDictionary> transfer_map;
+ 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);
+ if (index == SimpleNumberDictionary::kNotFound) {
+ return MaybeHandle<JSArrayBuffer>();
+ }
+ Handle<JSArrayBuffer> array_buffer(
+ JSArrayBuffer::cast(transfer_map->ValueAt(index)), isolate_);
+ AddObjectWithID(id, array_buffer);
+ return array_buffer;
+}
+
+MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
+ Handle<JSArrayBuffer> buffer) {
+ uint32_t buffer_byte_length = static_cast<uint32_t>(buffer->byte_length());
+ uint8_t tag = 0;
+ uint32_t byte_offset = 0;
+ uint32_t byte_length = 0;
+ 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;
+
+ 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;
+ }
+#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
+ case ArrayBufferViewTag::k##Type##Array: \
+ external_array_type = kExternal##Type##Array; \
+ element_size = sizeof(ctype); \
+ break;
+ TYPED_ARRAYS(TYPED_ARRAY_CASE)
+#undef TYPED_ARRAY_CASE
+ }
+ if (element_size == 0 || byte_offset % element_size != 0 ||
+ byte_length % element_size != 0) {
+ return MaybeHandle<JSArrayBufferView>();
+ }
+ Handle<JSTypedArray> typed_array = isolate_->factory()->NewJSTypedArray(
+ external_array_type, buffer, byte_offset, byte_length / element_size,
+ allocation_);
+ AddObjectWithID(id, typed_array);
+ return typed_array;
+}
+
+MaybeHandle<JSObject> ValueDeserializer::ReadWasmModuleTransfer() {
+ auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
+ if ((FLAG_wasm_disable_structured_cloning && !enabled_features.threads) ||
+ expect_inline_wasm()) {
+ 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;
+}
+
+MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
+ auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
+ if ((FLAG_wasm_disable_structured_cloning && !enabled_features.threads) ||
+ !expect_inline_wasm()) {
+ return MaybeHandle<JSObject>();
+ }
+
+ 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.
+ MaybeHandle<WasmModuleObject> result =
+ wasm::DeserializeNativeModule(isolate_, compiled_bytes, wire_bytes);
+ if (result.is_null()) {
+ wasm::ErrorThrower thrower(isolate_, "ValueDeserializer::ReadWasmModule");
+ // TODO(titzer): are the current features appropriate for deserializing?
+ auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
+ result = isolate_->wasm_engine()->SyncCompile(
+ isolate_, enabled_features, &thrower,
+ wasm::ModuleWireBytes(wire_bytes));
+ }
+ uint32_t id = next_id_++;
+ if (!result.is_null()) {
+ AddObjectWithID(id, result.ToHandleChecked());
+ }
+ return result;
+}
+
+MaybeHandle<WasmMemoryObject> ValueDeserializer::ReadWasmMemory() {
+ uint32_t id = next_id_++;
+
+ auto enabled_features = wasm::WasmFeaturesFromIsolate(isolate_);
+ if (!enabled_features.threads) {
+ return MaybeHandle<WasmMemoryObject>();
+ }
+
+ int32_t maximum_pages;
+ if (!ReadZigZag<int32_t>().To(&maximum_pages)) {
+ return MaybeHandle<WasmMemoryObject>();
+ }
+
+ SerializationTag tag;
+ if (!ReadTag().To(&tag) || tag != SerializationTag::kSharedArrayBuffer) {
+ return MaybeHandle<WasmMemoryObject>();
+ }
+
+ const bool is_shared = true;
+ Handle<JSArrayBuffer> buffer;
+ if (!ReadJSArrayBuffer(is_shared).ToHandle(&buffer)) {
+ return MaybeHandle<WasmMemoryObject>();
+ }
+
+ Handle<WasmMemoryObject> result =
+ WasmMemoryObject::New(isolate_, buffer, maximum_pages);
+
+ isolate_->wasm_engine()->memory_tracker()->RegisterWasmMemoryAsShared(
+ result, isolate_);
+
+ AddObjectWithID(id, result);
+ return result;
+}
+
+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;
+}
+
+// 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;
+ DescriptorArray descriptors = object->map().instance_descriptors();
+ for (unsigned i = 0; i < properties.size(); i++) {
+ // Initializing store.
+ object->WriteToField(i, descriptors.GetDetails(i), *properties[i]);
+ }
+}
+
+static bool IsValidObjectKey(Handle<Object> value) {
+ return value->IsName() || value->IsNumber();
+}
+
+Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
+ 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());
+ DCHECK_EQ(0, map->instance_descriptors().number_of_descriptors());
+ 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;
+ TransitionsAccessor transitions(isolate_, map);
+ Handle<String> expected_key = transitions.ExpectedTransitionKey();
+ if (!expected_key.is_null() && ReadExpectedString(expected_key)) {
+ key = expected_key;
+ target = transitions.ExpectedTransitionTarget();
+ } else {
+ if (!ReadObject().ToHandle(&key) || !IsValidObjectKey(key)) {
+ return Nothing<uint32_t>();
+ }
+ if (key->IsString()) {
+ key =
+ isolate_->factory()->InternalizeString(Handle<String>::cast(key));
+ // Don't reuse |transitions| because it could be stale.
+ transitioning = TransitionsAccessor(isolate_, map)
+ .FindTransitionToField(Handle<String>::cast(key))
+ .ToHandle(&target);
+ } 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);
+ Map::GeneralizeField(isolate_, target, descriptor,
+ details.constness(), expected_representation,
+ value_type);
+ }
+ 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);
+ if (!success || it.state() != LookupIterator::NOT_FOUND ||
+ 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++) {
+ SerializationTag tag;
+ if (!PeekTag().To(&tag)) return Nothing<uint32_t>();
+ if (tag == end_tag) {
+ ConsumeTag(end_tag);
+ return Just(num_properties);
+ }
+
+ Handle<Object> key;
+ if (!ReadObject().ToHandle(&key) || !IsValidObjectKey(key)) {
+ return Nothing<uint32_t>();
+ }
+ Handle<Object> value;
+ if (!ReadObject().ToHandle(&value)) return Nothing<uint32_t>();
+
+ bool success;
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ isolate_, object, key, &success, LookupIterator::OWN);
+ if (!success || it.state() != LookupIterator::NOT_FOUND ||
+ JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE)
+ .is_null()) {
+ return Nothing<uint32_t>();
+ }
+ }
+}
+
+bool ValueDeserializer::HasObjectWithID(uint32_t id) {
+ return id < static_cast<unsigned>(id_map_->length()) &&
+ !id_map_->get(id).IsTheHole(isolate_);
+}
+
+MaybeHandle<JSReceiver> ValueDeserializer::GetObjectWithID(uint32_t id) {
+ if (id >= static_cast<unsigned>(id_map_->length())) {
+ return MaybeHandle<JSReceiver>();
+ }
+ Object value = id_map_->get(id);
+ if (value.IsTheHole(isolate_)) return MaybeHandle<JSReceiver>();
+ DCHECK(value.IsJSReceiver());
+ return Handle<JSReceiver>(JSReceiver::cast(value), isolate_);
+}
+
+void ValueDeserializer::AddObjectWithID(uint32_t id,
+ Handle<JSReceiver> object) {
+ DCHECK(!HasObjectWithID(id));
+ Handle<FixedArray> new_array =
+ FixedArray::SetAndGrow(isolate_, id_map_, id, object);
+
+ // If the dictionary was reallocated, update the global handle.
+ if (!new_array.is_identical_to(id_map_)) {
+ GlobalHandles::Destroy(id_map_.location());
+ id_map_ = isolate_->global_handles()->Create(*new_array);
+ }
+}
+
+static Maybe<bool> SetPropertiesFromKeyValuePairs(Isolate* isolate,
+ Handle<JSObject> object,
+ Handle<Object>* data,
+ uint32_t num_properties) {
+ for (unsigned i = 0; i < 2 * num_properties; i += 2) {
+ Handle<Object> key = data[i];
+ if (!IsValidObjectKey(key)) return Nothing<bool>();
+ Handle<Object> value = data[i + 1];
+ bool success;
+ LookupIterator it = LookupIterator::PropertyOrElement(
+ isolate, object, key, &success, LookupIterator::OWN);
+ if (!success || it.state() != LookupIterator::NOT_FOUND ||
+ JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE)
+ .is_null()) {
+ return Nothing<bool>();
+ }
+ }
+ return Just(true);
+}
+
+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
+
+MaybeHandle<Object>
+ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() {
+ DCHECK_EQ(version_, 0u);
+ 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) {
+ isolate_->Throw(*isolate_->factory()->NewError(
+ MessageTemplate::kDataCloneDeserializationError));
+ return MaybeHandle<Object>();
+ }
+
+ size_t begin_properties =
+ stack.size() - 2 * static_cast<size_t>(num_properties);
+ Handle<JSObject> js_object = isolate_->factory()->NewJSObject(
+ isolate_->object_function(), allocation_);
+ if (num_properties &&
+ !SetPropertiesFromKeyValuePairs(
+ isolate_, js_object, &stack[begin_properties], num_properties)
+ .FromMaybe(false)) {
+ ThrowDeserializationExceptionIfNonePending(isolate_);
+ 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) {
+ isolate_->Throw(*isolate_->factory()->NewError(
+ MessageTemplate::kDataCloneDeserializationError));
+ return MaybeHandle<Object>();
+ }
+
+ Handle<JSArray> js_array = isolate_->factory()->NewJSArray(
+ 0, TERMINAL_FAST_ELEMENTS_KIND, allocation_);
+ 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)) {
+ ThrowDeserializationExceptionIfNonePending(isolate_);
+ return MaybeHandle<Object>();
+ }
+
+ stack.resize(begin_properties);
+ new_object = js_array;
+ break;
+ }
+ case SerializationTag::kEndDenseJSArray: {
+ // This was already broken in Chromium, and apparently wasn't missed.
+ isolate_->Throw(*isolate_->factory()->NewError(
+ MessageTemplate::kDataCloneDeserializationError));
+ return MaybeHandle<Object>();
+ }
+ 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_;
+
+ if (stack.size() != 1) {
+ isolate_->Throw(*isolate_->factory()->NewError(
+ MessageTemplate::kDataCloneDeserializationError));
+ return MaybeHandle<Object>();
+ }
+ return scope.CloseAndEscape(stack[0]);
+}
+
+} // namespace internal
+} // namespace v8