Index: src/value-serializer.cc |
diff --git a/src/value-serializer.cc b/src/value-serializer.cc |
index 8358b66b66d9f79dfc9e885e05865b14e341bc44..156ef9227c1ef9d83cbe6afa20cf8034d41c9582 100644 |
--- a/src/value-serializer.cc |
+++ b/src/value-serializer.cc |
@@ -54,9 +54,18 @@ enum class SerializationTag : uint8_t { |
// byteLength:uint32_t, then raw data |
kUtf8String = 'S', |
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 = '{', |
}; |
-ValueSerializer::ValueSerializer() {} |
+ValueSerializer::ValueSerializer(Isolate* isolate) |
+ : isolate_(isolate), |
+ zone_(isolate->allocator()), |
+ id_map_(isolate->heap(), &zone_) {} |
ValueSerializer::~ValueSerializer() {} |
@@ -144,6 +153,8 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) { |
if (object->IsString()) { |
WriteString(Handle<String>::cast(object)); |
return Just(true); |
+ } else if (object->IsJSReceiver()) { |
+ return WriteJSReceiver(Handle<JSReceiver>::cast(object)); |
} |
UNIMPLEMENTED(); |
return Nothing<bool>(); |
@@ -218,13 +229,95 @@ void ValueSerializer::WriteString(Handle<String> string) { |
} |
} |
+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 Just(true); |
+ } |
+ |
+ // 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() || instance_type <= LAST_SPECIAL_RECEIVER_TYPE) { |
+ return Nothing<bool>(); |
+ } |
+ |
+ // If we are at the end of the stack, abort. This function may recurse. |
+ if (StackLimitCheck(isolate_).HasOverflowed()) return Nothing<bool>(); |
+ |
+ HandleScope scope(isolate_); |
+ switch (instance_type) { |
+ case JS_OBJECT_TYPE: |
+ case JS_API_OBJECT_TYPE: |
+ return WriteJSObject(Handle<JSObject>::cast(receiver)); |
+ default: |
+ UNIMPLEMENTED(); |
+ break; |
+ } |
+ return Nothing<bool>(); |
+} |
+ |
+Maybe<bool> ValueSerializer::WriteJSObject(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)) { |
+ return Nothing<bool>(); |
+ } |
+ WriteTag(SerializationTag::kEndJSObject); |
+ WriteVarint<uint32_t>(properties_written); |
+ return Just(true); |
+} |
+ |
+Maybe<uint32_t> ValueSerializer::WriteJSObjectProperties( |
+ 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); |
+} |
+ |
ValueDeserializer::ValueDeserializer(Isolate* isolate, |
Vector<const uint8_t> data) |
: isolate_(isolate), |
position_(data.start()), |
- end_(data.start() + data.length()) {} |
+ end_(data.start() + data.length()), |
+ id_map_(Handle<SeededNumberDictionary>::cast( |
+ isolate->global_handles()->Create( |
+ *SeededNumberDictionary::New(isolate, 0)))) {} |
-ValueDeserializer::~ValueDeserializer() {} |
+ValueDeserializer::~ValueDeserializer() { |
+ GlobalHandles::Destroy(Handle<Object>::cast(id_map_).location()); |
+} |
Maybe<bool> ValueDeserializer::ReadHeader() { |
if (position_ < end_ && |
@@ -236,6 +329,17 @@ Maybe<bool> ValueDeserializer::ReadHeader() { |
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); |
+} |
+ |
Maybe<SerializationTag> ValueDeserializer::ReadTag() { |
SerializationTag tag; |
do { |
@@ -337,6 +441,13 @@ MaybeHandle<Object> ValueDeserializer::ReadObject() { |
return ReadUtf8String(); |
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(); |
default: |
return MaybeHandle<Object>(); |
} |
@@ -377,5 +488,86 @@ MaybeHandle<String> ValueDeserializer::ReadTwoByteString() { |
return string; |
} |
+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>(); |
+ |
+ uint32_t id = next_id_++; |
+ HandleScope scope(isolate_); |
+ Handle<JSObject> object = |
+ isolate_->factory()->NewJSObject(isolate_->object_function()); |
+ AddObjectWithID(id, object); |
+ |
+ uint32_t num_properties; |
+ uint32_t expected_num_properties; |
+ if (!ReadJSObjectProperties(object, SerializationTag::kEndJSObject) |
+ .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); |
+} |
+ |
+Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( |
+ Handle<JSObject> object, SerializationTag end_tag) { |
+ for (uint32_t num_properties = 0;; num_properties++) { |
+ SerializationTag tag; |
+ if (!PeekTag().To(&tag)) return Nothing<uint32_t>(); |
+ if (tag == end_tag) { |
+ SerializationTag consumed_tag = ReadTag().ToChecked(); |
+ USE(consumed_tag); |
+ DCHECK(tag == consumed_tag); |
+ return Just(num_properties); |
+ } |
+ |
+ Handle<Object> key; |
+ if (!ReadObject().ToHandle(&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 || |
+ JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE) |
+ .is_null()) { |
+ return Nothing<uint32_t>(); |
+ } |
+ } |
+} |
+ |
+bool ValueDeserializer::HasObjectWithID(uint32_t id) { |
+ return id_map_->Has(isolate_, id); |
+} |
+ |
+MaybeHandle<JSReceiver> ValueDeserializer::GetObjectWithID(uint32_t id) { |
+ int index = id_map_->FindEntry(isolate_, id); |
+ if (index == SeededNumberDictionary::kNotFound) { |
+ return MaybeHandle<JSReceiver>(); |
+ } |
+ Object* value = id_map_->ValueAt(index); |
+ DCHECK(value->IsJSReceiver()); |
+ return Handle<JSReceiver>(JSReceiver::cast(value), isolate_); |
+} |
+ |
+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); |
+ |
+ // If the dictionary was reallocated, update the global handle. |
+ if (!new_dictionary.is_identical_to(id_map_)) { |
+ GlobalHandles::Destroy(Handle<Object>::cast(id_map_).location()); |
+ id_map_ = Handle<SeededNumberDictionary>::cast( |
+ isolate_->global_handles()->Create(*new_dictionary)); |
+ } |
+} |
+ |
} // namespace internal |
} // namespace v8 |