aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/value-serializer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/value-serializer.cc')
-rw-r--r--deps/v8/src/value-serializer.cc890
1 files changed, 824 insertions, 66 deletions
diff --git a/deps/v8/src/value-serializer.cc b/deps/v8/src/value-serializer.cc
index 0af4838abf..1d2e36dc04 100644
--- a/deps/v8/src/value-serializer.cc
+++ b/deps/v8/src/value-serializer.cc
@@ -7,16 +7,19 @@
#include <type_traits>
#include "src/base/logging.h"
+#include "src/conversions.h"
#include "src/factory.h"
#include "src/handles-inl.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/objects.h"
+#include "src/transitions.h"
namespace v8 {
namespace internal {
static const uint32_t kLatestVersion = 9;
+static const int kPretenureThreshold = 100 * KB;
template <typename T>
static size_t BytesNeededForVarint(T value) {
@@ -82,12 +85,54 @@ enum class SerializationTag : uint8_t {
// 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 (transferred). transferID:uint32_t
+ kSharedArrayBufferTransfer = 'u',
};
-ValueSerializer::ValueSerializer(Isolate* isolate)
+namespace {
+
+enum class ArrayBufferViewTag : uint8_t {
+ kInt8Array = 'b',
+ kUint8Array = 'B',
+ kUint8ClampedArray = 'C',
+ kInt16Array = 'w',
+ kUint16Array = 'W',
+ kInt32Array = 'd',
+ kUint32Array = 'D',
+ kFloat32Array = 'f',
+ kFloat64Array = 'F',
+ kDataView = '?',
+};
+
+} // namespace
+
+ValueSerializer::ValueSerializer(Isolate* isolate,
+ v8::ValueSerializer::Delegate* delegate)
: isolate_(isolate),
+ delegate_(delegate),
zone_(isolate->allocator()),
- id_map_(isolate->heap(), &zone_) {}
+ id_map_(isolate->heap(), &zone_),
+ array_buffer_transfer_map_(isolate->heap(), &zone_) {}
ValueSerializer::~ValueSerializer() {}
@@ -150,6 +195,11 @@ void ValueSerializer::WriteTwoByteString(Vector<const uc16> chars) {
reinterpret_cast<const uint8_t*>(chars.end()));
}
+void ValueSerializer::WriteRawBytes(const void* source, size_t length) {
+ const uint8_t* begin = reinterpret_cast<const uint8_t*>(source);
+ buffer_.insert(buffer_.end(), begin, begin + length);
+}
+
uint8_t* ValueSerializer::ReserveRawBytes(size_t bytes) {
if (!bytes) return nullptr;
auto old_size = buffer_.size();
@@ -157,6 +207,20 @@ uint8_t* ValueSerializer::ReserveRawBytes(size_t bytes) {
return &buffer_[old_size];
}
+void ValueSerializer::WriteUint32(uint32_t value) {
+ WriteVarint<uint32_t>(value);
+}
+
+void ValueSerializer::WriteUint64(uint64_t value) {
+ WriteVarint<uint64_t>(value);
+}
+
+void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id,
+ Handle<JSArrayBuffer> array_buffer) {
+ DCHECK(!array_buffer_transfer_map_.Find(array_buffer));
+ array_buffer_transfer_map_.Set(array_buffer, transfer_id);
+}
+
Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
if (object->IsSmi()) {
WriteSmi(Smi::cast(*object));
@@ -172,15 +236,33 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
case MUTABLE_HEAP_NUMBER_TYPE:
WriteHeapNumber(HeapNumber::cast(*object));
return Just(true);
+ 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)) {
+ 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 Just(true);
} else if (object->IsJSReceiver()) {
return WriteJSReceiver(Handle<JSReceiver>::cast(object));
+ } else {
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, object);
+ return Nothing<bool>();
}
- UNIMPLEMENTED();
- return Nothing<bool>();
}
}
@@ -267,20 +349,27 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
// Eliminate callable and exotic objects, which should not be serialized.
InstanceType instance_type = receiver->map()->instance_type();
- if (receiver->IsCallable() || instance_type <= LAST_SPECIAL_RECEIVER_TYPE) {
+ if (receiver->IsCallable() || (instance_type <= LAST_SPECIAL_RECEIVER_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.
- if (StackLimitCheck(isolate_).HasOverflowed()) return Nothing<bool>();
+ 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:
- return WriteJSObject(Handle<JSObject>::cast(receiver));
+ case JS_API_OBJECT_TYPE: {
+ Handle<JSObject> js_object = Handle<JSObject>::cast(receiver);
+ return js_object->GetInternalFieldCount() ? WriteHostObject(js_object)
+ : WriteJSObject(js_object);
+ }
+ case JS_SPECIAL_API_OBJECT_TYPE:
+ return WriteHostObject(Handle<JSObject>::cast(receiver));
case JS_DATE_TYPE:
WriteJSDate(JSDate::cast(*receiver));
return Just(true);
@@ -289,21 +378,76 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
case JS_REGEXP_TYPE:
WriteJSRegExp(JSRegExp::cast(*receiver));
return Just(true);
+ 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(JSArrayBuffer::cast(*receiver));
+ case JS_TYPED_ARRAY_TYPE:
+ case JS_DATA_VIEW_TYPE:
+ return WriteJSArrayBufferView(JSArrayBufferView::cast(*receiver));
default:
- UNIMPLEMENTED();
- break;
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, receiver);
+ return Nothing<bool>();
}
return Nothing<bool>();
}
Maybe<bool> ValueSerializer::WriteJSObject(Handle<JSObject> object) {
+ DCHECK_GT(object->map()->instance_type(), LAST_CUSTOM_ELEMENTS_RECEIVER);
+ 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.type() == DATA)) {
+ 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 Just(true);
+}
+
+Maybe<bool> ValueSerializer::WriteJSObjectSlow(Handle<JSObject> object) {
WriteTag(SerializationTag::kBeginJSObject);
Handle<FixedArray> keys;
uint32_t properties_written;
if (!KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS)
.ToHandle(&keys) ||
- !WriteJSObjectProperties(object, keys).To(&properties_written)) {
+ !WriteJSObjectPropertiesSlow(object, keys).To(&properties_written)) {
return Nothing<bool>();
}
WriteTag(SerializationTag::kEndJSObject);
@@ -331,7 +475,46 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
// format changes.
WriteTag(SerializationTag::kBeginDenseJSArray);
WriteVarint<uint32_t>(length);
- for (uint32_t i = 0; i < length; i++) {
+ uint32_t i = 0;
+
+ // Fast paths. Note that FAST_ELEMENTS in particular can bail due to the
+ // structure of the elements changing.
+ switch (array->GetElementsKind()) {
+ case FAST_SMI_ELEMENTS: {
+ Handle<FixedArray> elements(FixedArray::cast(array->elements()),
+ isolate_);
+ for (; i < length; i++) WriteSmi(Smi::cast(elements->get(i)));
+ break;
+ }
+ case FAST_DOUBLE_ELEMENTS: {
+ Handle<FixedDoubleArray> elements(
+ FixedDoubleArray::cast(array->elements()), isolate_);
+ for (; i < length; i++) {
+ WriteTag(SerializationTag::kDouble);
+ WriteDouble(elements->get_scalar(i));
+ }
+ break;
+ }
+ case FAST_ELEMENTS: {
+ Handle<Object> old_length(array->length(), isolate_);
+ for (; i < length; i++) {
+ if (array->length() != *old_length ||
+ array->GetElementsKind() != FAST_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.
@@ -342,6 +525,7 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
return Nothing<bool>();
}
}
+
KeyAccumulator accumulator(isolate_, KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS);
if (!accumulator.CollectOwnPropertyNames(array, array).FromMaybe(false)) {
@@ -350,7 +534,7 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
Handle<FixedArray> keys =
accumulator.GetKeys(GetKeysConversion::kConvertToString);
uint32_t properties_written;
- if (!WriteJSObjectProperties(array, keys).To(&properties_written)) {
+ if (!WriteJSObjectPropertiesSlow(array, keys).To(&properties_written)) {
return Nothing<bool>();
}
WriteTag(SerializationTag::kEndDenseJSArray);
@@ -364,7 +548,7 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
if (!KeyAccumulator::GetKeys(array, KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS)
.ToHandle(&keys) ||
- !WriteJSObjectProperties(array, keys).To(&properties_written)) {
+ !WriteJSObjectPropertiesSlow(array, keys).To(&properties_written)) {
return Nothing<bool>();
}
WriteTag(SerializationTag::kEndSparseJSArray);
@@ -401,6 +585,7 @@ Maybe<bool> ValueSerializer::WriteJSValue(Handle<JSValue> value) {
v8::String::NO_NULL_TERMINATION);
} else {
DCHECK(inner_value->IsSymbol());
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, value);
return Nothing<bool>();
}
return Just(true);
@@ -417,7 +602,135 @@ void ValueSerializer::WriteJSRegExp(JSRegExp* regexp) {
WriteVarint(static_cast<uint32_t>(regexp->GetFlags()));
}
-Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties(
+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()));
+ int length = table->NumberOfElements() * 2;
+ Handle<FixedArray> entries = isolate_->factory()->NewFixedArray(length);
+ {
+ DisallowHeapAllocation no_gc;
+ Oddball* the_hole = isolate_->heap()->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 Just(true);
+}
+
+Maybe<bool> ValueSerializer::WriteJSSet(Handle<JSSet> set) {
+ // First copy the element pointers, since getters could mutate them.
+ Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()));
+ int length = table->NumberOfElements();
+ Handle<FixedArray> entries = isolate_->factory()->NewFixedArray(length);
+ {
+ DisallowHeapAllocation no_gc;
+ Oddball* the_hole = isolate_->heap()->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 Just(true);
+}
+
+Maybe<bool> ValueSerializer::WriteJSArrayBuffer(JSArrayBuffer* array_buffer) {
+ uint32_t* transfer_entry = array_buffer_transfer_map_.Find(array_buffer);
+ if (transfer_entry) {
+ DCHECK(array_buffer->was_neutered() || array_buffer->is_shared());
+ WriteTag(array_buffer->is_shared()
+ ? SerializationTag::kSharedArrayBufferTransfer
+ : SerializationTag::kArrayBufferTransfer);
+ WriteVarint(*transfer_entry);
+ return Just(true);
+ }
+
+ if (array_buffer->is_shared()) {
+ ThrowDataCloneError(
+ MessageTemplate::kDataCloneErrorSharedArrayBufferNotTransferred);
+ return Nothing<bool>();
+ }
+ if (array_buffer->was_neutered()) {
+ ThrowDataCloneError(MessageTemplate::kDataCloneErrorNeuteredArrayBuffer);
+ return Nothing<bool>();
+ }
+ double byte_length = array_buffer->byte_length()->Number();
+ if (byte_length > std::numeric_limits<uint32_t>::max()) {
+ ThrowDataCloneError(MessageTemplate::kDataCloneError, handle(array_buffer));
+ return Nothing<bool>();
+ }
+ WriteTag(SerializationTag::kArrayBuffer);
+ WriteVarint<uint32_t>(byte_length);
+ WriteRawBytes(array_buffer->backing_store(), byte_length);
+ return Just(true);
+}
+
+Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) {
+ WriteTag(SerializationTag::kArrayBufferView);
+ ArrayBufferViewTag tag = ArrayBufferViewTag::kInt8Array;
+ if (view->IsJSTypedArray()) {
+ switch (JSTypedArray::cast(view)->type()) {
+#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
+ 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(NumberToUint32(view->byte_offset()));
+ WriteVarint(NumberToUint32(view->byte_length()));
+ return Just(true);
+}
+
+Maybe<bool> ValueSerializer::WriteHostObject(Handle<JSObject> object) {
+ 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>());
+ DCHECK(!result.IsNothing());
+ return result;
+}
+
+Maybe<uint32_t> ValueSerializer::WriteJSObjectPropertiesSlow(
Handle<JSObject> object, Handle<FixedArray> keys) {
uint32_t properties_written = 0;
int length = keys->length();
@@ -445,25 +758,56 @@ Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties(
return Just(properties_written);
}
+void ValueSerializer::ThrowDataCloneError(
+ MessageTemplate::Template template_index) {
+ return ThrowDataCloneError(template_index,
+ isolate_->factory()->empty_string());
+}
+
+void ValueSerializer::ThrowDataCloneError(
+ MessageTemplate::Template template_index, Handle<Object> arg0) {
+ Handle<String> message =
+ MessageTemplate::FormatMessage(isolate_, template_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)
+ Vector<const uint8_t> data,
+ v8::ValueDeserializer::Delegate* delegate)
: isolate_(isolate),
+ delegate_(delegate),
position_(data.start()),
end_(data.start() + data.length()),
- id_map_(Handle<SeededNumberDictionary>::cast(
- isolate->global_handles()->Create(
- *SeededNumberDictionary::New(isolate, 0)))) {}
+ pretenure_(data.length() > kPretenureThreshold ? TENURED : NOT_TENURED),
+ id_map_(Handle<FixedArray>::cast(isolate->global_handles()->Create(
+ isolate_->heap()->empty_fixed_array()))) {}
ValueDeserializer::~ValueDeserializer() {
GlobalHandles::Destroy(Handle<Object>::cast(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_)) return Nothing<bool>();
- if (version_ > kLatestVersion) return Nothing<bool>();
+ if (!ReadVarint<uint32_t>().To(&version_) || version_ > kLatestVersion) {
+ isolate_->Throw(*isolate_->factory()->NewError(
+ MessageTemplate::kDataCloneDeserializationVersionError));
+ return Nothing<bool>();
+ }
}
return Just(true);
}
@@ -511,7 +855,7 @@ Maybe<T> ValueDeserializer::ReadVarint() {
if (position_ >= end_) return Nothing<T>();
uint8_t byte = *position_;
if (V8_LIKELY(shift < sizeof(T) * 8)) {
- value |= (byte & 0x7f) << shift;
+ value |= static_cast<T>(byte & 0x7f) << shift;
shift += 7;
}
has_another_byte = byte & 0x80;
@@ -551,7 +895,67 @@ Maybe<Vector<const uint8_t>> ValueDeserializer::ReadRawBytes(int 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_ =
+ Handle<SeededNumberDictionary>::cast(isolate_->global_handles()->Create(
+ *SeededNumberDictionary::New(isolate_, 0)));
+ }
+ Handle<SeededNumberDictionary> dictionary =
+ array_buffer_transfer_map_.ToHandleChecked();
+ const bool used_as_prototype = false;
+ Handle<SeededNumberDictionary> new_dictionary =
+ SeededNumberDictionary::AtNumberPut(dictionary, transfer_id, array_buffer,
+ used_as_prototype);
+ if (!new_dictionary.is_identical_to(dictionary)) {
+ GlobalHandles::Destroy(Handle<Object>::cast(dictionary).location());
+ array_buffer_transfer_map_ = Handle<SeededNumberDictionary>::cast(
+ isolate_->global_handles()->Create(*new_dictionary));
+ }
+}
+
MaybeHandle<Object> ValueDeserializer::ReadObject() {
+ 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) {
@@ -570,17 +974,19 @@ MaybeHandle<Object> ValueDeserializer::ReadObject() {
case SerializationTag::kInt32: {
Maybe<int32_t> number = ReadZigZag<int32_t>();
if (number.IsNothing()) return MaybeHandle<Object>();
- return isolate_->factory()->NewNumberFromInt(number.FromJust());
+ return isolate_->factory()->NewNumberFromInt(number.FromJust(),
+ pretenure_);
}
case SerializationTag::kUint32: {
Maybe<uint32_t> number = ReadVarint<uint32_t>();
if (number.IsNothing()) return MaybeHandle<Object>();
- return isolate_->factory()->NewNumberFromUint(number.FromJust());
+ return isolate_->factory()->NewNumberFromUint(number.FromJust(),
+ pretenure_);
}
case SerializationTag::kDouble: {
Maybe<double> number = ReadDouble();
if (number.IsNothing()) return MaybeHandle<Object>();
- return isolate_->factory()->NewNumber(number.FromJust());
+ return isolate_->factory()->NewNumber(number.FromJust(), pretenure_);
}
case SerializationTag::kUtf8String:
return ReadUtf8String();
@@ -606,8 +1012,25 @@ MaybeHandle<Object> ValueDeserializer::ReadObject() {
return ReadJSValue(tag);
case SerializationTag::kRegExp:
return ReadJSRegExp();
+ case SerializationTag::kBeginJSMap:
+ return ReadJSMap();
+ case SerializationTag::kBeginJSSet:
+ return ReadJSSet();
+ case SerializationTag::kArrayBuffer:
+ return ReadJSArrayBuffer();
+ case SerializationTag::kArrayBufferTransfer: {
+ const bool is_shared = false;
+ return ReadTransferredJSArrayBuffer(is_shared);
+ }
+ case SerializationTag::kSharedArrayBufferTransfer: {
+ const bool is_shared = true;
+ return ReadTransferredJSArrayBuffer(is_shared);
+ }
default:
- return MaybeHandle<Object>();
+ // TODO(jbroman): Introduce an explicit tag for host objects to avoid
+ // having to treat every unknown tag as a potential host object.
+ position_--;
+ return ReadHostObject();
}
}
@@ -620,7 +1043,7 @@ MaybeHandle<String> ValueDeserializer::ReadUtf8String() {
!ReadRawBytes(utf8_length).To(&utf8_bytes))
return MaybeHandle<String>();
return isolate_->factory()->NewStringFromUtf8(
- Vector<const char>::cast(utf8_bytes));
+ Vector<const char>::cast(utf8_bytes), pretenure_);
}
MaybeHandle<String> ValueDeserializer::ReadTwoByteString() {
@@ -636,7 +1059,7 @@ MaybeHandle<String> ValueDeserializer::ReadTwoByteString() {
// string on the heap (regardless of alignment).
Handle<SeqTwoByteString> string;
if (!isolate_->factory()
- ->NewRawTwoByteString(byte_length / sizeof(uc16))
+ ->NewRawTwoByteString(byte_length / sizeof(uc16), pretenure_)
.ToHandle(&string))
return MaybeHandle<String>();
@@ -646,19 +1069,59 @@ MaybeHandle<String> ValueDeserializer::ReadTwoByteString() {
return string;
}
+bool ValueDeserializer::ReadExpectedString(Handle<String> expected) {
+ // 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;
+ }
+
+ expected = String::Flatten(expected);
+ DisallowHeapAllocation no_gc;
+ String::FlatContent flat = expected->GetFlatContent();
+
+ // If the bytes are verbatim what is in the flattened string, then the string
+ // is successfully consumed.
+ if (tag == SerializationTag::kUtf8String && flat.IsOneByte()) {
+ Vector<const uint8_t> chars = flat.ToOneByteVector();
+ if (byte_length == chars.length() &&
+ String::IsAscii(chars.begin(), 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;
+ }
+ }
+
+ position_ = original_position;
+ return false;
+}
+
MaybeHandle<JSObject> ValueDeserializer::ReadJSObject() {
// If we are at the end of the stack, abort. This function may recurse.
- if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSObject>();
+ STACK_CHECK(isolate_, MaybeHandle<JSObject>());
uint32_t id = next_id_++;
HandleScope scope(isolate_);
Handle<JSObject> object =
- isolate_->factory()->NewJSObject(isolate_->object_function());
+ isolate_->factory()->NewJSObject(isolate_->object_function(), pretenure_);
AddObjectWithID(id, object);
uint32_t num_properties;
uint32_t expected_num_properties;
- if (!ReadJSObjectProperties(object, SerializationTag::kEndJSObject)
+ if (!ReadJSObjectProperties(object, SerializationTag::kEndJSObject, true)
.To(&num_properties) ||
!ReadVarint<uint32_t>().To(&expected_num_properties) ||
num_properties != expected_num_properties) {
@@ -671,21 +1134,22 @@ MaybeHandle<JSObject> ValueDeserializer::ReadJSObject() {
MaybeHandle<JSArray> ValueDeserializer::ReadSparseJSArray() {
// If we are at the end of the stack, abort. This function may recurse.
- if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSArray>();
+ 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);
+ Handle<JSArray> array = isolate_->factory()->NewJSArray(
+ 0, TERMINAL_FAST_ELEMENTS_KIND, pretenure_);
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)
+ if (!ReadJSObjectProperties(array, SerializationTag::kEndSparseJSArray, false)
.To(&num_properties) ||
!ReadVarint<uint32_t>().To(&expected_num_properties) ||
!ReadVarint<uint32_t>().To(&expected_length) ||
@@ -699,7 +1163,7 @@ MaybeHandle<JSArray> ValueDeserializer::ReadSparseJSArray() {
MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() {
// If we are at the end of the stack, abort. This function may recurse.
- if (StackLimitCheck(isolate_).HasOverflowed()) return MaybeHandle<JSArray>();
+ STACK_CHECK(isolate_, MaybeHandle<JSArray>());
uint32_t length;
if (!ReadVarint<uint32_t>().To(&length)) return MaybeHandle<JSArray>();
@@ -707,7 +1171,8 @@ MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() {
uint32_t id = next_id_++;
HandleScope scope(isolate_);
Handle<JSArray> array = isolate_->factory()->NewJSArray(
- FAST_HOLEY_ELEMENTS, length, length, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE);
+ FAST_HOLEY_ELEMENTS, length, length, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE,
+ pretenure_);
AddObjectWithID(id, array);
Handle<FixedArray> elements(FixedArray::cast(array->elements()), isolate_);
@@ -722,7 +1187,7 @@ MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() {
uint32_t num_properties;
uint32_t expected_num_properties;
uint32_t expected_length;
- if (!ReadJSObjectProperties(array, SerializationTag::kEndDenseJSArray)
+ if (!ReadJSObjectProperties(array, SerializationTag::kEndDenseJSArray, false)
.To(&num_properties) ||
!ReadVarint<uint32_t>().To(&expected_num_properties) ||
!ReadVarint<uint32_t>().To(&expected_length) ||
@@ -752,29 +1217,30 @@ MaybeHandle<JSValue> ValueDeserializer::ReadJSValue(SerializationTag tag) {
Handle<JSValue> value;
switch (tag) {
case SerializationTag::kTrueObject:
- value = Handle<JSValue>::cast(
- isolate_->factory()->NewJSObject(isolate_->boolean_function()));
+ value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
+ isolate_->boolean_function(), pretenure_));
value->set_value(isolate_->heap()->true_value());
break;
case SerializationTag::kFalseObject:
- value = Handle<JSValue>::cast(
- isolate_->factory()->NewJSObject(isolate_->boolean_function()));
+ value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
+ isolate_->boolean_function(), pretenure_));
value->set_value(isolate_->heap()->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()));
- Handle<Object> number_object = isolate_->factory()->NewNumber(number);
+ value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
+ isolate_->number_function(), pretenure_));
+ Handle<Object> number_object =
+ isolate_->factory()->NewNumber(number, pretenure_);
value->set_value(*number_object);
break;
}
case SerializationTag::kStringObject: {
Handle<String> string;
if (!ReadUtf8String().ToHandle(&string)) return MaybeHandle<JSValue>();
- value = Handle<JSValue>::cast(
- isolate_->factory()->NewJSObject(isolate_->string_function()));
+ value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
+ isolate_->string_function(), pretenure_));
value->set_value(*string);
break;
}
@@ -801,9 +1267,296 @@ MaybeHandle<JSRegExp> ValueDeserializer::ReadJSRegExp() {
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]) ||
+ 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]) ||
+ 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() {
+ uint32_t id = next_id_++;
+ uint32_t byte_length;
+ Vector<const uint8_t> bytes;
+ 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, pretenure_);
+ JSArrayBuffer::SetupAllocatingData(array_buffer, isolate_, byte_length,
+ should_initialize);
+ memcpy(array_buffer->backing_store(), position_, byte_length);
+ position_ += byte_length;
+ AddObjectWithID(id, array_buffer);
+ return array_buffer;
+}
+
+MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer(
+ bool is_shared) {
+ uint32_t id = next_id_++;
+ uint32_t transfer_id;
+ Handle<SeededNumberDictionary> 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 == SeededNumberDictionary::kNotFound) {
+ return MaybeHandle<JSArrayBuffer>();
+ }
+ Handle<JSArrayBuffer> array_buffer(
+ JSArrayBuffer::cast(transfer_map->ValueAt(index)), isolate_);
+ DCHECK_EQ(is_shared, array_buffer->is_shared());
+ AddObjectWithID(id, array_buffer);
+ return array_buffer;
+}
+
+MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
+ Handle<JSArrayBuffer> buffer) {
+ uint32_t buffer_byte_length = NumberToUint32(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, size) \
+ case ArrayBufferViewTag::k##Type##Array: \
+ external_array_type = kExternal##Type##Array; \
+ element_size = size; \
+ break;
+ TYPED_ARRAYS(TYPED_ARRAY_CASE)
+#undef TYPED_ARRAY_CASE
+ }
+ if (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,
+ pretenure_);
+ AddObjectWithID(id, typed_array);
+ return typed_array;
+}
+
+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++) {
+ object->WriteToField(i, descriptors->GetDetails(i), *properties[i]);
+ }
+}
+
Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
- Handle<JSObject> object, SerializationTag end_tag) {
- for (uint32_t num_properties = 0;; num_properties++) {
+ 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(map->instance_descriptors()->IsEmpty());
+ 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;
+ Handle<String> expected_key = TransitionArray::ExpectedTransitionKey(map);
+ if (!expected_key.is_null() && ReadExpectedString(expected_key)) {
+ key = expected_key;
+ target = TransitionArray::ExpectedTransitionTarget(map);
+ } else {
+ if (!ReadObject().ToHandle(&key)) return Nothing<uint32_t>();
+ if (key->IsString()) {
+ key =
+ isolate_->factory()->InternalizeString(Handle<String>::cast(key));
+ target = TransitionArray::FindTransitionToField(
+ map, Handle<String>::cast(key));
+ transitioning = !target.is_null();
+ } 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::GeneralizeFieldType(target, descriptor,
+ 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 ||
+ 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) {
@@ -828,15 +1581,16 @@ Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
}
bool ValueDeserializer::HasObjectWithID(uint32_t id) {
- return id_map_->Has(isolate_, id);
+ return id < static_cast<unsigned>(id_map_->length()) &&
+ !id_map_->get(id)->IsTheHole(isolate_);
}
MaybeHandle<JSReceiver> ValueDeserializer::GetObjectWithID(uint32_t id) {
- int index = id_map_->FindEntry(isolate_, id);
- if (index == SeededNumberDictionary::kNotFound) {
+ if (id >= static_cast<unsigned>(id_map_->length())) {
return MaybeHandle<JSReceiver>();
}
- Object* value = id_map_->ValueAt(index);
+ Object* value = id_map_->get(id);
+ if (value->IsTheHole(isolate_)) return MaybeHandle<JSReceiver>();
DCHECK(value->IsJSReceiver());
return Handle<JSReceiver>(JSReceiver::cast(value), isolate_);
}
@@ -844,16 +1598,13 @@ MaybeHandle<JSReceiver> ValueDeserializer::GetObjectWithID(uint32_t id) {
void ValueDeserializer::AddObjectWithID(uint32_t id,
Handle<JSReceiver> object) {
DCHECK(!HasObjectWithID(id));
- const bool used_as_prototype = false;
- Handle<SeededNumberDictionary> new_dictionary =
- SeededNumberDictionary::AtNumberPut(id_map_, id, object,
- used_as_prototype);
+ Handle<FixedArray> new_array = FixedArray::SetAndGrow(id_map_, id, object);
// If the dictionary was reallocated, update the global handle.
- if (!new_dictionary.is_identical_to(id_map_)) {
+ if (!new_array.is_identical_to(id_map_)) {
GlobalHandles::Destroy(Handle<Object>::cast(id_map_).location());
- id_map_ = Handle<SeededNumberDictionary>::cast(
- isolate_->global_handles()->Create(*new_dictionary));
+ id_map_ = Handle<FixedArray>::cast(
+ isolate_->global_handles()->Create(*new_array));
}
}
@@ -878,8 +1629,7 @@ static Maybe<bool> SetPropertiesFromKeyValuePairs(Isolate* isolate,
MaybeHandle<Object>
ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() {
- if (version_ > 0) return MaybeHandle<Object>();
-
+ DCHECK_EQ(version_, 0);
HandleScope scope(isolate_);
std::vector<Handle<Object>> stack;
while (position_ < end_) {
@@ -901,8 +1651,8 @@ ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() {
size_t begin_properties =
stack.size() - 2 * static_cast<size_t>(num_properties);
- Handle<JSObject> js_object =
- isolate_->factory()->NewJSObject(isolate_->object_function());
+ Handle<JSObject> js_object = isolate_->factory()->NewJSObject(
+ isolate_->object_function(), pretenure_);
if (num_properties &&
!SetPropertiesFromKeyValuePairs(
isolate_, js_object, &stack[begin_properties], num_properties)
@@ -926,7 +1676,8 @@ ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() {
return MaybeHandle<Object>();
}
- Handle<JSArray> js_array = isolate_->factory()->NewJSArray(0);
+ Handle<JSArray> js_array = isolate_->factory()->NewJSArray(
+ 0, TERMINAL_FAST_ELEMENTS_KIND, pretenure_);
JSArray::SetLength(js_array, length);
size_t begin_properties =
stack.size() - 2 * static_cast<size_t>(num_properties);
@@ -941,9 +1692,12 @@ ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() {
new_object = js_array;
break;
}
- case SerializationTag::kEndDenseJSArray:
+ 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;
@@ -959,7 +1713,11 @@ ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() {
#endif
position_ = end_;
- if (stack.size() != 1) return MaybeHandle<Object>();
+ if (stack.size() != 1) {
+ isolate_->Throw(*isolate_->factory()->NewError(
+ MessageTemplate::kDataCloneDeserializationError));
+ return MaybeHandle<Object>();
+ }
return scope.CloseAndEscape(stack[0]);
}