isolate_->heap()->IterateStrongRoots(this, VISIT_ONLY_STRONG);
isolate_->heap()->RepairFreeListsAfterDeserialization();
isolate_->heap()->IterateWeakRoots(this, VISIT_ALL);
+ DeserializeDeferredObjects();
isolate_->heap()->set_native_contexts_list(
isolate_->heap()->undefined_value());
Object* outdated_contexts;
VisitPointer(&root);
VisitPointer(&outdated_contexts);
+ DeserializeDeferredObjects();
// There's no code deserialized here. If this assert fires
// then that's changed and logging should be added to notify
DisallowHeapAllocation no_gc;
Object* root;
VisitPointer(&root);
+ DeserializeDeferredObjects();
return Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(root));
}
}
}
-void Deserializer::RelinkAllocationSite(AllocationSite* site) {
- if (isolate_->heap()->allocation_sites_list() == Smi::FromInt(0)) {
- site->set_weak_next(isolate_->heap()->undefined_value());
- } else {
- site->set_weak_next(isolate_->heap()->allocation_sites_list());
+void Deserializer::DeserializeDeferredObjects() {
+ for (int code = source_.Get(); code != kSynchronize; code = source_.Get()) {
+ int space = code & kSpaceMask;
+ DCHECK(space <= kNumberOfSpaces);
+ DCHECK(code - space == kNewObject);
+ HeapObject* object = GetBackReferencedObject(space);
+ int size = source_.GetInt() << kPointerSizeLog2;
+ Address obj_address = object->address();
+ Object** start = reinterpret_cast<Object**>(obj_address + kPointerSize);
+ Object** end = reinterpret_cast<Object**>(obj_address + size);
+ bool filled = ReadData(start, end, space, obj_address);
+ CHECK(filled);
+ if (object->IsAllocationSite()) {
+ RelinkAllocationSite(AllocationSite::cast(object));
+ }
}
- isolate_->heap()->set_allocation_sites_list(site);
}
};
-HeapObject* Deserializer::ProcessNewObjectFromSerializedCode(HeapObject* obj) {
+HeapObject* Deserializer::PostProcessNewObject(HeapObject* obj) {
+ DCHECK(deserializing_user_code());
if (obj->IsString()) {
String* string = String::cast(obj);
// Uninitialize hash field as the hash seed may have changed.
}
} else if (obj->IsScript()) {
Script::cast(obj)->set_id(isolate_->heap()->NextScriptId());
+ } else {
+ DCHECK(CanBeDeferred(obj));
}
return obj;
}
+void Deserializer::RelinkAllocationSite(AllocationSite* obj) {
+ DCHECK(obj->IsAllocationSite());
+ // Allocation sites are present in the snapshot, and must be linked into
+ // a list at deserialization time.
+ AllocationSite* site = AllocationSite::cast(obj);
+ // TODO(mvstanton): consider treating the heap()->allocation_sites_list()
+ // as a (weak) root. If this root is relocated correctly,
+ // RelinkAllocationSite() isn't necessary.
+ if (isolate_->heap()->allocation_sites_list() == Smi::FromInt(0)) {
+ site->set_weak_next(isolate_->heap()->undefined_value());
+ } else {
+ site->set_weak_next(isolate_->heap()->allocation_sites_list());
+ }
+ isolate_->heap()->set_allocation_sites_list(site);
+}
+
+
HeapObject* Deserializer::GetBackReferencedObject(int space) {
HeapObject* obj;
BackReference back_reference(source_.GetInt());
if (FLAG_log_snapshot_positions) {
LOG(isolate_, SnapshotPositionEvent(address, source_.position()));
}
- ReadData(current, limit, space_number, address);
- // TODO(mvstanton): consider treating the heap()->allocation_sites_list()
- // as a (weak) root. If this root is relocated correctly,
- // RelinkAllocationSite() isn't necessary.
- if (obj->IsAllocationSite()) RelinkAllocationSite(AllocationSite::cast(obj));
+ if (ReadData(current, limit, space_number, address)) {
+ // Only post process if object content has not been deferred.
+ if (obj->IsAllocationSite()) {
+ RelinkAllocationSite(AllocationSite::cast(obj));
+ }
- // Fix up strings from serialized user code.
- if (deserializing_user_code()) obj = ProcessNewObjectFromSerializedCode(obj);
+ if (deserializing_user_code()) obj = PostProcessNewObject(obj);
+ }
Object* write_back_obj = obj;
UnalignedCopy(write_back, &write_back_obj);
#ifdef DEBUG
if (obj->IsCode()) {
DCHECK(space_number == CODE_SPACE || space_number == LO_SPACE);
-#ifdef VERIFY_HEAP
- obj->ObjectVerify();
-#endif // VERIFY_HEAP
} else {
DCHECK(space_number != CODE_SPACE);
}
}
-void Deserializer::ReadData(Object** current, Object** limit, int source_space,
+bool Deserializer::ReadData(Object** current, Object** limit, int source_space,
Address current_object_address) {
Isolate* const isolate = isolate_;
// Write barrier support costs around 1% in startup time. In fact there
break;
}
+ case kDeferred: {
+ // Deferred can only occur right after the heap object header.
+ DCHECK(current == reinterpret_cast<Object**>(current_object_address +
+ kPointerSize));
+ HeapObject* obj = HeapObject::FromAddress(current_object_address);
+ // If the deferred object is a map, its instance type may be used
+ // during deserialization. Initialize it with a temporary value.
+ if (obj->IsMap()) Map::cast(obj)->set_instance_type(FILLER_TYPE);
+ current = limit;
+ return false;
+ }
+
case kSynchronize:
// If we get here then that indicates that you have a mismatch between
// the number of GC roots when serializing and deserializing.
}
}
CHECK_EQ(limit, current);
+ return true;
}
sink_(sink),
external_reference_encoder_(isolate),
root_index_map_(isolate),
+ recursion_depth_(0),
code_address_map_(NULL),
large_objects_total_size_(0),
seen_large_objects_index_(0) {
}
+void Serializer::SerializeDeferredObjects() {
+ while (deferred_objects_.length() > 0) {
+ HeapObject* obj = deferred_objects_.RemoveLast();
+ ObjectSerializer obj_serializer(this, obj, sink_, kPlain, kStartOfObject);
+ obj_serializer.SerializeDeferred();
+ }
+ sink_->Put(kSynchronize, "Finished with deferred objects");
+}
+
+
void StartupSerializer::SerializeStrongReferences() {
Isolate* isolate = this->isolate();
// No active threads.
}
VisitPointer(o);
SerializeOutdatedContextsAsFixedArray();
+ SerializeDeferredObjects();
Pad();
}
sink_->Put(reinterpret_cast<byte*>(&length_smi)[i], "Byte");
}
for (int i = 0; i < length; i++) {
- BackReference back_ref = outdated_contexts_[i];
- DCHECK(BackReferenceIsAlreadyAllocated(back_ref));
- sink_->Put(kBackref + back_ref.space(), "BackRef");
- sink_->PutInt(back_ref.reference(), "BackRefValue");
+ Context* context = outdated_contexts_[i];
+ BackReference back_reference = back_reference_map_.Lookup(context);
+ sink_->Put(kBackref + back_reference.space(), "BackRef");
+ PutBackReference(context, back_reference);
}
}
}
"BackRefWithSkip");
sink_->PutInt(skip, "BackRefSkipDistance");
}
- DCHECK(BackReferenceIsAlreadyAllocated(back_reference));
- sink_->PutInt(back_reference.reference(), "BackRefValue");
-
- hot_objects_.Add(obj);
+ PutBackReference(obj, back_reference);
}
return true;
}
}
-void StartupSerializer::SerializeWeakReferences() {
+void StartupSerializer::SerializeWeakReferencesAndDeferred() {
// This phase comes right after the serialization (of the snapshot).
// After we have done the partial serialization the partial snapshot cache
// will contain some references needed to decode the partial snapshot. We
Object* undefined = isolate()->heap()->undefined_value();
VisitPointer(&undefined);
isolate()->heap()->IterateWeakRoots(this, VISIT_ALL);
+ SerializeDeferredObjects();
Pad();
}
}
+void Serializer::PutBackReference(HeapObject* object, BackReference reference) {
+ DCHECK(BackReferenceIsAlreadyAllocated(reference));
+ sink_->PutInt(reference.reference(), "BackRefValue");
+ hot_objects_.Add(object);
+}
+
+
void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
WhereToPoint where_to_point, int skip) {
if (obj->IsMap()) {
Context::cast(obj)->global_object() == global_object_) {
// Context refers to the current global object. This reference will
// become outdated after deserialization.
- BackReference back_reference = back_reference_map_.Lookup(obj);
- DCHECK(back_reference.is_valid());
- outdated_contexts_.Add(back_reference);
+ outdated_contexts_.Add(Context::cast(obj));
}
}
// We cannot serialize typed array objects correctly.
DCHECK(!object_->IsJSTypedArray());
+ // We don't expect fillers.
+ DCHECK(!object_->IsFiller());
+
if (object_->IsPrototypeInfo()) {
Object* prototype_users = PrototypeInfo::cast(object_)->prototype_users();
if (prototype_users->IsWeakFixedArray()) {
CHECK_EQ(0, bytes_processed_so_far_);
bytes_processed_so_far_ = kPointerSize;
+ RecursionScope recursion(serializer_);
+ // Objects that are immediately post processed during deserialization
+ // cannot be deferred, since post processing requires the object content.
+ if (recursion.ExceedsMaximum() && CanBeDeferred(object_)) {
+ serializer_->QueueDeferredObject(object_);
+ sink_->Put(kDeferred, "Deferring object content");
+ return;
+ }
+
+ object_->IterateBody(map->instance_type(), size, this);
+ OutputRawData(object_->address() + size);
+}
+
+
+void Serializer::ObjectSerializer::SerializeDeferred() {
+ if (FLAG_trace_serializer) {
+ PrintF(" Encoding deferred heap object: ");
+ object_->ShortPrint();
+ PrintF("\n");
+ }
+
+ int size = object_->Size();
+ Map* map = object_->map();
+ BackReference reference = serializer_->back_reference_map()->Lookup(object_);
+
+ // Serialize the rest of the object.
+ CHECK_EQ(0, bytes_processed_so_far_);
+ bytes_processed_so_far_ = kPointerSize;
+
+ sink_->Put(kNewObject + reference.space(), "deferred object");
+ serializer_->PutBackReference(object_, reference);
+ sink_->PutInt(size >> kPointerSizeLog2, "deferred object size");
+
object_->IterateBody(map->instance_type(), size, this);
OutputRawData(object_->address() + size);
}
DisallowHeapAllocation no_gc;
Object** location = Handle<Object>::cast(info).location();
cs.VisitPointer(location);
+ cs.SerializeDeferredObjects();
cs.Pad();
SerializedCodeData data(sink.data(), cs);
static const int kNumberOfSpaces = LAST_SPACE + 1;
protected:
+ static bool CanBeDeferred(HeapObject* o) {
+ return !o->IsString() && !o->IsScript();
+ }
+
// ---------- byte code range 0x00..0x7f ----------
// Byte codes in this range represent Where, HowToCode and WhereToPoint.
// Where the pointed-to object can be found:
static const int kNop = 0x3d;
// Move to next reserved chunk.
static const int kNextChunk = 0x3e;
+ // Deferring object content.
+ static const int kDeferred = 0x3f;
// A tag emitted at strategic points in the snapshot to delineate sections.
// If the deserializer does not find these at the expected moments then it
// is an indication that the snapshot and the VM do not fit together.
memcpy(dest, src, sizeof(*src));
}
- // Allocation sites are present in the snapshot, and must be linked into
- // a list at deserialization time.
- void RelinkAllocationSite(AllocationSite* site);
+ void DeserializeDeferredObjects();
// Fills in some heap data in an area from start to end (non-inclusive). The
// space id is used for the write barrier. The object_address is the address
// of the object we are writing into, or NULL if we are not writing into an
// object, i.e. if we are writing a series of tagged values that are not on
- // the heap.
- void ReadData(Object** start, Object** end, int space,
+ // the heap. Return false if the object content has been deferred.
+ bool ReadData(Object** start, Object** end, int space,
Address object_address);
void ReadObject(int space_number, Object** write_back);
Address Allocate(int space_index, int size);
// Special handling for serialized code like hooking up internalized strings.
- HeapObject* ProcessNewObjectFromSerializedCode(HeapObject* obj);
+ HeapObject* PostProcessNewObject(HeapObject* obj);
+
+ void RelinkAllocationSite(AllocationSite* obj);
// This returns the address of an object that has been described in the
// snapshot by chunk index and offset.
void EncodeReservations(List<SerializedData::Reservation>* out) const;
+ void SerializeDeferredObjects();
+
Isolate* isolate() const { return isolate_; }
BackReferenceMap* back_reference_map() { return &back_reference_map_; }
is_code_object_(o->IsCode()),
code_has_been_output_(false) {}
void Serialize();
+ void SerializeDeferred();
void VisitPointers(Object** start, Object** end);
void VisitEmbeddedPointer(RelocInfo* target);
void VisitExternalReference(Address* p);
bool code_has_been_output_;
};
+ class RecursionScope {
+ public:
+ explicit RecursionScope(Serializer* serializer) : serializer_(serializer) {
+ serializer_->recursion_depth_++;
+ }
+ ~RecursionScope() { serializer_->recursion_depth_--; }
+ bool ExceedsMaximum() {
+ return serializer_->recursion_depth_ >= kMaxRecursionDepth;
+ }
+
+ private:
+ static const int kMaxRecursionDepth = 32;
+ Serializer* serializer_;
+ };
+
virtual void SerializeObject(HeapObject* o, HowToCode how_to_code,
WhereToPoint where_to_point, int skip) = 0;
void PutRoot(int index, HeapObject* object, HowToCode how, WhereToPoint where,
int skip);
+ void PutBackReference(HeapObject* object, BackReference reference);
+
// Returns true if the object was successfully serialized.
bool SerializeKnownObject(HeapObject* obj, HowToCode how_to_code,
WhereToPoint where_to_point, int skip);
SnapshotByteSink* sink() const { return sink_; }
+ void QueueDeferredObject(HeapObject* obj) {
+ DCHECK(back_reference_map_.Lookup(obj).is_valid());
+ deferred_objects_.Add(obj);
+ }
+
void OutputStatistics(const char* name);
Isolate* isolate_;
BackReferenceMap back_reference_map_;
RootIndexMap root_index_map_;
+ int recursion_depth_;
+
friend class Deserializer;
friend class ObjectSerializer;
+ friend class RecursionScope;
friend class SnapshotData;
private:
List<byte> code_buffer_;
+ // To handle stack overflow.
+ List<HeapObject*> deferred_objects_;
+
#ifdef OBJECT_PRINT
static const int kInstanceTypes = 256;
int* instance_type_count_;
void SerializeOutdatedContextsAsFixedArray();
Serializer* startup_serializer_;
- List<BackReference> outdated_contexts_;
+ List<Context*> outdated_contexts_;
Object* global_object_;
PartialCacheIndexMap partial_cache_index_map_;
DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
virtual void SerializeStrongReferences();
virtual void SerializeObject(HeapObject* o, HowToCode how_to_code,
WhereToPoint where_to_point, int skip) override;
- void SerializeWeakReferences();
+ void SerializeWeakReferencesAndDeferred();
void Serialize() {
SerializeStrongReferences();
- SerializeWeakReferences();
- Pad();
+ SerializeWeakReferencesAndDeferred();
}
private: