diff options
Diffstat (limited to 'src/3rdparty/v8/src/serialize.cc')
-rw-r--r-- | src/3rdparty/v8/src/serialize.cc | 716 |
1 files changed, 320 insertions, 396 deletions
diff --git a/src/3rdparty/v8/src/serialize.cc b/src/3rdparty/v8/src/serialize.cc index cf8e5e1..dfc5574 100644 --- a/src/3rdparty/v8/src/serialize.cc +++ b/src/3rdparty/v8/src/serialize.cc @@ -37,6 +37,7 @@ #include "platform.h" #include "runtime.h" #include "serialize.h" +#include "snapshot.h" #include "stub-cache.h" #include "v8threads.h" @@ -510,6 +511,22 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) { UNCLASSIFIED, 47, "date_cache_stamp"); + Add(ExternalReference::address_of_pending_message_obj(isolate).address(), + UNCLASSIFIED, + 48, + "address_of_pending_message_obj"); + Add(ExternalReference::address_of_has_pending_message(isolate).address(), + UNCLASSIFIED, + 49, + "address_of_has_pending_message"); + Add(ExternalReference::address_of_pending_message_script(isolate).address(), + UNCLASSIFIED, + 50, + "pending_message_script"); + Add(ExternalReference::get_make_code_young_function(isolate).address(), + UNCLASSIFIED, + 51, + "Code::MakeCodeYoung"); } @@ -586,104 +603,27 @@ Deserializer::Deserializer(SnapshotByteSource* source) : isolate_(NULL), source_(source), external_reference_decoder_(NULL) { -} - - -// 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 Deserializer::Allocate(int space_index, Space* space, int size) { - Address address; - if (!SpaceIsLarge(space_index)) { - ASSERT(!SpaceIsPaged(space_index) || - size <= Page::kPageSize - Page::kObjectStartOffset); - MaybeObject* maybe_new_allocation; - if (space_index == NEW_SPACE) { - maybe_new_allocation = - reinterpret_cast<NewSpace*>(space)->AllocateRaw(size); - } else { - maybe_new_allocation = - reinterpret_cast<PagedSpace*>(space)->AllocateRaw(size); - } - ASSERT(!maybe_new_allocation->IsFailure()); - Object* new_allocation = maybe_new_allocation->ToObjectUnchecked(); - HeapObject* new_object = HeapObject::cast(new_allocation); - address = new_object->address(); - high_water_[space_index] = address + size; - } else { - ASSERT(SpaceIsLarge(space_index)); - LargeObjectSpace* lo_space = reinterpret_cast<LargeObjectSpace*>(space); - Object* new_allocation; - if (space_index == kLargeData || space_index == kLargeFixedArray) { - new_allocation = - lo_space->AllocateRaw(size, NOT_EXECUTABLE)->ToObjectUnchecked(); - } else { - ASSERT_EQ(kLargeCode, space_index); - new_allocation = - lo_space->AllocateRaw(size, EXECUTABLE)->ToObjectUnchecked(); - } - HeapObject* new_object = HeapObject::cast(new_allocation); - // Record all large objects in the same space. - address = new_object->address(); - pages_[LO_SPACE].Add(address); + for (int i = 0; i < LAST_SPACE + 1; i++) { + reservations_[i] = kUninitializedReservation; } - last_object_address_ = 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* Deserializer::GetAddressFromEnd(int space) { - int offset = source_->GetInt(); - ASSERT(!SpaceIsLarge(space)); - offset <<= kObjectAlignmentBits; - return HeapObject::FromAddress(high_water_[space] - offset); -} - - -// This returns the address of an object that has been described in the -// snapshot as being offset bytes into a particular space. -HeapObject* Deserializer::GetAddressFromStart(int space) { - int offset = source_->GetInt(); - if (SpaceIsLarge(space)) { - // Large spaces have one object per 'page'. - return HeapObject::FromAddress(pages_[LO_SPACE][offset]); - } - offset <<= kObjectAlignmentBits; - if (space == NEW_SPACE) { - // New space has only one space - numbered 0. - return HeapObject::FromAddress(pages_[space][0] + offset); - } - ASSERT(SpaceIsPaged(space)); - int page_of_pointee = offset >> kPageSizeBits; - Address object_address = pages_[space][page_of_pointee] + - (offset & Page::kPageAlignmentMask); - return HeapObject::FromAddress(object_address); } void Deserializer::Deserialize() { isolate_ = Isolate::Current(); ASSERT(isolate_ != NULL); - // Don't GC while deserializing - just expand the heap. - AlwaysAllocateScope always_allocate; - // Don't use the free lists while deserializing. - LinearAllocationScope allocate_linearly; + isolate_->heap()->ReserveSpace(reservations_, &high_water_[0]); // No active threads. ASSERT_EQ(NULL, isolate_->thread_manager()->FirstThreadStateInUse()); // No active handles. ASSERT(isolate_->handle_scope_implementer()->blocks()->is_empty()); - // Make sure the entire partial snapshot cache is traversed, filling it with - // valid object pointers. - isolate_->set_serialize_partial_snapshot_cache_length( - Isolate::kPartialSnapshotCacheCapacity); ASSERT_EQ(NULL, external_reference_decoder_); external_reference_decoder_ = new ExternalReferenceDecoder(); isolate_->heap()->IterateStrongRoots(this, VISIT_ONLY_STRONG); + isolate_->heap()->RepairFreeListsAfterBoot(); isolate_->heap()->IterateWeakRoots(this, VISIT_ALL); - isolate_->heap()->set_global_contexts_list( + isolate_->heap()->set_native_contexts_list( isolate_->heap()->undefined_value()); // Update data pointers to the external strings containing natives sources. @@ -693,19 +633,33 @@ void Deserializer::Deserialize() { ExternalAsciiString::cast(source)->update_data_cache(); } } + + // Issue code events for newly deserialized code objects. + LOG_CODE_EVENT(isolate_, LogCodeObjects()); + LOG_CODE_EVENT(isolate_, LogCompiledFunctions()); } void Deserializer::DeserializePartial(Object** root) { isolate_ = Isolate::Current(); - // Don't GC while deserializing - just expand the heap. - AlwaysAllocateScope always_allocate; - // Don't use the free lists while deserializing. - LinearAllocationScope allocate_linearly; + for (int i = NEW_SPACE; i < kNumberOfSpaces; i++) { + ASSERT(reservations_[i] != kUninitializedReservation); + } + isolate_->heap()->ReserveSpace(reservations_, &high_water_[0]); if (external_reference_decoder_ == NULL) { external_reference_decoder_ = new ExternalReferenceDecoder(); } + + // Keep track of the code space start and end pointers in case new + // code objects were unserialized + OldSpace* code_space = isolate_->heap()->code_space(); + Address start_address = code_space->top(); VisitPointer(root); + + // There's no code deserialized here. If this assert fires + // then that's changed and logging should be added to notify + // the profiler et al of the new code. + CHECK_EQ(start_address, code_space->top()); } @@ -733,10 +687,9 @@ void Deserializer::VisitPointers(Object** start, Object** end) { // written very late, which means the FreeSpace map is not set up by the // time we need to use it to mark the space at the end of a page free. void Deserializer::ReadObject(int space_number, - Space* space, Object** write_back) { int size = source_->GetInt() << kObjectAlignmentBits; - Address address = Allocate(space_number, space, size); + Address address = Allocate(space_number, size); *write_back = HeapObject::FromAddress(address); Object** current = reinterpret_cast<Object**>(address); Object** limit = current + (size >> kPointerSizeLog2); @@ -745,44 +698,19 @@ void Deserializer::ReadObject(int space_number, } ReadChunk(current, limit, space_number, address); #ifdef DEBUG - bool is_codespace = (space == HEAP->code_space()) || - ((space == HEAP->lo_space()) && (space_number == kLargeCode)); + bool is_codespace = (space_number == CODE_SPACE); ASSERT(HeapObject::FromAddress(address)->IsCode() == is_codespace); #endif } - -// This macro is always used with a constant argument so it should all fold -// away to almost nothing in the generated code. It might be nicer to do this -// with the ternary operator but there are type issues with that. -#define ASSIGN_DEST_SPACE(space_number) \ - Space* dest_space; \ - if (space_number == NEW_SPACE) { \ - dest_space = isolate->heap()->new_space(); \ - } else if (space_number == OLD_POINTER_SPACE) { \ - dest_space = isolate->heap()->old_pointer_space(); \ - } else if (space_number == OLD_DATA_SPACE) { \ - dest_space = isolate->heap()->old_data_space(); \ - } else if (space_number == CODE_SPACE) { \ - dest_space = isolate->heap()->code_space(); \ - } else if (space_number == MAP_SPACE) { \ - dest_space = isolate->heap()->map_space(); \ - } else if (space_number == CELL_SPACE) { \ - dest_space = isolate->heap()->cell_space(); \ - } else { \ - ASSERT(space_number >= LO_SPACE); \ - dest_space = isolate->heap()->lo_space(); \ - } - - -static const int kUnknownOffsetFromStart = -1; - - void Deserializer::ReadChunk(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 + // are no new space objects in current boot snapshots, so it's not needed, + // but that may change. bool write_barrier_needed = (current_object_address != NULL && source_space != NEW_SPACE && source_space != CELL_SPACE && @@ -798,21 +726,19 @@ void Deserializer::ReadChunk(Object** current, ASSERT((within & ~kWhereToPointMask) == 0); \ ASSERT((space_number & ~kSpaceMask) == 0); -#define CASE_BODY(where, how, within, space_number_if_any, offset_from_start) \ +#define CASE_BODY(where, how, within, space_number_if_any) \ { \ bool emit_write_barrier = false; \ bool current_was_incremented = false; \ int space_number = space_number_if_any == kAnyOldSpace ? \ (data & kSpaceMask) : space_number_if_any; \ if (where == kNewObject && how == kPlain && within == kStartOfObject) {\ - ASSIGN_DEST_SPACE(space_number) \ - ReadObject(space_number, dest_space, current); \ + ReadObject(space_number, current); \ emit_write_barrier = (space_number == NEW_SPACE); \ } else { \ Object* new_object = NULL; /* May not be a real Object pointer. */ \ if (where == kNewObject) { \ - ASSIGN_DEST_SPACE(space_number) \ - ReadObject(space_number, dest_space, &new_object); \ + ReadObject(space_number, &new_object); \ } else if (where == kRootArray) { \ int root_id = source_->GetInt(); \ new_object = isolate->heap()->roots_array_start()[root_id]; \ @@ -823,6 +749,9 @@ void Deserializer::ReadChunk(Object** current, [cache_index]; \ emit_write_barrier = isolate->heap()->InNewSpace(new_object); \ } else if (where == kExternalReference) { \ + int skip = source_->GetInt(); \ + current = reinterpret_cast<Object**>(reinterpret_cast<Address>( \ + current) + skip); \ int reference_id = source_->GetInt(); \ Address address = external_reference_decoder_-> \ Decode(reference_id); \ @@ -831,21 +760,26 @@ void Deserializer::ReadChunk(Object** current, emit_write_barrier = (space_number == NEW_SPACE); \ new_object = GetAddressFromEnd(data & kSpaceMask); \ } else { \ - ASSERT(where == kFromStart); \ - if (offset_from_start == kUnknownOffsetFromStart) { \ - emit_write_barrier = (space_number == NEW_SPACE); \ - new_object = GetAddressFromStart(data & kSpaceMask); \ + ASSERT(where == kBackrefWithSkip); \ + int skip = source_->GetInt(); \ + current = reinterpret_cast<Object**>( \ + reinterpret_cast<Address>(current) + skip); \ + emit_write_barrier = (space_number == NEW_SPACE); \ + new_object = GetAddressFromEnd(data & kSpaceMask); \ + } \ + if (within == kInnerPointer) { \ + if (space_number != CODE_SPACE || new_object->IsCode()) { \ + Code* new_code_object = reinterpret_cast<Code*>(new_object); \ + new_object = reinterpret_cast<Object*>( \ + new_code_object->instruction_start()); \ } else { \ - Address object_address = pages_[space_number][0] + \ - (offset_from_start << kObjectAlignmentBits); \ - new_object = HeapObject::FromAddress(object_address); \ + ASSERT(space_number == CODE_SPACE); \ + JSGlobalPropertyCell* cell = \ + JSGlobalPropertyCell::cast(new_object); \ + new_object = reinterpret_cast<Object*>( \ + cell->ValueAddress()); \ } \ } \ - if (within == kFirstInstruction) { \ - Code* new_code_object = reinterpret_cast<Code*>(new_object); \ - new_object = reinterpret_cast<Object*>( \ - new_code_object->instruction_start()); \ - } \ if (how == kFromCode) { \ Address location_of_branch_data = \ reinterpret_cast<Address>(current); \ @@ -871,47 +805,18 @@ void Deserializer::ReadChunk(Object** current, break; \ } \ -// This generates a case and a body for each space. The large object spaces are -// very rare in snapshots so they are grouped in one body. -#define ONE_PER_SPACE(where, how, within) \ - CASE_STATEMENT(where, how, within, NEW_SPACE) \ - CASE_BODY(where, how, within, NEW_SPACE, kUnknownOffsetFromStart) \ - CASE_STATEMENT(where, how, within, OLD_DATA_SPACE) \ - CASE_BODY(where, how, within, OLD_DATA_SPACE, kUnknownOffsetFromStart) \ - CASE_STATEMENT(where, how, within, OLD_POINTER_SPACE) \ - CASE_BODY(where, how, within, OLD_POINTER_SPACE, kUnknownOffsetFromStart) \ - CASE_STATEMENT(where, how, within, CODE_SPACE) \ - CASE_BODY(where, how, within, CODE_SPACE, kUnknownOffsetFromStart) \ - CASE_STATEMENT(where, how, within, CELL_SPACE) \ - CASE_BODY(where, how, within, CELL_SPACE, kUnknownOffsetFromStart) \ - CASE_STATEMENT(where, how, within, MAP_SPACE) \ - CASE_BODY(where, how, within, MAP_SPACE, kUnknownOffsetFromStart) \ - CASE_STATEMENT(where, how, within, kLargeData) \ - CASE_STATEMENT(where, how, within, kLargeCode) \ - CASE_STATEMENT(where, how, within, kLargeFixedArray) \ - CASE_BODY(where, how, within, kAnyOldSpace, kUnknownOffsetFromStart) - // This generates a case and a body for the new space (which has to do extra // write barrier handling) and handles the other spaces with 8 fall-through // cases and one body. #define ALL_SPACES(where, how, within) \ CASE_STATEMENT(where, how, within, NEW_SPACE) \ - CASE_BODY(where, how, within, NEW_SPACE, kUnknownOffsetFromStart) \ + CASE_BODY(where, how, within, NEW_SPACE) \ CASE_STATEMENT(where, how, within, OLD_DATA_SPACE) \ CASE_STATEMENT(where, how, within, OLD_POINTER_SPACE) \ CASE_STATEMENT(where, how, within, CODE_SPACE) \ CASE_STATEMENT(where, how, within, CELL_SPACE) \ CASE_STATEMENT(where, how, within, MAP_SPACE) \ - CASE_STATEMENT(where, how, within, kLargeData) \ - CASE_STATEMENT(where, how, within, kLargeCode) \ - CASE_STATEMENT(where, how, within, kLargeFixedArray) \ - CASE_BODY(where, how, within, kAnyOldSpace, kUnknownOffsetFromStart) - -#define ONE_PER_CODE_SPACE(where, how, within) \ - CASE_STATEMENT(where, how, within, CODE_SPACE) \ - CASE_BODY(where, how, within, CODE_SPACE, kUnknownOffsetFromStart) \ - CASE_STATEMENT(where, how, within, kLargeCode) \ - CASE_BODY(where, how, within, kLargeCode, kUnknownOffsetFromStart) + CASE_BODY(where, how, within, kAnyOldSpace) #define FOUR_CASES(byte_code) \ case byte_code: \ @@ -925,14 +830,48 @@ void Deserializer::ReadChunk(Object** current, FOUR_CASES(byte_code + 8) \ FOUR_CASES(byte_code + 12) +#define COMMON_RAW_LENGTHS(f) \ + f(1) \ + f(2) \ + f(3) \ + f(4) \ + f(5) \ + f(6) \ + f(7) \ + f(8) \ + f(9) \ + f(10) \ + f(11) \ + f(12) \ + f(13) \ + f(14) \ + f(15) \ + f(16) \ + f(17) \ + f(18) \ + f(19) \ + f(20) \ + f(21) \ + f(22) \ + f(23) \ + f(24) \ + f(25) \ + f(26) \ + f(27) \ + f(28) \ + f(29) \ + f(30) \ + f(31) + // We generate 15 cases and bodies that process special tags that combine // the raw data tag and the length into one byte. -#define RAW_CASE(index, size) \ - case kRawData + index: { \ - byte* raw_data_out = reinterpret_cast<byte*>(current); \ - source_->CopyRaw(raw_data_out, size); \ - current = reinterpret_cast<Object**>(raw_data_out + size); \ - break; \ +#define RAW_CASE(index) \ + case kRawData + index: { \ + byte* raw_data_out = reinterpret_cast<byte*>(current); \ + source_->CopyRaw(raw_data_out, index * kPointerSize); \ + current = \ + reinterpret_cast<Object**>(raw_data_out + index * kPointerSize); \ + break; \ } COMMON_RAW_LENGTHS(RAW_CASE) #undef RAW_CASE @@ -943,12 +882,11 @@ void Deserializer::ReadChunk(Object** current, int size = source_->GetInt(); byte* raw_data_out = reinterpret_cast<byte*>(current); source_->CopyRaw(raw_data_out, size); - current = reinterpret_cast<Object**>(raw_data_out + size); break; } - SIXTEEN_CASES(kRootArrayLowConstants) - SIXTEEN_CASES(kRootArrayHighConstants) { + SIXTEEN_CASES(kRootArrayConstants + kNoSkipDistance) + SIXTEEN_CASES(kRootArrayConstants + kNoSkipDistance + 16) { int root_id = RootArrayConstantFromByteCode(data); Object* object = isolate->heap()->roots_array_start()[root_id]; ASSERT(!isolate->heap()->InNewSpace(object)); @@ -956,6 +894,18 @@ void Deserializer::ReadChunk(Object** current, break; } + SIXTEEN_CASES(kRootArrayConstants + kHasSkipDistance) + SIXTEEN_CASES(kRootArrayConstants + kHasSkipDistance + 16) { + int root_id = RootArrayConstantFromByteCode(data); + int skip = source_->GetInt(); + current = reinterpret_cast<Object**>( + reinterpret_cast<intptr_t>(current) + skip); + Object* object = isolate->heap()->roots_array_start()[root_id]; + ASSERT(!isolate->heap()->InNewSpace(object)); + *current++ = object; + break; + } + case kRepeat: { int repeats = source_->GetInt(); Object* object = current[-1]; @@ -967,10 +917,11 @@ void Deserializer::ReadChunk(Object** current, STATIC_ASSERT(kRootArrayNumberOfConstantEncodings == Heap::kOldSpaceRoots); - STATIC_ASSERT(kMaxRepeats == 12); - FOUR_CASES(kConstantRepeat) - FOUR_CASES(kConstantRepeat + 4) - FOUR_CASES(kConstantRepeat + 8) { + STATIC_ASSERT(kMaxRepeats == 13); + case kConstantRepeat: + FOUR_CASES(kConstantRepeat + 1) + FOUR_CASES(kConstantRepeat + 5) + FOUR_CASES(kConstantRepeat + 9) { int repeats = RepeatsForCode(data); Object* object = current[-1]; ASSERT(!isolate->heap()->InNewSpace(object)); @@ -981,98 +932,80 @@ void Deserializer::ReadChunk(Object** current, // Deserialize a new object and write a pointer to it to the current // object. - ONE_PER_SPACE(kNewObject, kPlain, kStartOfObject) - // Support for direct instruction pointers in functions - ONE_PER_CODE_SPACE(kNewObject, kPlain, kFirstInstruction) + ALL_SPACES(kNewObject, kPlain, kStartOfObject) + // Support for direct instruction pointers in functions. It's an inner + // pointer because it points at the entry point, not at the start of the + // code object. + CASE_STATEMENT(kNewObject, kPlain, kInnerPointer, CODE_SPACE) + CASE_BODY(kNewObject, kPlain, kInnerPointer, CODE_SPACE) // Deserialize a new code object and write a pointer to its first // instruction to the current code object. - ONE_PER_SPACE(kNewObject, kFromCode, kFirstInstruction) + ALL_SPACES(kNewObject, kFromCode, kInnerPointer) // Find a recently deserialized object using its offset from the current // allocation point and write a pointer to it to the current object. ALL_SPACES(kBackref, kPlain, kStartOfObject) + ALL_SPACES(kBackrefWithSkip, kPlain, kStartOfObject) #if V8_TARGET_ARCH_MIPS // Deserialize a new object from pointer found in code and write // a pointer to it to the current object. Required only for MIPS, and // omitted on the other architectures because it is fully unrolled and // would cause bloat. - ONE_PER_SPACE(kNewObject, kFromCode, kStartOfObject) + ALL_SPACES(kNewObject, kFromCode, kStartOfObject) // Find a recently deserialized code object using its offset from the // current allocation point and write a pointer to it to the current // object. Required only for MIPS. ALL_SPACES(kBackref, kFromCode, kStartOfObject) - // Find an already deserialized code object using its offset from - // the start and write a pointer to it to the current object. - // Required only for MIPS. - ALL_SPACES(kFromStart, kFromCode, kStartOfObject) + ALL_SPACES(kBackrefWithSkip, kFromCode, kStartOfObject) #endif // Find a recently deserialized code object using its offset from the // current allocation point and write a pointer to its first instruction // to the current code object or the instruction pointer in a function // object. - ALL_SPACES(kBackref, kFromCode, kFirstInstruction) - ALL_SPACES(kBackref, kPlain, kFirstInstruction) - // Find an already deserialized object using its offset from the start - // and write a pointer to it to the current object. - ALL_SPACES(kFromStart, kPlain, kStartOfObject) - ALL_SPACES(kFromStart, kPlain, kFirstInstruction) - // Find an already deserialized code object using its offset from the - // start and write a pointer to its first instruction to the current code - // object. - ALL_SPACES(kFromStart, kFromCode, kFirstInstruction) + ALL_SPACES(kBackref, kFromCode, kInnerPointer) + ALL_SPACES(kBackrefWithSkip, kFromCode, kInnerPointer) + ALL_SPACES(kBackref, kPlain, kInnerPointer) + ALL_SPACES(kBackrefWithSkip, kPlain, kInnerPointer) // Find an object in the roots array and write a pointer to it to the // current object. CASE_STATEMENT(kRootArray, kPlain, kStartOfObject, 0) - CASE_BODY(kRootArray, kPlain, kStartOfObject, 0, kUnknownOffsetFromStart) + CASE_BODY(kRootArray, kPlain, kStartOfObject, 0) // Find an object in the partial snapshots cache and write a pointer to it // to the current object. CASE_STATEMENT(kPartialSnapshotCache, kPlain, kStartOfObject, 0) CASE_BODY(kPartialSnapshotCache, kPlain, kStartOfObject, - 0, - kUnknownOffsetFromStart) + 0) // Find an code entry in the partial snapshots cache and // write a pointer to it to the current object. - CASE_STATEMENT(kPartialSnapshotCache, kPlain, kFirstInstruction, 0) + CASE_STATEMENT(kPartialSnapshotCache, kPlain, kInnerPointer, 0) CASE_BODY(kPartialSnapshotCache, kPlain, - kFirstInstruction, - 0, - kUnknownOffsetFromStart) + kInnerPointer, + 0) // Find an external reference and write a pointer to it to the current // object. CASE_STATEMENT(kExternalReference, kPlain, kStartOfObject, 0) CASE_BODY(kExternalReference, kPlain, kStartOfObject, - 0, - kUnknownOffsetFromStart) + 0) // Find an external reference and write a pointer to it in the current // code object. CASE_STATEMENT(kExternalReference, kFromCode, kStartOfObject, 0) CASE_BODY(kExternalReference, kFromCode, kStartOfObject, - 0, - kUnknownOffsetFromStart) + 0) #undef CASE_STATEMENT #undef CASE_BODY -#undef ONE_PER_SPACE #undef ALL_SPACES -#undef ASSIGN_DEST_SPACE - - case kNewPage: { - int space = source_->Get(); - pages_[space].Add(last_object_address_); - if (space == CODE_SPACE) { - CPU::FlushICache(last_object_address_, Page::kPageSize); - } - break; - } case kSkip: { - current++; + int size = source_->GetInt(); + current = reinterpret_cast<Object**>( + reinterpret_cast<intptr_t>(current) + size); break; } @@ -1097,18 +1030,20 @@ void Deserializer::ReadChunk(Object** current, UNREACHABLE(); } } - ASSERT_EQ(current, limit); + ASSERT_EQ(limit, current); } 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 >= static_cast<uintptr_t>(1u) << shift) { - Put((static_cast<int>((integer >> shift)) & 0x7f) | 0x80, "IntPart"); - } - } - PutSection(static_cast<int>(integer & 0x7f), "IntLastPart"); + ASSERT(integer < 1 << 22); + integer <<= 2; + int bytes = 1; + if (integer > 0xff) bytes = 2; + if (integer > 0xffff) bytes = 3; + integer |= bytes; + Put(static_cast<int>(integer & 0xff), "IntPart1"); + if (bytes > 1) Put(static_cast<int>((integer >> 8) & 0xff), "IntPart2"); + if (bytes > 2) Put(static_cast<int>((integer >> 16) & 0xff), "IntPart3"); } @@ -1116,7 +1051,6 @@ Serializer::Serializer(SnapshotByteSink* sink) : sink_(sink), current_root_index_(0), external_reference_encoder_(new ExternalReferenceEncoder), - large_object_total_(0), root_index_wave_front_(0) { isolate_ = Isolate::Current(); // The serializer is meant to be used only to generate initial heap images @@ -1149,22 +1083,7 @@ void StartupSerializer::SerializeStrongReferences() { void PartialSerializer::Serialize(Object** object) { this->VisitPointer(object); - Isolate* isolate = Isolate::Current(); - - // After we have done the partial serialization the partial snapshot cache - // will contain some references needed to decode the partial snapshot. We - // fill it up with undefineds so it has a predictable length so the - // deserialization code doesn't need to know the length. - for (int index = isolate->serialize_partial_snapshot_cache_length(); - index < Isolate::kPartialSnapshotCacheCapacity; - index++) { - isolate->serialize_partial_snapshot_cache()[index] = - isolate->heap()->undefined_value(); - startup_serializer_->VisitPointer( - &isolate->serialize_partial_snapshot_cache()[index]); - } - isolate->set_serialize_partial_snapshot_cache_length( - Isolate::kPartialSnapshotCacheCapacity); + Pad(); } @@ -1179,14 +1098,14 @@ void Serializer::VisitPointers(Object** start, Object** end) { if (reinterpret_cast<Address>(current) == isolate->heap()->store_buffer()->TopAddress()) { sink_->Put(kSkip, "Skip"); + sink_->PutInt(kPointerSize, "SkipOneWord"); } else if ((*current)->IsSmi()) { - sink_->Put(kRawData, "RawData"); - sink_->PutInt(kPointerSize, "length"); + sink_->Put(kRawData + 1, "Smi"); for (int i = 0; i < kPointerSize; i++) { sink_->Put(reinterpret_cast<byte*>(current)[i], "Byte"); } } else { - SerializeObject(*current, kPlain, kStartOfObject); + SerializeObject(*current, kPlain, kStartOfObject, 0); } } } @@ -1194,26 +1113,29 @@ void Serializer::VisitPointers(Object** start, Object** end) { // This ensures that the partial snapshot cache keeps things alive during GC and // tracks their movement. When it is called during serialization of the startup -// snapshot the partial snapshot is empty, so nothing happens. When the partial -// (context) snapshot is created, this array is populated with the pointers that -// the partial snapshot will need. As that happens we emit serialized objects to -// the startup snapshot that correspond to the elements of this cache array. On -// deserialization we therefore need to visit the cache array. This fills it up -// with pointers to deserialized objects. +// snapshot nothing happens. When the partial (context) snapshot is created, +// this array is populated with the pointers that the partial snapshot will +// need. As that happens we emit serialized objects to the startup snapshot +// that correspond to the elements of this cache array. On deserialization we +// therefore need to visit the cache array. This fills it up with pointers to +// deserialized objects. void SerializerDeserializer::Iterate(ObjectVisitor* visitor) { + if (Serializer::enabled()) return; Isolate* isolate = Isolate::Current(); - visitor->VisitPointers( - isolate->serialize_partial_snapshot_cache(), - &isolate->serialize_partial_snapshot_cache()[ - isolate->serialize_partial_snapshot_cache_length()]); -} - - -// When deserializing we need to set the size of the snapshot cache. This means -// the root iteration code (above) will iterate over array elements, writing the -// references to deserialized objects in them. -void SerializerDeserializer::SetSnapshotCacheSize(int size) { - Isolate::Current()->set_serialize_partial_snapshot_cache_length(size); + for (int i = 0; ; i++) { + if (isolate->serialize_partial_snapshot_cache_length() <= i) { + // Extend the array ready to get a value from the visitor when + // deserializing. + isolate->PushToPartialSnapshotCache(Smi::FromInt(0)); + } + Object** cache = isolate->serialize_partial_snapshot_cache(); + visitor->VisitPointers(&cache[i], &cache[i + 1]); + // Sentinel is the undefined object, which is a root so it will not normally + // be found in the cache. + if (cache[i] == isolate->heap()->undefined_value()) { + break; + } + } } @@ -1231,14 +1153,11 @@ int PartialSerializer::PartialSnapshotCacheIndex(HeapObject* heap_object) { // then visit the pointer so that it becomes part of the startup snapshot // and we can refer to it from the partial snapshot. int length = isolate->serialize_partial_snapshot_cache_length(); - CHECK(length < Isolate::kPartialSnapshotCacheCapacity); - isolate->serialize_partial_snapshot_cache()[length] = heap_object; - startup_serializer_->VisitPointer( - &isolate->serialize_partial_snapshot_cache()[length]); + isolate->PushToPartialSnapshotCache(heap_object); + startup_serializer_->VisitPointer(reinterpret_cast<Object**>(&heap_object)); // We don't recurse from the startup snapshot generator into the partial // snapshot generator. - ASSERT(length == isolate->serialize_partial_snapshot_cache_length()); - isolate->set_serialize_partial_snapshot_cache_length(length + 1); + ASSERT(length == isolate->serialize_partial_snapshot_cache_length() - 1); return length; } @@ -1273,58 +1192,50 @@ void Serializer::SerializeReferenceToPreviousObject( int space, int address, HowToCode how_to_code, - WhereToPoint where_to_point) { + WhereToPoint where_to_point, + int skip) { int offset = CurrentAllocationAddress(space) - address; - bool from_start = true; - if (SpaceIsPaged(space)) { - // For paged space it is simple to encode back from current allocation if - // the object is on the same page as the current allocation pointer. - if ((CurrentAllocationAddress(space) >> kPageSizeBits) == - (address >> kPageSizeBits)) { - from_start = false; - address = offset; - } - } else if (space == NEW_SPACE) { - // For new space it is always simple to encode back from current allocation. - if (offset < address) { - from_start = false; - address = offset; - } - } - // 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)) address >>= kObjectAlignmentBits; - if (from_start) { - sink_->Put(kFromStart + how_to_code + where_to_point + space, "RefSer"); - sink_->PutInt(address, "address"); - } else { + // Shift out the bits that are always 0. + offset >>= kObjectAlignmentBits; + if (skip == 0) { sink_->Put(kBackref + how_to_code + where_to_point + space, "BackRefSer"); - sink_->PutInt(address, "address"); + } else { + sink_->Put(kBackrefWithSkip + how_to_code + where_to_point + space, + "BackRefSerWithSkip"); + sink_->PutInt(skip, "BackRefSkipDistance"); } + sink_->PutInt(offset, "offset"); } void StartupSerializer::SerializeObject( Object* o, HowToCode how_to_code, - WhereToPoint where_to_point) { + WhereToPoint where_to_point, + int skip) { CHECK(o->IsHeapObject()); HeapObject* heap_object = HeapObject::cast(o); int root_index; if ((root_index = RootIndex(heap_object, how_to_code)) != kInvalidRootIndex) { - PutRoot(root_index, heap_object, how_to_code, where_to_point); + PutRoot(root_index, heap_object, how_to_code, where_to_point, skip); return; } if (address_mapper_.IsMapped(heap_object)) { - int space = SpaceOfAlreadySerializedObject(heap_object); + int space = SpaceOfObject(heap_object); int address = address_mapper_.MappedTo(heap_object); SerializeReferenceToPreviousObject(space, address, how_to_code, - where_to_point); + where_to_point, + skip); } else { + if (skip != 0) { + sink_->Put(kSkip, "FlushPendingSkip"); + sink_->PutInt(skip, "SkipDistance"); + } + // Object has not yet been serialized. Serialize it here. ObjectSerializer object_serializer(this, heap_object, @@ -1337,32 +1248,41 @@ void StartupSerializer::SerializeObject( void StartupSerializer::SerializeWeakReferences() { - for (int i = Isolate::Current()->serialize_partial_snapshot_cache_length(); - i < Isolate::kPartialSnapshotCacheCapacity; - i++) { - sink_->Put(kRootArray + kPlain + kStartOfObject, "RootSerialization"); - sink_->PutInt(Heap::kUndefinedValueRootIndex, "root_index"); - } + // This phase comes right after the partial 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 + // add one entry with 'undefined' which is the sentinel that the deserializer + // uses to know it is done deserializing the array. + Isolate* isolate = Isolate::Current(); + Object* undefined = isolate->heap()->undefined_value(); + VisitPointer(&undefined); HEAP->IterateWeakRoots(this, VISIT_ALL); + Pad(); } void Serializer::PutRoot(int root_index, HeapObject* object, SerializerDeserializer::HowToCode how_to_code, - SerializerDeserializer::WhereToPoint where_to_point) { + SerializerDeserializer::WhereToPoint where_to_point, + int skip) { if (how_to_code == kPlain && where_to_point == kStartOfObject && root_index < kRootArrayNumberOfConstantEncodings && !HEAP->InNewSpace(object)) { - if (root_index < kRootArrayNumberOfLowConstantEncodings) { - sink_->Put(kRootArrayLowConstants + root_index, "RootLoConstant"); + if (skip == 0) { + sink_->Put(kRootArrayConstants + kNoSkipDistance + root_index, + "RootConstant"); } else { - sink_->Put(kRootArrayHighConstants + root_index - - kRootArrayNumberOfLowConstantEncodings, - "RootHiConstant"); + sink_->Put(kRootArrayConstants + kHasSkipDistance + root_index, + "RootConstant"); + sink_->PutInt(skip, "SkipInPutRoot"); } } else { + if (skip != 0) { + sink_->Put(kSkip, "SkipFromPutRoot"); + sink_->PutInt(skip, "SkipFromPutRootDistance"); + } sink_->Put(kRootArray + how_to_code + where_to_point, "RootSerialization"); sink_->PutInt(root_index, "root_index"); } @@ -1372,7 +1292,8 @@ void Serializer::PutRoot(int root_index, void PartialSerializer::SerializeObject( Object* o, HowToCode how_to_code, - WhereToPoint where_to_point) { + WhereToPoint where_to_point, + int skip) { CHECK(o->IsHeapObject()); HeapObject* heap_object = HeapObject::cast(o); @@ -1380,16 +1301,21 @@ void PartialSerializer::SerializeObject( // The code-caches link to context-specific code objects, which // the startup and context serializes cannot currently handle. ASSERT(Map::cast(heap_object)->code_cache() == - heap_object->GetHeap()->raw_unchecked_empty_fixed_array()); + heap_object->GetHeap()->empty_fixed_array()); } int root_index; if ((root_index = RootIndex(heap_object, how_to_code)) != kInvalidRootIndex) { - PutRoot(root_index, heap_object, how_to_code, where_to_point); + PutRoot(root_index, heap_object, how_to_code, where_to_point, skip); return; } if (ShouldBeInThePartialSnapshotCache(heap_object)) { + if (skip != 0) { + sink_->Put(kSkip, "SkipFromSerializeObject"); + sink_->PutInt(skip, "SkipDistanceFromSerializeObject"); + } + int cache_index = PartialSnapshotCacheIndex(heap_object); sink_->Put(kPartialSnapshotCache + how_to_code + where_to_point, "PartialSnapshotCache"); @@ -1406,13 +1332,18 @@ void PartialSerializer::SerializeObject( ASSERT(!heap_object->IsSymbol()); if (address_mapper_.IsMapped(heap_object)) { - int space = SpaceOfAlreadySerializedObject(heap_object); + int space = SpaceOfObject(heap_object); int address = address_mapper_.MappedTo(heap_object); SerializeReferenceToPreviousObject(space, address, how_to_code, - where_to_point); + where_to_point, + skip); } else { + if (skip != 0) { + sink_->Put(kSkip, "SkipFromSerializeObject"); + sink_->PutInt(skip, "SkipDistanceFromSerializeObject"); + } // Object has not yet been serialized. Serialize it here. ObjectSerializer serializer(this, heap_object, @@ -1436,16 +1367,11 @@ void Serializer::ObjectSerializer::Serialize() { SnapshotPositionEvent(object_->address(), sink_->Position())); // Mark this object as already serialized. - bool start_new_page; - int offset = serializer_->Allocate(space, size, &start_new_page); + int offset = serializer_->Allocate(space, size); serializer_->address_mapper()->AddMapping(object_, offset); - if (start_new_page) { - sink_->Put(kNewPage, "NewPage"); - sink_->PutSection(space, "NewPageSpace"); - } // Serialize the map (first word of the object). - serializer_->SerializeObject(object_->map(), kPlain, kStartOfObject); + serializer_->SerializeObject(object_->map(), kPlain, kStartOfObject, 0); // Serialize the rest of the object. CHECK_EQ(0, bytes_processed_so_far_); @@ -1486,7 +1412,8 @@ void Serializer::ObjectSerializer::VisitPointers(Object** start, sink_->Put(CodeForRepeats(repeat_count), "SerializeRepeats"); } } else { - serializer_->SerializeObject(current_contents, kPlain, kStartOfObject); + serializer_->SerializeObject( + current_contents, kPlain, kStartOfObject, 0); bytes_processed_so_far_ += kPointerSize; current++; } @@ -1498,9 +1425,10 @@ void Serializer::ObjectSerializer::VisitPointers(Object** start, void Serializer::ObjectSerializer::VisitEmbeddedPointer(RelocInfo* rinfo) { Object** current = rinfo->target_object_address(); - OutputRawData(rinfo->target_address_address()); + int skip = OutputRawData(rinfo->target_address_address(), + kCanReturnSkipInsteadOfSkipping); HowToCode representation = rinfo->IsCodedSpecially() ? kFromCode : kPlain; - serializer_->SerializeObject(*current, representation, kStartOfObject); + serializer_->SerializeObject(*current, representation, kStartOfObject, skip); bytes_processed_so_far_ += rinfo->target_address_size(); } @@ -1508,10 +1436,12 @@ void Serializer::ObjectSerializer::VisitEmbeddedPointer(RelocInfo* rinfo) { void Serializer::ObjectSerializer::VisitExternalReferences(Address* start, Address* end) { Address references_start = reinterpret_cast<Address>(start); - OutputRawData(references_start); + int skip = OutputRawData(references_start, kCanReturnSkipInsteadOfSkipping); for (Address* current = start; current < end; current++) { sink_->Put(kExternalReference + kPlain + kStartOfObject, "ExternalRef"); + sink_->PutInt(skip, "SkipB4ExternalRef"); + skip = 0; int reference_id = serializer_->EncodeExternalReference(*current); sink_->PutInt(reference_id, "reference id"); } @@ -1521,12 +1451,13 @@ void Serializer::ObjectSerializer::VisitExternalReferences(Address* start, void Serializer::ObjectSerializer::VisitExternalReference(RelocInfo* rinfo) { Address references_start = rinfo->target_address_address(); - OutputRawData(references_start); + int skip = OutputRawData(references_start, kCanReturnSkipInsteadOfSkipping); Address* current = rinfo->target_reference_address(); int representation = rinfo->IsCodedSpecially() ? kFromCode + kStartOfObject : kPlain + kStartOfObject; sink_->Put(kExternalReference + representation, "ExternalRef"); + sink_->PutInt(skip, "SkipB4ExternalRef"); int reference_id = serializer_->EncodeExternalReference(*current); sink_->PutInt(reference_id, "reference id"); bytes_processed_so_far_ += rinfo->target_address_size(); @@ -1535,7 +1466,7 @@ void Serializer::ObjectSerializer::VisitExternalReference(RelocInfo* rinfo) { void Serializer::ObjectSerializer::VisitRuntimeEntry(RelocInfo* rinfo) { Address target_start = rinfo->target_address_address(); - OutputRawData(target_start); + int skip = OutputRawData(target_start, kCanReturnSkipInsteadOfSkipping); Address target = rinfo->target_address(); uint32_t encoding = serializer_->EncodeExternalReference(target); CHECK(target == NULL ? encoding == 0 : encoding != 0); @@ -1547,6 +1478,7 @@ void Serializer::ObjectSerializer::VisitRuntimeEntry(RelocInfo* rinfo) { representation = kStartOfObject + kPlain; } sink_->Put(kExternalReference + representation, "ExternalReference"); + sink_->PutInt(skip, "SkipB4ExternalRef"); sink_->PutInt(encoding, "reference id"); bytes_processed_so_far_ += rinfo->target_address_size(); } @@ -1555,25 +1487,27 @@ void Serializer::ObjectSerializer::VisitRuntimeEntry(RelocInfo* rinfo) { void Serializer::ObjectSerializer::VisitCodeTarget(RelocInfo* rinfo) { CHECK(RelocInfo::IsCodeTarget(rinfo->rmode())); Address target_start = rinfo->target_address_address(); - OutputRawData(target_start); + int skip = OutputRawData(target_start, kCanReturnSkipInsteadOfSkipping); Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); - serializer_->SerializeObject(target, kFromCode, kFirstInstruction); + serializer_->SerializeObject(target, kFromCode, kInnerPointer, skip); bytes_processed_so_far_ += rinfo->target_address_size(); } void Serializer::ObjectSerializer::VisitCodeEntry(Address entry_address) { Code* target = Code::cast(Code::GetObjectFromEntryAddress(entry_address)); - OutputRawData(entry_address); - serializer_->SerializeObject(target, kPlain, kFirstInstruction); + int skip = OutputRawData(entry_address, kCanReturnSkipInsteadOfSkipping); + serializer_->SerializeObject(target, kPlain, kInnerPointer, skip); bytes_processed_so_far_ += kPointerSize; } void Serializer::ObjectSerializer::VisitGlobalPropertyCell(RelocInfo* rinfo) { - // We shouldn't have any global property cell references in code - // objects in the snapshot. - UNREACHABLE(); + ASSERT(rinfo->rmode() == RelocInfo::GLOBAL_PROPERTY_CELL); + JSGlobalPropertyCell* cell = + JSGlobalPropertyCell::cast(rinfo->target_cell()); + int skip = OutputRawData(rinfo->pc(), kCanReturnSkipInsteadOfSkipping); + serializer_->SerializeObject(cell, kPlain, kInnerPointer, skip); } @@ -1601,59 +1535,58 @@ void Serializer::ObjectSerializer::VisitExternalAsciiString( } -void Serializer::ObjectSerializer::OutputRawData(Address up_to) { +int Serializer::ObjectSerializer::OutputRawData( + Address up_to, Serializer::ObjectSerializer::ReturnSkip return_skip) { Address object_start = object_->address(); + Address base = object_start + bytes_processed_so_far_; int up_to_offset = static_cast<int>(up_to - object_start); - int skipped = up_to_offset - bytes_processed_so_far_; + int to_skip = up_to_offset - bytes_processed_so_far_; + int bytes_to_output = to_skip; + bytes_processed_so_far_ += to_skip; // This assert will fail if the reloc info gives us the target_address_address // locations in a non-ascending order. Luckily that doesn't happen. - ASSERT(skipped >= 0); - if (skipped != 0) { - Address base = object_start + bytes_processed_so_far_; -#define RAW_CASE(index, length) \ - if (skipped == length) { \ + ASSERT(to_skip >= 0); + bool outputting_code = false; + if (to_skip != 0 && code_object_ && !code_has_been_output_) { + // Output the code all at once and fix later. + bytes_to_output = object_->Size() + to_skip - bytes_processed_so_far_; + outputting_code = true; + code_has_been_output_ = true; + } + if (bytes_to_output != 0 && + (!code_object_ || outputting_code)) { +#define RAW_CASE(index) \ + if (!outputting_code && bytes_to_output == index * kPointerSize && \ + index * kPointerSize == to_skip) { \ sink_->PutSection(kRawData + index, "RawDataFixed"); \ + to_skip = 0; /* This insn already skips. */ \ } else /* NOLINT */ COMMON_RAW_LENGTHS(RAW_CASE) #undef RAW_CASE { /* NOLINT */ + // We always end up here if we are outputting the code of a code object. sink_->Put(kRawData, "RawData"); - sink_->PutInt(skipped, "length"); + sink_->PutInt(bytes_to_output, "length"); } - for (int i = 0; i < skipped; i++) { + for (int i = 0; i < bytes_to_output; i++) { unsigned int data = base[i]; sink_->PutSection(data, "Byte"); } - bytes_processed_so_far_ += skipped; } -} - - -int Serializer::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; - } + if (to_skip != 0 && return_skip == kIgnoringReturn) { + sink_->Put(kSkip, "Skip"); + sink_->PutInt(to_skip, "SkipDistance"); + to_skip = 0; } - UNREACHABLE(); - return 0; + return to_skip; } -int Serializer::SpaceOfAlreadySerializedObject(HeapObject* object) { +int Serializer::SpaceOfObject(HeapObject* object) { for (int i = FIRST_SPACE; i <= LAST_SPACE; i++) { AllocationSpace s = static_cast<AllocationSpace>(i); if (HEAP->InSpace(object, s)) { + ASSERT(i < kNumberOfSpaces); return i; } } @@ -1662,34 +1595,8 @@ int Serializer::SpaceOfAlreadySerializedObject(HeapObject* object) { } -int Serializer::Allocate(int space, int size, bool* new_page) { +int Serializer::Allocate(int space, int size) { CHECK(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. - *new_page = true; - large_object_total_ += size; - return fullness_[LO_SPACE]++; - } - *new_page = false; - if (fullness_[space] == 0) { - *new_page = true; - } - 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. - CHECK(IsPowerOf2(Page::kPageSize)); - int used_in_this_page = (fullness_[space] & (Page::kPageSize - 1)); - CHECK(size <= SpaceAreaSize(space)); - if (used_in_this_page + size > SpaceAreaSize(space)) { - *new_page = true; - fullness_[space] = RoundUp(fullness_[space], Page::kPageSize); - } - } int allocation_address = fullness_[space]; fullness_[space] = allocation_address + size; return allocation_address; @@ -1705,4 +1612,21 @@ int Serializer::SpaceAreaSize(int space) { } +void Serializer::Pad() { + // The non-branching GetInt will read up to 3 bytes too far, so we need + // to pad the snapshot to make sure we don't read over the end. + for (unsigned i = 0; i < sizeof(int32_t) - 1; i++) { + sink_->Put(kNop, "Padding"); + } +} + + +bool SnapshotByteSource::AtEOF() { + if (0u + length_ - position_ > 2 * sizeof(uint32_t)) return false; + for (int x = position_; x < length_; x++) { + if (data_[x] != SerializerDeserializer::nop()) return false; + } + return true; +} + } } // namespace v8::internal |