void* operator new(size_t size);
void operator delete(void*, size_t);
- // This Data class is accessible internally through a typedef in the
- // ImplementationUtilities class.
+ // This Data class is accessible internally as HandleScopeData through a
+ // typedef in the ImplementationUtilities class.
class V8EXPORT Data {
public:
int extensions;
INLINE(static Address target_address_at(Address pc));
INLINE(static void set_target_address_at(Address pc, Address target));
+ // Here we are patching the address in the constant pool, not the actual call
+ // instruction. The address in the constant pool is the same size as a
+ // pointer.
+ static const int kCallTargetSize = kPointerSize;
+
// Size of an instruction.
static const int kInstrSize = sizeof(Instr);
void Bootstrapper::Iterate(ObjectVisitor* v) {
natives_cache.Iterate(v);
+ v->Synchronize("NativesCache");
extensions_cache.Iterate(v);
+ v->Synchronize("Extensions");
PendingFixups::Iterate(v);
+ v->Synchronize("PendingFixups");
}
class VariableProxy;
class RelocInfo;
class Deserializer;
+class GenericDeserializer; // TODO(erikcorry): Get rid of this.
class MessageLocation;
class ObjectGroup;
class TickSample;
LO_SPACE, // Promoted large objects.
FIRST_SPACE = NEW_SPACE,
- LAST_SPACE = LO_SPACE
+ LAST_SPACE = LO_SPACE,
+ FIRST_PAGED_SPACE = OLD_POINTER_SPACE,
+ LAST_PAGED_SPACE = CELL_SPACE
};
const int kSpaceTagSize = 3;
const int kSpaceTagMask = (1 << kSpaceTagSize) - 1;
int Heap::gc_count_ = 0;
int Heap::always_allocate_scope_depth_ = 0;
+int Heap::linear_allocation_scope_depth_ = 0;
bool Heap::context_disposed_pending_ = false;
#ifdef DEBUG
}
-#ifdef DEBUG
-#define SYNCHRONIZE_TAG(tag) v->Synchronize(tag)
-#else
-#define SYNCHRONIZE_TAG(tag)
-#endif
-
void Heap::IterateRoots(ObjectVisitor* v) {
IterateStrongRoots(v);
v->VisitPointer(reinterpret_cast<Object**>(&roots_[kSymbolTableRootIndex]));
- SYNCHRONIZE_TAG("symbol_table");
+ v->Synchronize("symbol_table");
}
void Heap::IterateStrongRoots(ObjectVisitor* v) {
v->VisitPointers(&roots_[0], &roots_[kStrongRootListLength]);
- SYNCHRONIZE_TAG("strong_root_list");
+ v->Synchronize("strong_root_list");
v->VisitPointer(bit_cast<Object**, String**>(&hidden_symbol_));
- SYNCHRONIZE_TAG("symbol");
+ v->Synchronize("symbol");
Bootstrapper::Iterate(v);
- SYNCHRONIZE_TAG("bootstrapper");
+ v->Synchronize("bootstrapper");
Top::Iterate(v);
- SYNCHRONIZE_TAG("top");
+ v->Synchronize("top");
Relocatable::Iterate(v);
- SYNCHRONIZE_TAG("relocatable");
+ v->Synchronize("relocatable");
#ifdef ENABLE_DEBUGGER_SUPPORT
Debug::Iterate(v);
#endif
- SYNCHRONIZE_TAG("debug");
+ v->Synchronize("debug");
CompilationCache::Iterate(v);
- SYNCHRONIZE_TAG("compilationcache");
+ v->Synchronize("compilationcache");
// Iterate over local handles in handle scopes.
HandleScopeImplementer::Iterate(v);
- SYNCHRONIZE_TAG("handlescope");
+ v->Synchronize("handlescope");
// Iterate over the builtin code objects and code stubs in the heap. Note
// that it is not strictly necessary to iterate over code objects on
// scavenge collections. We still do it here because this same function
// is used by the mark-sweep collector and the deserializer.
Builtins::IterateBuiltins(v);
- SYNCHRONIZE_TAG("builtins");
+ v->Synchronize("builtins");
// Iterate over global handles.
GlobalHandles::IterateRoots(v);
- SYNCHRONIZE_TAG("globalhandles");
+ v->Synchronize("globalhandles");
// Iterate over pointers being held by inactive threads.
ThreadManager::Iterate(v);
- SYNCHRONIZE_TAG("threadmanager");
+ v->Synchronize("threadmanager");
}
-#undef SYNCHRONIZE_TAG
// Flag is set when the heap has been configured. The heap can be repeatedly
// Defines all the roots in Heap.
#define UNCONDITIONAL_STRONG_ROOT_LIST(V) \
- /* Cluster the most popular ones in a few cache lines here at the top. */ \
+ /* Put the byte array map early. We need it to be in place by the time */ \
+ /* the deserializer hits the next page, since it wants to put a byte */ \
+ /* array in the unused space at the end of the page. */ \
+ V(Map, byte_array_map, ByteArrayMap) \
+ V(Map, one_pointer_filler_map, OnePointerFillerMap) \
+ V(Map, two_pointer_filler_map, TwoPointerFillerMap) \
+ /* Cluster the most popular ones in a few cache lines here at the top. */ \
V(Smi, stack_limit, StackLimit) \
V(Object, undefined_value, UndefinedValue) \
V(Object, the_hole_value, TheHoleValue) \
undetectable_medium_ascii_string_map, \
UndetectableMediumAsciiStringMap) \
V(Map, undetectable_long_ascii_string_map, UndetectableLongAsciiStringMap) \
- V(Map, byte_array_map, ByteArrayMap) \
V(Map, pixel_array_map, PixelArrayMap) \
V(Map, external_byte_array_map, ExternalByteArrayMap) \
V(Map, external_unsigned_byte_array_map, ExternalUnsignedByteArrayMap) \
V(Map, boilerplate_function_map, BoilerplateFunctionMap) \
V(Map, shared_function_info_map, SharedFunctionInfoMap) \
V(Map, proxy_map, ProxyMap) \
- V(Map, one_pointer_filler_map, OnePointerFillerMap) \
- V(Map, two_pointer_filler_map, TwoPointerFillerMap) \
V(Object, nan_value, NanValue) \
V(Object, minus_zero_value, MinusZeroValue) \
V(String, empty_string, EmptyString) \
static Address always_allocate_scope_depth_address() {
return reinterpret_cast<Address>(&always_allocate_scope_depth_);
}
+ static bool linear_allocation() {
+ return linear_allocation_scope_depth_ != 0;
+ }
static Address* NewSpaceAllocationTopAddress() {
return new_space_.allocation_top_address();
static bool Contains(HeapObject* value);
// Checks whether an address/object in a space.
- // Currently used by tests and heap verification only.
+ // Currently used by tests, serialization and heap verification only.
static bool InSpace(Address addr, AllocationSpace space);
static bool InSpace(HeapObject* value, AllocationSpace space);
static int survived_since_last_expansion_;
static int always_allocate_scope_depth_;
+ static int linear_allocation_scope_depth_;
static bool context_disposed_pending_;
static const int kMaxMapSpaceSize = 8*MB;
friend class Factory;
friend class DisallowAllocationFailure;
friend class AlwaysAllocateScope;
+ friend class LinearAllocationScope;
};
};
+class LinearAllocationScope {
+ public:
+ LinearAllocationScope() {
+ Heap::linear_allocation_scope_depth_++;
+ }
+
+ ~LinearAllocationScope() {
+ Heap::linear_allocation_scope_depth_--;
+ ASSERT(Heap::linear_allocation_scope_depth_ >= 0);
+ }
+};
+
+
#ifdef DEBUG
// Visitor class to verify interior pointers that do not have remembered set
// bits. All heap object pointers have to point into the heap to a location
inline static Address target_address_at(Address pc);
inline static void set_target_address_at(Address pc, Address target);
+ static const int kCallTargetSize = kPointerSize;
+
// Distance between the address of the code target in the call instruction
// and the return address
static const int kCallTargetAddressOffset = kPointerSize;
class List {
public:
+ List() { Initialize(0); }
INLINE(explicit List(int capacity)) { Initialize(capacity); }
INLINE(~List()) { DeleteData(data_); }
}
+bool MapWord::IsSerializationAddress() {
+ return HAS_SMI_TAG(reinterpret_cast<Object*>(value_));
+}
+
+
+MapWord MapWord::FromSerializationAddress(int raw) {
+ // When the map word is being used as a serialization address we Smi-encode
+ // the serialization address (which is always a smallish positive integer).
+ return MapWord(reinterpret_cast<uintptr_t>(Smi::FromInt(raw)));
+}
+
+
+int MapWord::ToSerializationAddress() {
+ // When the map word is being used as a serialization address we treat the
+ // map word as a Smi and get the small integer that it encodes.
+ return reinterpret_cast<Smi*>(value_)->value();
+}
+
+
bool MapWord::IsMarked() {
return (value_ & kMarkingMask) == 0;
}
// View this map word as a forwarding address.
inline HeapObject* ToForwardingAddress();
+ // True if this map word is a serialization address. This will only be the
+ // case during a destructive serialization of the heap.
+ inline bool IsSerializationAddress();
+
+ // Create a map word from a serialization address.
+ static inline MapWord FromSerializationAddress(int raw);
+
+ // View this map word as a serialization address.
+ inline int ToSerializationAddress();
// Marking phase of full collection: the map word of live objects is
// marked, and may be marked as overflowed (eg, the object is live, its
// Intended for serialization/deserialization checking: insert, or
// check for the presence of, a tag at this position in the stream.
virtual void Synchronize(const char* tag) {}
+#else
+ inline void Synchronize(const char* tag) {}
#endif
};
#endif
+class NoGlobalHandlesChecker : public ObjectVisitor {
+ public:
+ virtual void VisitPointers(Object** start, Object** end) {
+ ASSERT(false);
+ }
+};
+
+
+class GlobalHandleDestroyer : public ObjectVisitor {
+ void VisitPointers(Object**start, Object**end) {
+ while (start < end) {
+ GlobalHandles::Destroy(start++);
+ }
+ }
+};
+
+
void Deserializer::Deserialize() {
+ // No global handles.
+ NoGlobalHandlesChecker checker;
+ GlobalHandles::IterateRoots(&checker);
// No active threads.
ASSERT_EQ(NULL, ThreadState::FirstInUse());
// No active handles.
GetHeader();
Heap::IterateRoots(this);
GetContextStack();
+ // Any global handles that have been set up by deserialization are leaked
+ // since noone is keeping track of them. So we discard them now.
+ GlobalHandleDestroyer destroyer;
+ GlobalHandles::IterateRoots(&destroyer);
}
}
+Deserializer2::Deserializer2(SnapshotByteSource* source)
+ : source_(source),
+ external_reference_decoder_(NULL) {
+ for (int i = 0; i <= LAST_SPACE; i++) {
+ fullness_[i] = 0;
+ }
+}
+
+
+// This routine both allocates a new object, and also keeps
+// track of where objects have been allocated so that we can
+// fix back references when deserializing.
+Address Deserializer2::Allocate(int space_index, int size) {
+ HeapObject* new_object;
+ int old_fullness = CurrentAllocationAddress(space_index);
+ // When we start a new page we need to record its location.
+ bool record_page = (old_fullness == 0);
+ if (SpaceIsPaged(space_index)) {
+ PagedSpace* space;
+ switch (space_index) {
+ case OLD_DATA_SPACE: space = Heap::old_data_space(); break;
+ case OLD_POINTER_SPACE: space = Heap::old_pointer_space(); break;
+ case MAP_SPACE: space = Heap::map_space(); break;
+ case CODE_SPACE: space = Heap::code_space(); break;
+ case CELL_SPACE: space = Heap::cell_space(); break;
+ default: UNREACHABLE(); space = NULL; break;
+ }
+ ASSERT(size <= Page::kPageSize - Page::kObjectStartOffset);
+ int current_page = old_fullness >> Page::kPageSizeBits;
+ int new_fullness = old_fullness + size;
+ int new_page = new_fullness >> Page::kPageSizeBits;
+ // What is our new position within the current page.
+ int intra_page_offset = new_fullness - current_page * Page::kPageSize;
+ if (intra_page_offset > Page::kPageSize - Page::kObjectStartOffset) {
+ // This object will not fit in a page and we have to move to the next.
+ new_page = current_page + 1;
+ old_fullness = new_page << Page::kPageSizeBits;
+ new_fullness = old_fullness + size;
+ record_page = true;
+ }
+ fullness_[space_index] = new_fullness;
+ Object* new_allocation = space->AllocateRaw(size);
+ new_object = HeapObject::cast(new_allocation);
+ ASSERT(!new_object->IsFailure());
+ ASSERT((reinterpret_cast<intptr_t>(new_object->address()) &
+ Page::kPageAlignmentMask) ==
+ (old_fullness & Page::kPageAlignmentMask) +
+ Page::kObjectStartOffset);
+ } else if (SpaceIsLarge(space_index)) {
+ ASSERT(size > Page::kPageSize - Page::kObjectStartOffset);
+ fullness_[LO_SPACE]++;
+ LargeObjectSpace* lo_space = Heap::lo_space();
+ Object* new_allocation;
+ if (space_index == kLargeData) {
+ new_allocation = lo_space->AllocateRaw(size);
+ } else if (space_index == kLargeFixedArray) {
+ new_allocation = lo_space->AllocateRawFixedArray(size);
+ } else {
+ ASSERT(space_index == kLargeCode);
+ new_allocation = lo_space->AllocateRawCode(size);
+ }
+ ASSERT(!new_allocation->IsFailure());
+ new_object = HeapObject::cast(new_allocation);
+ record_page = true;
+ // The page recording below records all large objects in the same space.
+ space_index = LO_SPACE;
+ } else {
+ ASSERT(space_index == NEW_SPACE);
+ Object* new_allocation = Heap::new_space()->AllocateRaw(size);
+ fullness_[space_index] += size;
+ ASSERT(!new_allocation->IsFailure());
+ new_object = HeapObject::cast(new_allocation);
+ }
+ Address address = new_object->address();
+ if (record_page) {
+ pages_[space_index].Add(address);
+ }
+ return address;
+}
+
+
+// This returns the address of an object that has been described in the
+// snapshot as being offset bytes back in a particular space.
+HeapObject* Deserializer2::GetAddress(int space) {
+ int offset = source_->GetInt();
+ if (SpaceIsLarge(space)) {
+ // Large spaces have one object per 'page'.
+ return HeapObject::FromAddress(
+ pages_[LO_SPACE][fullness_[LO_SPACE] - offset]);
+ }
+ offset <<= kObjectAlignmentBits;
+ if (space == NEW_SPACE) {
+ // New space has only one space - numbered 0.
+ return HeapObject::FromAddress(
+ pages_[space][0] + fullness_[space] - offset);
+ }
+ ASSERT(SpaceIsPaged(space));
+ int virtual_address = fullness_[space] - offset;
+ int page_of_pointee = (virtual_address) >> Page::kPageSizeBits;
+ Address object_address = pages_[space][page_of_pointee] +
+ (virtual_address & Page::kPageAlignmentMask);
+ return HeapObject::FromAddress(object_address);
+}
+
+
+void Deserializer2::Deserialize() {
+ // Don't GC while deserializing - just expand the heap.
+ AlwaysAllocateScope always_allocate;
+ // Don't use the free lists while deserializing.
+ LinearAllocationScope allocate_linearly;
+ // No active threads.
+ ASSERT_EQ(NULL, ThreadState::FirstInUse());
+ // No active handles.
+ ASSERT(HandleScopeImplementer::instance()->blocks()->is_empty());
+ ASSERT(external_reference_decoder_ == NULL);
+ external_reference_decoder_ = new ExternalReferenceDecoder();
+ Heap::IterateRoots(this);
+ ASSERT(source_->AtEOF());
+ delete external_reference_decoder_;
+ external_reference_decoder_ = NULL;
+}
+
+
+// This is called on the roots. It is the driver of the deserialization
+// process.
+void Deserializer2::VisitPointers(Object** start, Object** end) {
+ for (Object** current = start; current < end; current++) {
+ DataType data = static_cast<DataType>(source_->Get());
+ if (data == SMI_SERIALIZATION) {
+ *current = Smi::FromInt(source_->GetInt() - kSmiBias);
+ } else if (data == BACKREF_SERIALIZATION) {
+ int space = source_->Get();
+ *current = GetAddress(space);
+ } else {
+ ASSERT(data == OBJECT_SERIALIZATION);
+ ReadObject(current);
+ }
+ }
+}
+
+
+// This routine writes the new object into the pointer provided and then
+// returns true if the new object was in young space and false otherwise.
+// The reason for this strange interface is that otherwise the object is
+// written very late, which means the ByteArray map is not set up by the
+// time we need to use it to mark the space at the end of a page free (by
+// making it into a byte array).
+bool Deserializer2::ReadObject(Object** write_back) {
+ int space = source_->Get();
+ int size = source_->GetInt() << kObjectAlignmentBits;
+ Address address = Allocate(space, size);
+ *write_back = HeapObject::FromAddress(address);
+ Object** current = reinterpret_cast<Object**>(address);
+ Object** limit = current + (size >> kPointerSizeLog2);
+ while (current < limit) {
+ DataType data = static_cast<DataType>(source_->Get());
+ switch (data) {
+ case SMI_SERIALIZATION:
+ *current++ = Smi::FromInt(source_->GetInt() - kSmiBias);
+ break;
+ case RAW_DATA_SERIALIZATION: {
+ int size = source_->GetInt();
+ byte* raw_data_out = reinterpret_cast<byte*>(current);
+ for (int j = 0; j < size; j++) {
+ *raw_data_out++ = source_->Get();
+ }
+ current = reinterpret_cast<Object**>(raw_data_out);
+ break;
+ }
+ case OBJECT_SERIALIZATION: {
+ // Recurse to unpack an object that is forward-referenced from here.
+ bool in_new_space = ReadObject(current);
+ if (in_new_space && space != NEW_SPACE) {
+ Heap::RecordWrite(address,
+ reinterpret_cast<Address>(current) - address);
+ }
+ current++;
+ break;
+ }
+ case CODE_OBJECT_SERIALIZATION: {
+ Object* new_code_object = NULL;
+ ReadObject(&new_code_object);
+ Code* code_object = reinterpret_cast<Code*>(new_code_object);
+ // Setting a branch/call to another code object from code.
+ Address location_of_branch_data = reinterpret_cast<Address>(current);
+ Assembler::set_target_address_at(location_of_branch_data,
+ code_object->instruction_start());
+ location_of_branch_data += Assembler::kCallTargetSize;
+ current = reinterpret_cast<Object**>(location_of_branch_data);
+ break;
+ }
+ case BACKREF_SERIALIZATION: {
+ // Write a backreference to an object we unpacked earlier.
+ int backref_space = source_->Get();
+ if (backref_space == NEW_SPACE && space != NEW_SPACE) {
+ Heap::RecordWrite(address,
+ reinterpret_cast<Address>(current) - address);
+ }
+ *current++ = GetAddress(backref_space);
+ break;
+ }
+ case CODE_BACKREF_SERIALIZATION: {
+ int backref_space = source_->Get();
+ // Can't use Code::cast because heap is not set up yet and assertions
+ // will fail.
+ Code* code_object = reinterpret_cast<Code*>(GetAddress(backref_space));
+ // Setting a branch/call to previously decoded code object from code.
+ Address location_of_branch_data = reinterpret_cast<Address>(current);
+ Assembler::set_target_address_at(location_of_branch_data,
+ code_object->instruction_start());
+ location_of_branch_data += Assembler::kCallTargetSize;
+ current = reinterpret_cast<Object**>(location_of_branch_data);
+ break;
+ }
+ case EXTERNAL_REFERENCE_SERIALIZATION: {
+ int reference_id = source_->GetInt();
+ Address address = external_reference_decoder_->Decode(reference_id);
+ *current++ = reinterpret_cast<Object*>(address);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ }
+ ASSERT(current == limit);
+ return space == NEW_SPACE;
+}
+
+
+void SnapshotByteSink::PutInt(uintptr_t integer, const char* description) {
+ const int max_shift = ((kPointerSize * kBitsPerByte) / 7) * 7;
+ for (int shift = max_shift; shift > 0; shift -= 7) {
+ if (integer >= 1u << shift) {
+ Put(((integer >> shift) & 0x7f) | 0x80, "intpart");
+ }
+ }
+ Put(integer & 0x7f, "intlastpart");
+}
+
+#ifdef DEBUG
+
+void Deserializer2::Synchronize(const char* tag) {
+ int data = source_->Get();
+ // If this assert fails then that indicates that you have a mismatch between
+ // the number of GC roots when serializing and deserializing.
+ ASSERT(data == SYNCHRONIZE);
+ do {
+ int character = source_->Get();
+ if (character == 0) break;
+ if (FLAG_debug_serialization) {
+ PrintF("%c", character);
+ }
+ } while (true);
+ if (FLAG_debug_serialization) {
+ PrintF("\n");
+ }
+}
+
+
+void Serializer2::Synchronize(const char* tag) {
+ sink_->Put(SYNCHRONIZE, tag);
+ int character;
+ do {
+ character = *tag++;
+ sink_->Put(character, "tagcharacter");
+ } while (character != 0);
+}
+
+#endif
+
+Serializer2::Serializer2(SnapshotByteSink* sink)
+ : sink_(sink),
+ current_root_index_(0),
+ external_reference_encoder_(NULL) {
+ for (int i = 0; i <= LAST_SPACE; i++) {
+ fullness_[i] = 0;
+ }
+}
+
+
+void Serializer2::Serialize() {
+ // No active threads.
+ CHECK_EQ(NULL, ThreadState::FirstInUse());
+ // No active or weak handles.
+ CHECK(HandleScopeImplementer::instance()->blocks()->is_empty());
+ CHECK_EQ(0, GlobalHandles::NumberOfWeakHandles());
+ ASSERT(external_reference_encoder_ == NULL);
+ external_reference_encoder_ = new ExternalReferenceEncoder();
+ Heap::IterateRoots(this);
+ delete external_reference_encoder_;
+ external_reference_encoder_ = NULL;
+}
+
+
+void Serializer2::VisitPointers(Object** start, Object** end) {
+ for (Object** current = start; current < end; current++) {
+ SerializeObject(*current, TAGGED_REPRESENTATION);
+ }
+}
+
+
+void Serializer2::SerializeObject(
+ Object* o,
+ ReferenceRepresentation reference_representation) {
+ if (o->IsHeapObject()) {
+ HeapObject* heap_object = HeapObject::cast(o);
+ MapWord map_word = heap_object->map_word();
+ if (map_word.IsSerializationAddress()) {
+ int space = SpaceOfAlreadySerializedObject(heap_object);
+ int offset =
+ CurrentAllocationAddress(space) - map_word.ToSerializationAddress();
+ // If we are actually dealing with real offsets (and not a numbering of
+ // all objects) then we should shift out the bits that are always 0.
+ if (!SpaceIsLarge(space)) offset >>= kObjectAlignmentBits;
+ if (reference_representation == CODE_TARGET_REPRESENTATION) {
+ sink_->Put(CODE_BACKREF_SERIALIZATION, "BackRefCodeSerialization");
+ } else {
+ ASSERT(reference_representation == TAGGED_REPRESENTATION);
+ sink_->Put(BACKREF_SERIALIZATION, "BackRefSerialization");
+ }
+ sink_->Put(space, "space");
+ sink_->PutInt(offset, "offset");
+ } else {
+ // Object has not yet been serialized. Serialize it here.
+ ObjectSerializer serializer(this,
+ heap_object,
+ sink_,
+ reference_representation);
+ serializer.Serialize();
+ }
+ } else {
+ // Serialize a Smi.
+ unsigned int value = Smi::cast(o)->value() + kSmiBias;
+ sink_->Put(SMI_SERIALIZATION, "SmiSerialization");
+ sink_->PutInt(value, "smi");
+ }
+}
+
+
+void Serializer2::ObjectSerializer::Serialize() {
+ int space = Serializer2::SpaceOfObject(object_);
+ int size = object_->Size();
+
+ if (reference_representation_ == TAGGED_REPRESENTATION) {
+ sink_->Put(OBJECT_SERIALIZATION, "ObjectSerialization");
+ } else {
+ ASSERT(reference_representation_ == CODE_TARGET_REPRESENTATION);
+ sink_->Put(CODE_OBJECT_SERIALIZATION, "ObjectSerialization");
+ }
+ sink_->Put(space, "space");
+ sink_->PutInt(size >> kObjectAlignmentBits, "Size in words");
+
+ // Get the map before overwriting it.
+ Map* map = object_->map();
+ // Mark this object as already serialized.
+ object_->set_map_word(
+ MapWord::FromSerializationAddress(serializer_->Allocate(space, size)));
+
+ // Serialize the map (first word of the object).
+ serializer_->SerializeObject(map, TAGGED_REPRESENTATION);
+
+ // Serialize the rest of the object.
+ ASSERT(bytes_processed_so_far_ == 0);
+ bytes_processed_so_far_ = kPointerSize;
+ object_->IterateBody(map->instance_type(), size, this);
+ OutputRawData(object_->address() + size);
+}
+
+
+void Serializer2::ObjectSerializer::VisitPointers(Object** start,
+ Object** end) {
+ Address pointers_start = reinterpret_cast<Address>(start);
+ OutputRawData(pointers_start);
+
+ for (Object** current = start; current < end; current++) {
+ serializer_->SerializeObject(*current, TAGGED_REPRESENTATION);
+ }
+ bytes_processed_so_far_ += (end - start) * kPointerSize;
+}
+
+
+void Serializer2::ObjectSerializer::VisitExternalReferences(Address* start,
+ Address* end) {
+ Address references_start = reinterpret_cast<Address>(start);
+ OutputRawData(references_start);
+
+ for (Address* current = start; current < end; current++) {
+ sink_->Put(EXTERNAL_REFERENCE_SERIALIZATION, "External reference");
+ int reference_id = serializer_->EncodeExternalReference(*current);
+ sink_->PutInt(reference_id, "reference id");
+ }
+ bytes_processed_so_far_ += (end - start) * kPointerSize;
+}
+
+
+void Serializer2::ObjectSerializer::VisitCodeTarget(RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
+ Address target_start = rinfo->target_address_address();
+ OutputRawData(target_start);
+ Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
+ serializer_->SerializeObject(target, CODE_TARGET_REPRESENTATION);
+ bytes_processed_so_far_ += Assembler::kCallTargetSize;
+}
+
+
+void Serializer2::ObjectSerializer::OutputRawData(Address up_to) {
+ Address object_start = object_->address();
+ int up_to_offset = up_to - object_start;
+ int skipped = up_to_offset - bytes_processed_so_far_;
+ ASSERT(skipped >= 0);
+ if (skipped != 0) {
+ sink_->Put(RAW_DATA_SERIALIZATION, "raw data");
+ sink_->PutInt(skipped, "length");
+ for (int i = 0; i < skipped; i++) {
+ unsigned int data = object_start[bytes_processed_so_far_ + i];
+ sink_->Put(data, "byte");
+ }
+ }
+ bytes_processed_so_far_ += skipped;
+}
+
+
+int Serializer2::SpaceOfObject(HeapObject* object) {
+ for (int i = FIRST_SPACE; i <= LAST_SPACE; i++) {
+ AllocationSpace s = static_cast<AllocationSpace>(i);
+ if (Heap::InSpace(object, s)) {
+ if (i == LO_SPACE) {
+ if (object->IsCode()) {
+ return kLargeCode;
+ } else if (object->IsFixedArray()) {
+ return kLargeFixedArray;
+ } else {
+ return kLargeData;
+ }
+ }
+ return i;
+ }
+ }
+ UNREACHABLE();
+ return 0;
+}
+
+
+int Serializer2::SpaceOfAlreadySerializedObject(HeapObject* object) {
+ for (int i = FIRST_SPACE; i <= LAST_SPACE; i++) {
+ AllocationSpace s = static_cast<AllocationSpace>(i);
+ if (Heap::InSpace(object, s)) {
+ return i;
+ }
+ }
+ UNREACHABLE();
+ return 0;
+}
+
+
+int Serializer2::Allocate(int space, int size) {
+ ASSERT(space >= 0 && space < kNumberOfSpaces);
+ if (SpaceIsLarge(space)) {
+ // In large object space we merely number the objects instead of trying to
+ // determine some sort of address.
+ return fullness_[LO_SPACE]++;
+ }
+ if (SpaceIsPaged(space)) {
+ // Paged spaces are a little special. We encode their addresses as if the
+ // pages were all contiguous and each page were filled up in the range
+ // 0 - Page::kObjectAreaSize. In practice the pages may not be contiguous
+ // and allocation does not start at offset 0 in the page, but this scheme
+ // means the deserializer can get the page number quickly by shifting the
+ // serialized address.
+ ASSERT(IsPowerOf2(Page::kPageSize));
+ int used_in_this_page = (fullness_[space] & (Page::kPageSize - 1));
+ ASSERT(size <= Page::kObjectAreaSize);
+ if (used_in_this_page + size > Page::kObjectAreaSize) {
+ fullness_[space] = RoundUp(fullness_[space], Page::kPageSize);
+ }
+ }
+ int allocation_address = fullness_[space];
+ fullness_[space] = allocation_address + size;
+ return allocation_address;
+}
+
+
} } // namespace v8::internal
// A Deserializer reads a snapshot and reconstructs the Object graph it defines.
-class Deserializer: public ObjectVisitor {
+
+// TODO(erikcorry): Get rid of this superclass when we are using the new
+// snapshot code exclusively.
+class GenericDeserializer: public ObjectVisitor {
+ public:
+ virtual void GetLog() = 0;
+ virtual void Deserialize() = 0;
+};
+
+
+// TODO(erikcorry): Get rid of this class.
+class Deserializer: public GenericDeserializer {
public:
// Create a deserializer. The snapshot is held in str and has size len.
Deserializer(const byte* str, int len);
DISALLOW_COPY_AND_ASSIGN(Deserializer);
};
+
+class SnapshotByteSource {
+ public:
+ SnapshotByteSource(const byte* array, int length)
+ : data_(array), length_(length), position_(0) { }
+
+ bool HasMore() { return position_ < length_; }
+
+ int Get() {
+ ASSERT(position_ < length_);
+ return data_[position_++];
+ }
+
+ int GetInt() {
+ // A little unwind to catch the really small ints.
+ int snapshot_byte = Get();
+ if ((snapshot_byte & 0x80) == 0) {
+ return snapshot_byte;
+ }
+ uintptr_t accumulator = (snapshot_byte & 0x7f) << 7;
+ while (true) {
+ snapshot_byte = Get();
+ if ((snapshot_byte & 0x80) == 0) {
+ return accumulator | snapshot_byte;
+ }
+ accumulator = (accumulator | (snapshot_byte & 0x7f)) << 7;
+ }
+ UNREACHABLE();
+ return accumulator;
+ }
+
+ bool AtEOF() {
+ return position_ == length_;
+ }
+
+ private:
+ const byte* data_;
+ int length_;
+ int position_;
+};
+
+
+// The SerDes class is a common superclass for Serializer2 and Deserializer2
+// which is used to store common constants and methods used by both.
+// TODO(erikcorry): This should inherit from ObjectVisitor.
+class SerDes: public GenericDeserializer {
+ protected:
+ enum DataType {
+ SMI_SERIALIZATION,
+ RAW_DATA_SERIALIZATION,
+ OBJECT_SERIALIZATION,
+ CODE_OBJECT_SERIALIZATION,
+ BACKREF_SERIALIZATION,
+ CODE_BACKREF_SERIALIZATION,
+ EXTERNAL_REFERENCE_SERIALIZATION,
+ SYNCHRONIZE
+ };
+ // Our Smi encoding is much more efficient for small positive integers than it
+ // is for negative numbers so we add a bias before encoding and subtract it
+ // after encoding so that popular small negative Smis are efficiently encoded.
+ static const int kSmiBias = 16;
+ static const int kLargeData = LAST_SPACE;
+ static const int kLargeCode = kLargeData + 1;
+ static const int kLargeFixedArray = kLargeCode + 1;
+ static const int kNumberOfSpaces = kLargeFixedArray + 1;
+
+ static inline bool SpaceIsLarge(int space) { return space >= kLargeData; }
+ static inline bool SpaceIsPaged(int space) {
+ return space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE;
+ }
+};
+
+
+
+// A Deserializer reads a snapshot and reconstructs the Object graph it defines.
+class Deserializer2: public SerDes {
+ public:
+ // Create a deserializer from a snapshot byte source.
+ explicit Deserializer2(SnapshotByteSource* source);
+
+ virtual ~Deserializer2() { }
+
+ // Deserialize the snapshot into an empty heap.
+ void Deserialize();
+ void GetLog() { } // TODO(erikcorry): Get rid of this.
+#ifdef DEBUG
+ virtual void Synchronize(const char* tag);
+#endif
+
+ private:
+ virtual void VisitPointers(Object** start, Object** end);
+
+ virtual void VisitExternalReferences(Address* start, Address* end) {
+ UNREACHABLE();
+ }
+
+ virtual void VisitRuntimeEntry(RelocInfo* rinfo) {
+ UNREACHABLE();
+ }
+
+ int CurrentAllocationAddress(int space) {
+ // The three different kinds of large objects have different tags in the
+ // snapshot so the deserializer knows which kind of object to allocate,
+ // but they share a fullness_ entry.
+ if (SpaceIsLarge(space)) space = LO_SPACE;
+ return fullness_[space];
+ }
+
+ HeapObject* GetAddress(int space);
+ Address Allocate(int space, int size);
+ bool ReadObject(Object** write_back);
+
+ // Keep track of the pages in the paged spaces.
+ // (In large object space we are keeping track of individual objects
+ // rather than pages.) In new space we just need the address of the
+ // first object and the others will flow from that.
+ List<Address> pages_[SerDes::kNumberOfSpaces];
+
+ SnapshotByteSource* source_;
+ ExternalReferenceDecoder* external_reference_decoder_;
+ // Keep track of the fullness of each space in order to generate
+ // relative addresses for back references. Large objects are
+ // just numbered sequentially since relative addresses make no
+ // sense in large object space.
+ int fullness_[LAST_SPACE + 1];
+
+ DISALLOW_COPY_AND_ASSIGN(Deserializer2);
+};
+
+
+class SnapshotByteSink {
+ public:
+ virtual ~SnapshotByteSink() { }
+ virtual void Put(int byte, const char* description) = 0;
+ void PutInt(uintptr_t integer, const char* description);
+};
+
+
+class Serializer2 : public SerDes {
+ public:
+ explicit Serializer2(SnapshotByteSink* sink);
+ // Serialize the current state of the heap. This operation destroys the
+ // heap contents.
+ void Serialize();
+ void VisitPointers(Object** start, Object** end);
+ void GetLog() { } // TODO(erikcorry): Get rid of this.
+ void Deserialize() { } // TODO(erikcorry): Get rid of this.
+#ifdef DEBUG
+ virtual void Synchronize(const char* tag);
+#endif
+
+ private:
+ enum ReferenceRepresentation {
+ TAGGED_REPRESENTATION, // A tagged object reference.
+ CODE_TARGET_REPRESENTATION // A reference to first instruction in target.
+ };
+ class ObjectSerializer : public ObjectVisitor {
+ public:
+ ObjectSerializer(Serializer2* serializer,
+ Object* o,
+ SnapshotByteSink* sink,
+ ReferenceRepresentation representation)
+ : serializer_(serializer),
+ object_(HeapObject::cast(o)),
+ sink_(sink),
+ reference_representation_(representation),
+ bytes_processed_so_far_(0) { }
+ void Serialize();
+ void VisitPointers(Object** start, Object** end);
+ void VisitExternalReferences(Address* start, Address* end);
+ void VisitCodeTarget(RelocInfo* target);
+
+ private:
+ void OutputRawData(Address up_to);
+
+ Serializer2* serializer_;
+ HeapObject* object_;
+ SnapshotByteSink* sink_;
+ ReferenceRepresentation reference_representation_;
+ int bytes_processed_so_far_;
+ };
+
+ void SerializeObject(Object* o, ReferenceRepresentation representation);
+ void InitializeAllocators();
+ // This will return the space for an object. If the object is in large
+ // object space it may return kLargeCode or kLargeFixedArray in order
+ // to indicate to the deserializer what kind of large object allocation
+ // to make.
+ static int SpaceOfObject(HeapObject* object);
+ // This just returns the space of the object. It will return LO_SPACE
+ // for all large objects since you can't check the type of the object
+ // once the map has been used for the serialization address.
+ static int SpaceOfAlreadySerializedObject(HeapObject* object);
+ int Allocate(int space, int size);
+ int CurrentAllocationAddress(int space) {
+ if (SpaceIsLarge(space)) space = LO_SPACE;
+ return fullness_[space];
+ }
+ int EncodeExternalReference(Address addr) {
+ return external_reference_encoder_->Encode(addr);
+ }
+
+ // Keep track of the fullness of each space in order to generate
+ // relative addresses for back references. Large objects are
+ // just numbered sequentially since relative addresses make no
+ // sense in large object space.
+ int fullness_[LAST_SPACE + 1];
+ SnapshotByteSink* sink_;
+ int current_root_index_;
+ ExternalReferenceEncoder* external_reference_encoder_;
+
+ friend class ObjectSerializer;
+ friend class Deserializer2;
+
+ DISALLOW_COPY_AND_ASSIGN(Serializer2);
+};
+
} } // namespace v8::internal
#endif // V8_SERIALIZE_H_
}
+bool Snapshot::Deserialize2(const byte* content, int len) {
+ SnapshotByteSource source(content, len);
+ Deserializer2 deserializer(&source);
+ return V8::Initialize(&deserializer);
+}
+
+
bool Snapshot::Initialize(const char* snapshot_file) {
if (snapshot_file) {
int len;
}
+bool Snapshot::Initialize2(const char* snapshot_file) {
+ if (snapshot_file) {
+ int len;
+ byte* str = ReadBytes(snapshot_file, &len);
+ if (!str) return false;
+ Deserialize2(str, len);
+ DeleteArray(str);
+ } else if (size_ > 0) {
+ Deserialize2(data_, size_);
+ }
+ return true;
+}
+
+
bool Snapshot::WriteToFile(const char* snapshot_file) {
Serializer ser;
ser.Serialize();
}
+class FileByteSink : public SnapshotByteSink {
+ public:
+ explicit FileByteSink(const char* snapshot_file) {
+ fp_ = fopen(snapshot_file, "wb");
+ if (fp_ == NULL) {
+ PrintF("Unable to write to snapshot file \"%s\"\n", snapshot_file);
+ exit(1);
+ }
+ }
+ virtual ~FileByteSink() {
+ if (fp_ != NULL) {
+ fclose(fp_);
+ }
+ }
+ virtual void Put(int byte, const char* description) {
+ if (fp_ != NULL) {
+ fputc(byte, fp_);
+ }
+ }
+
+ private:
+ FILE* fp_;
+};
+
+
+bool Snapshot::WriteToFile2(const char* snapshot_file) {
+ FileByteSink file(snapshot_file);
+ Serializer2 ser(&file);
+ ser.Serialize();
+ return true;
+}
+
+
+
} } // namespace v8::internal
// NULL, use the internal snapshot instead. Returns false if no snapshot
// could be found.
static bool Initialize(const char* snapshot_file = NULL);
+ static bool Initialize2(const char* snapshot_file = NULL);
// Returns whether or not the snapshot is enabled.
static bool IsEnabled() { return size_ != 0; }
// Write snapshot to the given file. Returns true if snapshot was written
// successfully.
static bool WriteToFile(const char* snapshot_file);
+ static bool WriteToFile2(const char* snapshot_file);
private:
static const byte data_[];
static int size_;
static bool Deserialize(const byte* content, int len);
+ static bool Deserialize2(const byte* content, int len);
DISALLOW_IMPLICIT_CONSTRUCTORS(Snapshot);
};
// correct size.
if (size_in_bytes > ByteArray::kAlignedSize) {
set_map(Heap::raw_unchecked_byte_array_map());
- ByteArray::cast(this)->set_length(ByteArray::LengthFor(size_in_bytes));
+ // Can't use ByteArray::cast because it fails during deserialization.
+ ByteArray* this_as_byte_array = reinterpret_cast<ByteArray*>(this);
+ this_as_byte_array->set_length(ByteArray::LengthFor(size_in_bytes));
} else if (size_in_bytes == kPointerSize) {
set_map(Heap::raw_unchecked_one_pointer_filler_map());
} else if (size_in_bytes == 2 * kPointerSize) {
} else {
UNREACHABLE();
}
- ASSERT(Size() == size_in_bytes);
+ // We would like to ASSERT(Size() == size_in_bytes) but this would fail during
+ // deserialization because the byte array map is not done yet.
}
return AllocateInNextPage(current_page, size_in_bytes);
}
- // There is no next page in this space. Try free list allocation.
- int wasted_bytes;
- Object* result = free_list_.Allocate(size_in_bytes, &wasted_bytes);
- accounting_stats_.WasteBytes(wasted_bytes);
- if (!result->IsFailure()) {
- accounting_stats_.AllocateBytes(size_in_bytes);
- return HeapObject::cast(result);
+ // There is no next page in this space. Try free list allocation unless that
+ // is currently forbidden.
+ if (!Heap::linear_allocation()) {
+ int wasted_bytes;
+ Object* result = free_list_.Allocate(size_in_bytes, &wasted_bytes);
+ accounting_stats_.WasteBytes(wasted_bytes);
+ if (!result->IsFailure()) {
+ accounting_stats_.AllocateBytes(size_in_bytes);
+ return HeapObject::cast(result);
+ }
}
// Free list allocation failed and there is no next page. Fail if we have
return AllocateInNextPage(current_page, size_in_bytes);
}
- // There is no next page in this space. Try free list allocation.
- // The fixed space free list implicitly assumes that all free blocks
- // are of the fixed size.
- if (size_in_bytes == object_size_in_bytes_) {
+ // There is no next page in this space. Try free list allocation unless
+ // that is currently forbidden. The fixed space free list implicitly assumes
+ // that all free blocks are of the fixed size.
+ if (!Heap::linear_allocation()) {
Object* result = free_list_.Allocate();
if (!result->IsFailure()) {
accounting_stats_.AllocateBytes(size_in_bytes);
bool V8::has_been_disposed_ = false;
bool V8::has_fatal_error_ = false;
-bool V8::Initialize(Deserializer *des) {
+bool V8::Initialize(GenericDeserializer *des) {
bool create_heap_objects = des == NULL;
if (has_been_disposed_ || has_fatal_error_) return false;
if (IsRunning()) return true;
// created from scratch. If a non-null Deserializer is given, the
// initial state is created by reading the deserialized data into an
// empty heap.
- static bool Initialize(Deserializer* des);
+ static bool Initialize(GenericDeserializer* des);
static void TearDown();
static bool IsRunning() { return is_running_; }
// To be dead you have to have lived
static inline Address target_address_at(Address pc);
static inline void set_target_address_at(Address pc, Address target);
inline Handle<Object> code_target_object_handle_at(Address pc);
+ // Number of bytes taken up by the branch target in the code.
+ static const int kCallTargetSize = 4; // Use 32-bit displacement.
// Distance between the address of the code target in the call instruction
// and the return address pushed on the stack.
static const int kCallTargetAddressOffset = 4; // Use 32-bit displacement.
[ $arch == arm ]
+# New serialization doesn't work on ARM yet.
+test-serialize/Deserialize2: SKIP
+test-serialize/DeserializeAndRunScript2: SKIP
+
# BUG(113): Test seems flaky on ARM.
test-spaces/LargeObjectSpace: PASS || FAIL
}
+static void Serialize2() {
+ Serializer::Enable();
+ // We have to create one context. One reason for this is so that the builtins
+ // can be loaded from v8natives.js and their addresses can be processed. This
+ // will clear the pending fixups array, which would otherwise contain GC roots
+ // that would confuse the serialization/deserialization process.
+ v8::Persistent<v8::Context> env = v8::Context::New();
+ env.Dispose();
+ Snapshot::WriteToFile2(FLAG_testing_serialization_file);
+}
+
+
// Test that the whole heap can be serialized when running from the
// internal snapshot.
// (Smoke test.)
}
+// Test that the whole heap can be serialized.
+TEST(Serialize2) {
+ v8::V8::Initialize();
+ Serialize2();
+}
+
+
// Test that the heap isn't destroyed after a serialization.
TEST(SerializeNondestructive) {
if (Snapshot::IsEnabled()) return;
}
+static void Deserialize2() {
+ CHECK(Snapshot::Initialize2(FLAG_testing_serialization_file));
+}
+
+
static void SanityCheck() {
v8::HandleScope scope;
#ifdef DEBUG
SanityCheck();
}
+
+DEPENDENT_TEST(Deserialize2, Serialize2) {
+ v8::HandleScope scope;
+
+ Deserialize2();
+
+ fflush(stdout);
+
+ v8::Persistent<v8::Context> env = v8::Context::New();
+ env->Enter();
+
+ SanityCheck();
+}
+
+
DEPENDENT_TEST(DeserializeAndRunScript, Serialize) {
v8::HandleScope scope;
}
+DEPENDENT_TEST(DeserializeAndRunScript2, Serialize2) {
+ v8::HandleScope scope;
+
+ Deserialize2();
+
+ v8::Persistent<v8::Context> env = v8::Context::New();
+ env->Enter();
+
+ const char* c_source = "\"1234\".length";
+ v8::Local<v8::String> source = v8::String::New(c_source);
+ v8::Local<v8::Script> script = v8::Script::Compile(source);
+ CHECK_EQ(4, script->Run()->Int32Value());
+}
+
+
DEPENDENT_TEST(DeserializeNatives, Serialize) {
v8::HandleScope scope;
}
-extern "C" void V8_Fatal(const char* file, int line, const char* format, ...);
-
-
TEST(TestThatAlwaysSucceeds) {
}