2009-03-30 13:32:28 +00:00
|
|
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
2008-07-03 15:10:15 +00:00
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are
|
|
|
|
// met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above
|
|
|
|
// copyright notice, this list of conditions and the following
|
|
|
|
// disclaimer in the documentation and/or other materials provided
|
|
|
|
// with the distribution.
|
|
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
|
|
// contributors may be used to endorse or promote products derived
|
|
|
|
// from this software without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
#include "v8.h"
|
|
|
|
|
|
|
|
#include "accessors.h"
|
|
|
|
#include "api.h"
|
|
|
|
#include "bootstrapper.h"
|
|
|
|
#include "codegen-inl.h"
|
2008-09-11 10:51:52 +00:00
|
|
|
#include "compilation-cache.h"
|
2008-07-03 15:10:15 +00:00
|
|
|
#include "debug.h"
|
2009-09-16 13:41:24 +00:00
|
|
|
#include "heap-profiler.h"
|
2008-07-03 15:10:15 +00:00
|
|
|
#include "global-handles.h"
|
|
|
|
#include "mark-compact.h"
|
|
|
|
#include "natives.h"
|
|
|
|
#include "scanner.h"
|
|
|
|
#include "scopeinfo.h"
|
2009-10-21 15:03:34 +00:00
|
|
|
#include "snapshot.h"
|
2008-07-03 15:10:15 +00:00
|
|
|
#include "v8threads.h"
|
2009-08-31 12:40:37 +00:00
|
|
|
#if V8_TARGET_ARCH_ARM && V8_NATIVE_REGEXP
|
|
|
|
#include "regexp-macro-assembler.h"
|
2009-10-26 12:26:42 +00:00
|
|
|
#include "arm/regexp-macro-assembler-arm.h"
|
2009-08-31 12:40:37 +00:00
|
|
|
#endif
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-25 10:05:56 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
|
2009-03-19 18:50:00 +00:00
|
|
|
String* Heap::hidden_symbol_;
|
2009-07-08 19:12:58 +00:00
|
|
|
Object* Heap::roots_[Heap::kRootListLength];
|
|
|
|
|
2009-03-19 18:50:00 +00:00
|
|
|
|
2008-10-17 09:13:27 +00:00
|
|
|
NewSpace Heap::new_space_;
|
2008-09-05 12:34:09 +00:00
|
|
|
OldSpace* Heap::old_pointer_space_ = NULL;
|
|
|
|
OldSpace* Heap::old_data_space_ = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
OldSpace* Heap::code_space_ = NULL;
|
|
|
|
MapSpace* Heap::map_space_ = NULL;
|
2009-07-09 11:13:08 +00:00
|
|
|
CellSpace* Heap::cell_space_ = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
LargeObjectSpace* Heap::lo_space_ = NULL;
|
|
|
|
|
2008-10-29 09:27:59 +00:00
|
|
|
static const int kMinimumPromotionLimit = 2*MB;
|
|
|
|
static const int kMinimumAllocationLimit = 8*MB;
|
|
|
|
|
|
|
|
int Heap::old_gen_promotion_limit_ = kMinimumPromotionLimit;
|
|
|
|
int Heap::old_gen_allocation_limit_ = kMinimumAllocationLimit;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
int Heap::old_gen_exhausted_ = false;
|
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
int Heap::amount_of_external_allocated_memory_ = 0;
|
|
|
|
int Heap::amount_of_external_allocated_memory_at_last_global_gc_ = 0;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// semispace_size_ should be a power of 2 and old_generation_size_ should be
|
|
|
|
// a multiple of Page::kPageSize.
|
2009-07-14 22:38:06 +00:00
|
|
|
#if defined(ANDROID)
|
2010-01-26 10:03:44 +00:00
|
|
|
int Heap::max_semispace_size_ = 2*MB;
|
|
|
|
int Heap::max_old_generation_size_ = 192*MB;
|
2009-06-15 11:44:04 +00:00
|
|
|
int Heap::initial_semispace_size_ = 128*KB;
|
2009-10-05 11:16:25 +00:00
|
|
|
size_t Heap::code_range_size_ = 0;
|
2009-08-20 08:08:18 +00:00
|
|
|
#elif defined(V8_TARGET_ARCH_X64)
|
2009-10-21 15:03:34 +00:00
|
|
|
int Heap::max_semispace_size_ = 16*MB;
|
|
|
|
int Heap::max_old_generation_size_ = 1*GB;
|
2009-08-18 11:26:14 +00:00
|
|
|
int Heap::initial_semispace_size_ = 1*MB;
|
2009-10-07 12:47:49 +00:00
|
|
|
size_t Heap::code_range_size_ = 512*MB;
|
2009-06-15 11:44:04 +00:00
|
|
|
#else
|
2009-10-21 15:03:34 +00:00
|
|
|
int Heap::max_semispace_size_ = 8*MB;
|
|
|
|
int Heap::max_old_generation_size_ = 512*MB;
|
2009-06-12 11:11:04 +00:00
|
|
|
int Heap::initial_semispace_size_ = 512*KB;
|
2009-10-05 11:16:25 +00:00
|
|
|
size_t Heap::code_range_size_ = 0;
|
2009-06-15 11:44:04 +00:00
|
|
|
#endif
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-10-21 15:03:34 +00:00
|
|
|
// The snapshot semispace size will be the default semispace size if
|
|
|
|
// snapshotting is used and will be the requested semispace size as
|
|
|
|
// set up by ConfigureHeap otherwise.
|
|
|
|
int Heap::reserved_semispace_size_ = Heap::max_semispace_size_;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
GCCallback Heap::global_gc_prologue_callback_ = NULL;
|
|
|
|
GCCallback Heap::global_gc_epilogue_callback_ = NULL;
|
|
|
|
|
|
|
|
// Variables set based on semispace_size_ and old_generation_size_ in
|
|
|
|
// ConfigureHeap.
|
2009-10-21 15:03:34 +00:00
|
|
|
|
|
|
|
// Will be 4 * reserved_semispace_size_ to ensure that young
|
|
|
|
// generation can be aligned to its size.
|
2009-06-12 11:11:04 +00:00
|
|
|
int Heap::survived_since_last_expansion_ = 0;
|
2009-07-22 10:01:25 +00:00
|
|
|
int Heap::external_allocation_limit_ = 0;
|
2009-06-12 11:11:04 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Heap::HeapState Heap::gc_state_ = NOT_IN_GC;
|
|
|
|
|
|
|
|
int Heap::mc_count_ = 0;
|
|
|
|
int Heap::gc_count_ = 0;
|
|
|
|
|
2008-10-30 09:15:58 +00:00
|
|
|
int Heap::always_allocate_scope_depth_ = 0;
|
2009-10-27 11:54:01 +00:00
|
|
|
int Heap::linear_allocation_scope_depth_ = 0;
|
2009-02-26 12:40:50 +00:00
|
|
|
bool Heap::context_disposed_pending_ = false;
|
2008-10-30 09:15:58 +00:00
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
bool Heap::allocation_allowed_ = true;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
int Heap::allocation_timeout_ = 0;
|
|
|
|
bool Heap::disallow_allocation_failure_ = false;
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
int Heap::Capacity() {
|
|
|
|
if (!HasBeenSetup()) return 0;
|
|
|
|
|
2008-10-17 09:13:27 +00:00
|
|
|
return new_space_.Capacity() +
|
2008-09-05 12:34:09 +00:00
|
|
|
old_pointer_space_->Capacity() +
|
|
|
|
old_data_space_->Capacity() +
|
2008-07-03 15:10:15 +00:00
|
|
|
code_space_->Capacity() +
|
2009-07-09 11:13:08 +00:00
|
|
|
map_space_->Capacity() +
|
|
|
|
cell_space_->Capacity();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-20 07:51:49 +00:00
|
|
|
int Heap::CommittedMemory() {
|
|
|
|
if (!HasBeenSetup()) return 0;
|
|
|
|
|
|
|
|
return new_space_.CommittedMemory() +
|
|
|
|
old_pointer_space_->CommittedMemory() +
|
|
|
|
old_data_space_->CommittedMemory() +
|
|
|
|
code_space_->CommittedMemory() +
|
|
|
|
map_space_->CommittedMemory() +
|
|
|
|
cell_space_->CommittedMemory() +
|
|
|
|
lo_space_->Size();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
int Heap::Available() {
|
|
|
|
if (!HasBeenSetup()) return 0;
|
|
|
|
|
2008-10-17 09:13:27 +00:00
|
|
|
return new_space_.Available() +
|
2008-09-05 12:34:09 +00:00
|
|
|
old_pointer_space_->Available() +
|
|
|
|
old_data_space_->Available() +
|
2008-07-03 15:10:15 +00:00
|
|
|
code_space_->Available() +
|
2009-07-09 11:13:08 +00:00
|
|
|
map_space_->Available() +
|
|
|
|
cell_space_->Available();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Heap::HasBeenSetup() {
|
2008-10-17 09:13:27 +00:00
|
|
|
return old_pointer_space_ != NULL &&
|
2008-09-05 12:34:09 +00:00
|
|
|
old_data_space_ != NULL &&
|
|
|
|
code_space_ != NULL &&
|
|
|
|
map_space_ != NULL &&
|
2009-07-09 11:13:08 +00:00
|
|
|
cell_space_ != NULL &&
|
2008-09-05 12:34:09 +00:00
|
|
|
lo_space_ != NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GarbageCollector Heap::SelectGarbageCollector(AllocationSpace space) {
|
|
|
|
// Is global GC requested?
|
|
|
|
if (space != NEW_SPACE || FLAG_gc_global) {
|
|
|
|
Counters::gc_compactor_caused_by_request.Increment();
|
|
|
|
return MARK_COMPACTOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is enough data promoted to justify a global GC?
|
2008-10-29 09:27:59 +00:00
|
|
|
if (OldGenerationPromotionLimitReached()) {
|
2008-07-03 15:10:15 +00:00
|
|
|
Counters::gc_compactor_caused_by_promoted_data.Increment();
|
|
|
|
return MARK_COMPACTOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Have allocation in OLD and LO failed?
|
|
|
|
if (old_gen_exhausted_) {
|
|
|
|
Counters::gc_compactor_caused_by_oldspace_exhaustion.Increment();
|
|
|
|
return MARK_COMPACTOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is there enough space left in OLD to guarantee that a scavenge can
|
|
|
|
// succeed?
|
|
|
|
//
|
2008-09-05 12:34:09 +00:00
|
|
|
// Note that MemoryAllocator->MaxAvailable() undercounts the memory available
|
2008-07-03 15:10:15 +00:00
|
|
|
// for object promotion. It counts only the bytes that the memory
|
|
|
|
// allocator has not yet allocated from the OS and assigned to any space,
|
|
|
|
// and does not count available bytes already in the old space or code
|
|
|
|
// space. Undercounting is safe---we may get an unrequested full GC when
|
|
|
|
// a scavenge would have succeeded.
|
2008-10-17 09:13:27 +00:00
|
|
|
if (MemoryAllocator::MaxAvailable() <= new_space_.Size()) {
|
2008-07-03 15:10:15 +00:00
|
|
|
Counters::gc_compactor_caused_by_oldspace_exhaustion.Increment();
|
|
|
|
return MARK_COMPACTOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default
|
|
|
|
return SCAVENGER;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO(1238405): Combine the infrastructure for --heap-stats and
|
|
|
|
// --log-gc to avoid the complicated preprocessor and flag testing.
|
|
|
|
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
void Heap::ReportStatisticsBeforeGC() {
|
|
|
|
// Heap::ReportHeapStatistics will also log NewSpace statistics when
|
|
|
|
// compiled with ENABLE_LOGGING_AND_PROFILING and --log-gc is set. The
|
|
|
|
// following logic is used to avoid double logging.
|
|
|
|
#if defined(DEBUG) && defined(ENABLE_LOGGING_AND_PROFILING)
|
2008-10-17 09:13:27 +00:00
|
|
|
if (FLAG_heap_stats || FLAG_log_gc) new_space_.CollectStatistics();
|
2008-07-03 15:10:15 +00:00
|
|
|
if (FLAG_heap_stats) {
|
|
|
|
ReportHeapStatistics("Before GC");
|
|
|
|
} else if (FLAG_log_gc) {
|
2008-10-17 09:13:27 +00:00
|
|
|
new_space_.ReportStatistics();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2008-10-17 09:13:27 +00:00
|
|
|
if (FLAG_heap_stats || FLAG_log_gc) new_space_.ClearHistograms();
|
2008-07-03 15:10:15 +00:00
|
|
|
#elif defined(DEBUG)
|
|
|
|
if (FLAG_heap_stats) {
|
2008-10-17 09:13:27 +00:00
|
|
|
new_space_.CollectStatistics();
|
2008-07-03 15:10:15 +00:00
|
|
|
ReportHeapStatistics("Before GC");
|
2008-10-17 09:13:27 +00:00
|
|
|
new_space_.ClearHistograms();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
#elif defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
if (FLAG_log_gc) {
|
2008-10-17 09:13:27 +00:00
|
|
|
new_space_.CollectStatistics();
|
|
|
|
new_space_.ReportStatistics();
|
|
|
|
new_space_.ClearHistograms();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-13 21:24:54 +00:00
|
|
|
#if defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
void Heap::PrintShortHeapStatistics() {
|
|
|
|
if (!FLAG_trace_gc_verbose) return;
|
|
|
|
PrintF("Memory allocator, used: %8d, available: %8d\n",
|
2009-10-20 07:51:49 +00:00
|
|
|
MemoryAllocator::Size(),
|
|
|
|
MemoryAllocator::Available());
|
2009-07-13 21:24:54 +00:00
|
|
|
PrintF("New space, used: %8d, available: %8d\n",
|
2009-10-20 07:51:49 +00:00
|
|
|
Heap::new_space_.Size(),
|
|
|
|
new_space_.Available());
|
|
|
|
PrintF("Old pointers, used: %8d, available: %8d, waste: %8d\n",
|
|
|
|
old_pointer_space_->Size(),
|
|
|
|
old_pointer_space_->Available(),
|
|
|
|
old_pointer_space_->Waste());
|
|
|
|
PrintF("Old data space, used: %8d, available: %8d, waste: %8d\n",
|
|
|
|
old_data_space_->Size(),
|
|
|
|
old_data_space_->Available(),
|
|
|
|
old_data_space_->Waste());
|
|
|
|
PrintF("Code space, used: %8d, available: %8d, waste: %8d\n",
|
|
|
|
code_space_->Size(),
|
|
|
|
code_space_->Available(),
|
|
|
|
code_space_->Waste());
|
|
|
|
PrintF("Map space, used: %8d, available: %8d, waste: %8d\n",
|
|
|
|
map_space_->Size(),
|
|
|
|
map_space_->Available(),
|
|
|
|
map_space_->Waste());
|
|
|
|
PrintF("Cell space, used: %8d, available: %8d, waste: %8d\n",
|
|
|
|
cell_space_->Size(),
|
|
|
|
cell_space_->Available(),
|
|
|
|
cell_space_->Waste());
|
2009-07-13 21:24:54 +00:00
|
|
|
PrintF("Large object space, used: %8d, avaialble: %8d\n",
|
2009-10-20 07:51:49 +00:00
|
|
|
lo_space_->Size(),
|
|
|
|
lo_space_->Available());
|
2009-07-13 21:24:54 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// TODO(1238405): Combine the infrastructure for --heap-stats and
|
|
|
|
// --log-gc to avoid the complicated preprocessor and flag testing.
|
|
|
|
void Heap::ReportStatisticsAfterGC() {
|
|
|
|
// Similar to the before GC, we use some complicated logic to ensure that
|
|
|
|
// NewSpace statistics are logged exactly once when --log-gc is turned on.
|
|
|
|
#if defined(DEBUG) && defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
if (FLAG_heap_stats) {
|
2009-07-01 13:20:09 +00:00
|
|
|
new_space_.CollectStatistics();
|
2008-07-03 15:10:15 +00:00
|
|
|
ReportHeapStatistics("After GC");
|
|
|
|
} else if (FLAG_log_gc) {
|
2008-10-17 09:13:27 +00:00
|
|
|
new_space_.ReportStatistics();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
#elif defined(DEBUG)
|
|
|
|
if (FLAG_heap_stats) ReportHeapStatistics("After GC");
|
|
|
|
#elif defined(ENABLE_LOGGING_AND_PROFILING)
|
2008-10-17 09:13:27 +00:00
|
|
|
if (FLAG_log_gc) new_space_.ReportStatistics();
|
2008-07-03 15:10:15 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif // defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::GarbageCollectionPrologue() {
|
2009-09-01 09:03:58 +00:00
|
|
|
TranscendentalCache::Clear();
|
2008-07-30 08:49:36 +00:00
|
|
|
gc_count_++;
|
2008-07-03 15:10:15 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
ASSERT(allocation_allowed_ && gc_state_ == NOT_IN_GC);
|
|
|
|
allow_allocation(false);
|
|
|
|
|
|
|
|
if (FLAG_verify_heap) {
|
|
|
|
Verify();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FLAG_gc_verbose) Print();
|
|
|
|
|
|
|
|
if (FLAG_print_rset) {
|
2008-09-05 12:34:09 +00:00
|
|
|
// Not all spaces have remembered set bits that we care about.
|
|
|
|
old_pointer_space_->PrintRSet();
|
2008-07-03 15:10:15 +00:00
|
|
|
map_space_->PrintRSet();
|
|
|
|
lo_space_->PrintRSet();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
ReportStatisticsBeforeGC();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int Heap::SizeOfObjects() {
|
2008-09-05 12:34:09 +00:00
|
|
|
int total = 0;
|
|
|
|
AllSpaces spaces;
|
2010-01-25 22:53:18 +00:00
|
|
|
for (Space* space = spaces.next(); space != NULL; space = spaces.next()) {
|
2009-08-13 12:35:59 +00:00
|
|
|
total += space->Size();
|
|
|
|
}
|
2008-09-05 12:34:09 +00:00
|
|
|
return total;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Heap::GarbageCollectionEpilogue() {
|
|
|
|
#ifdef DEBUG
|
|
|
|
allow_allocation(true);
|
|
|
|
ZapFromSpace();
|
|
|
|
|
|
|
|
if (FLAG_verify_heap) {
|
|
|
|
Verify();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FLAG_print_global_handles) GlobalHandles::Print();
|
|
|
|
if (FLAG_print_handles) PrintHandles();
|
|
|
|
if (FLAG_gc_verbose) Print();
|
|
|
|
if (FLAG_code_stats) ReportCodeStatistics("After GC");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Counters::alive_after_last_gc.Set(SizeOfObjects());
|
|
|
|
|
2009-07-08 19:12:58 +00:00
|
|
|
Counters::symbol_table_capacity.Set(symbol_table()->Capacity());
|
|
|
|
Counters::number_of_symbols.Set(symbol_table()->NumberOfElements());
|
2008-07-03 15:10:15 +00:00
|
|
|
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
ReportStatisticsAfterGC();
|
|
|
|
#endif
|
2009-05-18 13:14:37 +00:00
|
|
|
#ifdef ENABLE_DEBUGGER_SUPPORT
|
|
|
|
Debug::AfterGarbageCollection();
|
|
|
|
#endif
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-20 00:07:19 +00:00
|
|
|
void Heap::CollectAllGarbage(bool force_compaction) {
|
2008-09-05 12:34:09 +00:00
|
|
|
// Since we are ignoring the return value, the exact choice of space does
|
|
|
|
// not matter, so long as we do not specify NEW_SPACE, which would not
|
|
|
|
// cause a full GC.
|
2009-08-20 00:07:19 +00:00
|
|
|
MarkCompactCollector::SetForceCompaction(force_compaction);
|
2008-09-05 12:34:09 +00:00
|
|
|
CollectGarbage(0, OLD_POINTER_SPACE);
|
2009-08-20 00:07:19 +00:00
|
|
|
MarkCompactCollector::SetForceCompaction(false);
|
2008-09-05 12:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-26 12:40:50 +00:00
|
|
|
void Heap::CollectAllGarbageIfContextDisposed() {
|
2009-03-05 07:44:13 +00:00
|
|
|
// If the garbage collector interface is exposed through the global
|
|
|
|
// gc() function, we avoid being clever about forcing GCs when
|
|
|
|
// contexts are disposed and leave it to the embedder to make
|
|
|
|
// informed decisions about when to force a collection.
|
|
|
|
if (!FLAG_expose_gc && context_disposed_pending_) {
|
2009-03-13 16:06:31 +00:00
|
|
|
HistogramTimerScope scope(&Counters::gc_context);
|
2009-08-25 02:54:39 +00:00
|
|
|
CollectAllGarbage(false);
|
2009-02-26 12:40:50 +00:00
|
|
|
}
|
2009-03-05 07:44:13 +00:00
|
|
|
context_disposed_pending_ = false;
|
2009-02-26 12:40:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::NotifyContextDisposed() {
|
|
|
|
context_disposed_pending_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
bool Heap::CollectGarbage(int requested_size, AllocationSpace space) {
|
|
|
|
// The VM is in the GC state until exiting this function.
|
|
|
|
VMState state(GC);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Reset the allocation timeout to the GC interval, but make sure to
|
|
|
|
// allow at least a few allocations after a collection. The reason
|
|
|
|
// for this is that we have a lot of allocation sequences and we
|
|
|
|
// assume that a garbage collection will allow the subsequent
|
|
|
|
// allocation attempts to go through.
|
|
|
|
allocation_timeout_ = Max(6, FLAG_gc_interval);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
{ GCTracer tracer;
|
|
|
|
GarbageCollectionPrologue();
|
2008-07-30 08:49:36 +00:00
|
|
|
// The GC count was incremented in the prologue. Tell the tracer about
|
|
|
|
// it.
|
|
|
|
tracer.set_gc_count(gc_count_);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
GarbageCollector collector = SelectGarbageCollector(space);
|
2008-07-30 08:49:36 +00:00
|
|
|
// Tell the tracer which collector we've selected.
|
2008-07-03 15:10:15 +00:00
|
|
|
tracer.set_collector(collector);
|
|
|
|
|
2009-03-13 16:06:31 +00:00
|
|
|
HistogramTimer* rate = (collector == SCAVENGER)
|
2008-07-03 15:10:15 +00:00
|
|
|
? &Counters::gc_scavenger
|
|
|
|
: &Counters::gc_compactor;
|
|
|
|
rate->Start();
|
2008-07-30 08:49:36 +00:00
|
|
|
PerformGarbageCollection(space, collector, &tracer);
|
2008-07-03 15:10:15 +00:00
|
|
|
rate->Stop();
|
|
|
|
|
|
|
|
GarbageCollectionEpilogue();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef ENABLE_LOGGING_AND_PROFILING
|
|
|
|
if (FLAG_log_gc) HeapProfiler::WriteSample();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (space) {
|
|
|
|
case NEW_SPACE:
|
2008-10-17 09:13:27 +00:00
|
|
|
return new_space_.Available() >= requested_size;
|
2008-09-05 12:34:09 +00:00
|
|
|
case OLD_POINTER_SPACE:
|
|
|
|
return old_pointer_space_->Available() >= requested_size;
|
|
|
|
case OLD_DATA_SPACE:
|
|
|
|
return old_data_space_->Available() >= requested_size;
|
2008-07-03 15:10:15 +00:00
|
|
|
case CODE_SPACE:
|
|
|
|
return code_space_->Available() >= requested_size;
|
|
|
|
case MAP_SPACE:
|
|
|
|
return map_space_->Available() >= requested_size;
|
2009-07-09 11:13:08 +00:00
|
|
|
case CELL_SPACE:
|
|
|
|
return cell_space_->Available() >= requested_size;
|
2008-07-03 15:10:15 +00:00
|
|
|
case LO_SPACE:
|
|
|
|
return lo_space_->Available() >= requested_size;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
void Heap::PerformScavenge() {
|
|
|
|
GCTracer tracer;
|
|
|
|
PerformGarbageCollection(NEW_SPACE, SCAVENGER, &tracer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-14 12:00:56 +00:00
|
|
|
#ifdef DEBUG
|
2009-04-14 14:01:00 +00:00
|
|
|
// Helper class for verifying the symbol table.
|
|
|
|
class SymbolTableVerifier : public ObjectVisitor {
|
|
|
|
public:
|
|
|
|
SymbolTableVerifier() { }
|
|
|
|
void VisitPointers(Object** start, Object** end) {
|
|
|
|
// Visit all HeapObject pointers in [start, end).
|
|
|
|
for (Object** p = start; p < end; p++) {
|
|
|
|
if ((*p)->IsHeapObject()) {
|
|
|
|
// Check that the symbol is actually a symbol.
|
|
|
|
ASSERT((*p)->IsNull() || (*p)->IsUndefined() || (*p)->IsSymbol());
|
2009-04-14 12:00:56 +00:00
|
|
|
}
|
|
|
|
}
|
2009-04-14 14:01:00 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif // DEBUG
|
2009-04-14 12:00:56 +00:00
|
|
|
|
2009-04-14 14:01:00 +00:00
|
|
|
|
|
|
|
static void VerifySymbolTable() {
|
|
|
|
#ifdef DEBUG
|
2009-04-14 12:00:56 +00:00
|
|
|
SymbolTableVerifier verifier;
|
2009-07-08 19:12:58 +00:00
|
|
|
Heap::symbol_table()->IterateElements(&verifier);
|
2009-04-14 12:00:56 +00:00
|
|
|
#endif // DEBUG
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-14 14:46:31 +00:00
|
|
|
void Heap::ReserveSpace(
|
|
|
|
int new_space_size,
|
|
|
|
int pointer_space_size,
|
|
|
|
int data_space_size,
|
|
|
|
int code_space_size,
|
|
|
|
int map_space_size,
|
|
|
|
int cell_space_size,
|
|
|
|
int large_object_size) {
|
|
|
|
NewSpace* new_space = Heap::new_space();
|
|
|
|
PagedSpace* old_pointer_space = Heap::old_pointer_space();
|
|
|
|
PagedSpace* old_data_space = Heap::old_data_space();
|
|
|
|
PagedSpace* code_space = Heap::code_space();
|
|
|
|
PagedSpace* map_space = Heap::map_space();
|
|
|
|
PagedSpace* cell_space = Heap::cell_space();
|
|
|
|
LargeObjectSpace* lo_space = Heap::lo_space();
|
|
|
|
bool gc_performed = true;
|
|
|
|
while (gc_performed) {
|
|
|
|
gc_performed = false;
|
|
|
|
if (!new_space->ReserveSpace(new_space_size)) {
|
|
|
|
Heap::CollectGarbage(new_space_size, NEW_SPACE);
|
|
|
|
gc_performed = true;
|
|
|
|
}
|
|
|
|
if (!old_pointer_space->ReserveSpace(pointer_space_size)) {
|
|
|
|
Heap::CollectGarbage(pointer_space_size, OLD_POINTER_SPACE);
|
|
|
|
gc_performed = true;
|
|
|
|
}
|
|
|
|
if (!(old_data_space->ReserveSpace(data_space_size))) {
|
|
|
|
Heap::CollectGarbage(data_space_size, OLD_DATA_SPACE);
|
|
|
|
gc_performed = true;
|
|
|
|
}
|
|
|
|
if (!(code_space->ReserveSpace(code_space_size))) {
|
|
|
|
Heap::CollectGarbage(code_space_size, CODE_SPACE);
|
|
|
|
gc_performed = true;
|
|
|
|
}
|
|
|
|
if (!(map_space->ReserveSpace(map_space_size))) {
|
|
|
|
Heap::CollectGarbage(map_space_size, MAP_SPACE);
|
|
|
|
gc_performed = true;
|
|
|
|
}
|
|
|
|
if (!(cell_space->ReserveSpace(cell_space_size))) {
|
|
|
|
Heap::CollectGarbage(cell_space_size, CELL_SPACE);
|
|
|
|
gc_performed = true;
|
|
|
|
}
|
|
|
|
// We add a slack-factor of 2 in order to have space for the remembered
|
|
|
|
// set and a series of large-object allocations that are only just larger
|
|
|
|
// than the page size.
|
|
|
|
large_object_size *= 2;
|
|
|
|
// The ReserveSpace method on the large object space checks how much
|
|
|
|
// we can expand the old generation. This includes expansion caused by
|
|
|
|
// allocation in the other spaces.
|
|
|
|
large_object_size += cell_space_size + map_space_size + code_space_size +
|
|
|
|
data_space_size + pointer_space_size;
|
|
|
|
if (!(lo_space->ReserveSpace(large_object_size))) {
|
|
|
|
Heap::CollectGarbage(large_object_size, LO_SPACE);
|
|
|
|
gc_performed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-13 12:03:42 +00:00
|
|
|
void Heap::EnsureFromSpaceIsCommitted() {
|
|
|
|
if (new_space_.CommitFromSpaceIfNeeded()) return;
|
|
|
|
|
|
|
|
// Committing memory to from space failed.
|
|
|
|
// Try shrinking and try again.
|
|
|
|
Shrink();
|
|
|
|
if (new_space_.CommitFromSpaceIfNeeded()) return;
|
|
|
|
|
|
|
|
// Committing memory to from space failed again.
|
|
|
|
// Memory is exhausted and we will die.
|
|
|
|
V8::FatalProcessOutOfMemory("Committing semi space failed.");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Heap::PerformGarbageCollection(AllocationSpace space,
|
2008-07-30 08:49:36 +00:00
|
|
|
GarbageCollector collector,
|
|
|
|
GCTracer* tracer) {
|
2009-04-14 12:00:56 +00:00
|
|
|
VerifySymbolTable();
|
2008-07-03 15:10:15 +00:00
|
|
|
if (collector == MARK_COMPACTOR && global_gc_prologue_callback_) {
|
|
|
|
ASSERT(!allocation_allowed_);
|
|
|
|
global_gc_prologue_callback_();
|
|
|
|
}
|
2009-08-13 12:03:42 +00:00
|
|
|
EnsureFromSpaceIsCommitted();
|
2008-07-03 15:10:15 +00:00
|
|
|
if (collector == MARK_COMPACTOR) {
|
2008-07-30 08:49:36 +00:00
|
|
|
MarkCompact(tracer);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-10-29 09:27:59 +00:00
|
|
|
int old_gen_size = PromotedSpaceSize();
|
|
|
|
old_gen_promotion_limit_ =
|
|
|
|
old_gen_size + Max(kMinimumPromotionLimit, old_gen_size / 3);
|
|
|
|
old_gen_allocation_limit_ =
|
2009-06-12 11:11:04 +00:00
|
|
|
old_gen_size + Max(kMinimumAllocationLimit, old_gen_size / 2);
|
2008-07-03 15:10:15 +00:00
|
|
|
old_gen_exhausted_ = false;
|
|
|
|
}
|
2009-08-26 12:51:43 +00:00
|
|
|
Scavenge();
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Counters::objs_since_last_young.Set(0);
|
|
|
|
|
2009-10-16 12:11:59 +00:00
|
|
|
if (collector == MARK_COMPACTOR) {
|
|
|
|
DisableAssertNoAllocation allow_allocation;
|
|
|
|
GlobalHandles::PostGarbageCollectionProcessing();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update relocatables.
|
|
|
|
Relocatable::PostGarbageCollectionProcessing();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
if (collector == MARK_COMPACTOR) {
|
|
|
|
// Register the amount of external allocated memory.
|
|
|
|
amount_of_external_allocated_memory_at_last_global_gc_ =
|
|
|
|
amount_of_external_allocated_memory_;
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
if (collector == MARK_COMPACTOR && global_gc_epilogue_callback_) {
|
|
|
|
ASSERT(!allocation_allowed_);
|
|
|
|
global_gc_epilogue_callback_();
|
|
|
|
}
|
2009-04-14 12:00:56 +00:00
|
|
|
VerifySymbolTable();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
void Heap::MarkCompact(GCTracer* tracer) {
|
2008-07-03 15:10:15 +00:00
|
|
|
gc_state_ = MARK_COMPACT;
|
|
|
|
mc_count_++;
|
2008-07-30 08:49:36 +00:00
|
|
|
tracer->set_full_gc_count(mc_count_);
|
2008-07-03 15:10:15 +00:00
|
|
|
LOG(ResourceEvent("markcompact", "begin"));
|
|
|
|
|
2009-02-25 16:52:15 +00:00
|
|
|
MarkCompactCollector::Prepare(tracer);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-02-25 16:52:15 +00:00
|
|
|
bool is_compacting = MarkCompactCollector::IsCompacting();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-02-25 16:52:15 +00:00
|
|
|
MarkCompactPrologue(is_compacting);
|
|
|
|
|
|
|
|
MarkCompactCollector::CollectGarbage();
|
|
|
|
|
|
|
|
MarkCompactEpilogue(is_compacting);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
LOG(ResourceEvent("markcompact", "end"));
|
|
|
|
|
|
|
|
gc_state_ = NOT_IN_GC;
|
|
|
|
|
|
|
|
Shrink();
|
|
|
|
|
|
|
|
Counters::objs_since_last_full.Set(0);
|
2009-02-26 12:40:50 +00:00
|
|
|
context_disposed_pending_ = false;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-25 16:52:15 +00:00
|
|
|
void Heap::MarkCompactPrologue(bool is_compacting) {
|
|
|
|
// At any old GC clear the keyed lookup cache to enable collection of unused
|
|
|
|
// maps.
|
2009-06-17 06:07:49 +00:00
|
|
|
KeyedLookupCache::Clear();
|
2009-06-22 08:09:57 +00:00
|
|
|
ContextSlotCache::Clear();
|
2009-06-22 14:29:35 +00:00
|
|
|
DescriptorLookupCache::Clear();
|
2009-02-25 16:52:15 +00:00
|
|
|
|
2008-09-11 10:51:52 +00:00
|
|
|
CompilationCache::MarkCompactPrologue();
|
2009-02-25 16:52:15 +00:00
|
|
|
|
|
|
|
Top::MarkCompactPrologue(is_compacting);
|
|
|
|
ThreadManager::MarkCompactPrologue(is_compacting);
|
2010-01-06 11:19:28 +00:00
|
|
|
|
|
|
|
if (is_compacting) FlushNumberStringCache();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-25 16:52:15 +00:00
|
|
|
void Heap::MarkCompactEpilogue(bool is_compacting) {
|
|
|
|
Top::MarkCompactEpilogue(is_compacting);
|
|
|
|
ThreadManager::MarkCompactEpilogue(is_compacting);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::FindCodeObject(Address a) {
|
|
|
|
Object* obj = code_space_->FindObject(a);
|
|
|
|
if (obj->IsFailure()) {
|
|
|
|
obj = lo_space_->FindObject(a);
|
|
|
|
}
|
2008-07-30 08:49:36 +00:00
|
|
|
ASSERT(!obj->IsFailure());
|
2008-07-03 15:10:15 +00:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Helper class for copying HeapObjects
|
2008-10-21 08:48:11 +00:00
|
|
|
class ScavengeVisitor: public ObjectVisitor {
|
2008-07-03 15:10:15 +00:00
|
|
|
public:
|
|
|
|
|
2008-10-21 08:48:11 +00:00
|
|
|
void VisitPointer(Object** p) { ScavengePointer(p); }
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
void VisitPointers(Object** start, Object** end) {
|
|
|
|
// Copy all HeapObject pointers in [start, end)
|
2008-10-21 08:48:11 +00:00
|
|
|
for (Object** p = start; p < end; p++) ScavengePointer(p);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2008-10-21 08:48:11 +00:00
|
|
|
void ScavengePointer(Object** p) {
|
|
|
|
Object* object = *p;
|
|
|
|
if (!Heap::InNewSpace(object)) return;
|
|
|
|
Heap::ScavengeObject(reinterpret_cast<HeapObject**>(p),
|
|
|
|
reinterpret_cast<HeapObject*>(object));
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2009-05-15 14:46:59 +00:00
|
|
|
// A queue of pointers and maps of to-be-promoted objects during a
|
|
|
|
// scavenge collection.
|
|
|
|
class PromotionQueue {
|
|
|
|
public:
|
|
|
|
void Initialize(Address start_address) {
|
|
|
|
front_ = rear_ = reinterpret_cast<HeapObject**>(start_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_empty() { return front_ <= rear_; }
|
|
|
|
|
|
|
|
void insert(HeapObject* object, Map* map) {
|
|
|
|
*(--rear_) = object;
|
|
|
|
*(--rear_) = map;
|
|
|
|
// Assert no overflow into live objects.
|
|
|
|
ASSERT(reinterpret_cast<Address>(rear_) >= Heap::new_space()->top());
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove(HeapObject** object, Map** map) {
|
|
|
|
*object = *(--front_);
|
|
|
|
*map = Map::cast(*(--front_));
|
|
|
|
// Assert no underflow.
|
|
|
|
ASSERT(front_ >= rear_);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// The front of the queue is higher in memory than the rear.
|
|
|
|
HeapObject** front_;
|
|
|
|
HeapObject** rear_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-10-21 08:48:11 +00:00
|
|
|
// Shared state read by the scavenge collector and set by ScavengeObject.
|
2009-05-15 15:02:03 +00:00
|
|
|
static PromotionQueue promotion_queue;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2008-09-05 12:34:09 +00:00
|
|
|
// Visitor class to verify pointers in code or data space do not point into
|
2008-07-03 15:10:15 +00:00
|
|
|
// new space.
|
2008-09-05 12:34:09 +00:00
|
|
|
class VerifyNonPointerSpacePointersVisitor: public ObjectVisitor {
|
2008-07-03 15:10:15 +00:00
|
|
|
public:
|
|
|
|
void VisitPointers(Object** start, Object**end) {
|
|
|
|
for (Object** current = start; current < end; current++) {
|
|
|
|
if ((*current)->IsHeapObject()) {
|
|
|
|
ASSERT(!Heap::InNewSpace(HeapObject::cast(*current)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2009-05-07 10:43:33 +00:00
|
|
|
|
2009-05-14 08:55:34 +00:00
|
|
|
|
|
|
|
static void VerifyNonPointerSpacePointers() {
|
|
|
|
// Verify that there are no pointers to new space in spaces where we
|
|
|
|
// do not expect them.
|
|
|
|
VerifyNonPointerSpacePointersVisitor v;
|
|
|
|
HeapObjectIterator code_it(Heap::code_space());
|
2010-01-25 22:53:18 +00:00
|
|
|
for (HeapObject* object = code_it.next();
|
|
|
|
object != NULL; object = code_it.next())
|
2009-09-21 10:35:47 +00:00
|
|
|
object->Iterate(&v);
|
2009-05-14 08:55:34 +00:00
|
|
|
|
|
|
|
HeapObjectIterator data_it(Heap::old_data_space());
|
2010-01-25 22:53:18 +00:00
|
|
|
for (HeapObject* object = data_it.next();
|
|
|
|
object != NULL; object = data_it.next())
|
|
|
|
object->Iterate(&v);
|
2009-05-14 08:55:34 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-07-09 13:28:22 +00:00
|
|
|
|
2009-05-14 08:55:34 +00:00
|
|
|
void Heap::Scavenge() {
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_enable_slow_asserts) VerifyNonPointerSpacePointers();
|
2008-07-03 15:10:15 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
gc_state_ = SCAVENGE;
|
|
|
|
|
|
|
|
// Implements Cheney's copying algorithm
|
|
|
|
LOG(ResourceEvent("scavenge", "begin"));
|
|
|
|
|
2009-06-22 14:29:35 +00:00
|
|
|
// Clear descriptor cache.
|
|
|
|
DescriptorLookupCache::Clear();
|
|
|
|
|
2009-06-12 11:11:04 +00:00
|
|
|
// Used for updating survived_since_last_expansion_ at function end.
|
|
|
|
int survived_watermark = PromotedSpaceSize();
|
|
|
|
|
2008-10-17 09:13:27 +00:00
|
|
|
if (new_space_.Capacity() < new_space_.MaximumCapacity() &&
|
2009-06-12 11:11:04 +00:00
|
|
|
survived_since_last_expansion_ > new_space_.Capacity()) {
|
2009-08-19 10:36:19 +00:00
|
|
|
// Grow the size of new space if there is room to grow and enough
|
2009-06-12 11:11:04 +00:00
|
|
|
// data has survived scavenge since the last expansion.
|
2009-08-19 10:36:19 +00:00
|
|
|
new_space_.Grow();
|
2009-06-12 11:11:04 +00:00
|
|
|
survived_since_last_expansion_ = 0;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Flip the semispaces. After flipping, to space is empty, from space has
|
|
|
|
// live objects.
|
2008-10-17 09:13:27 +00:00
|
|
|
new_space_.Flip();
|
|
|
|
new_space_.ResetAllocationInfo();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-14 08:55:34 +00:00
|
|
|
// We need to sweep newly copied objects which can be either in the
|
|
|
|
// to space or promoted to the old generation. For to-space
|
|
|
|
// objects, we treat the bottom of the to space as a queue. Newly
|
|
|
|
// copied and unswept objects lie between a 'front' mark and the
|
|
|
|
// allocation pointer.
|
2008-07-03 15:10:15 +00:00
|
|
|
//
|
2009-05-14 08:55:34 +00:00
|
|
|
// Promoted objects can go into various old-generation spaces, and
|
|
|
|
// can be allocated internally in the spaces (from the free list).
|
|
|
|
// We treat the top of the to space as a queue of addresses of
|
|
|
|
// promoted objects. The addresses of newly promoted and unswept
|
|
|
|
// objects lie between a 'front' mark and a 'rear' mark that is
|
|
|
|
// updated as a side effect of promoting an object.
|
|
|
|
//
|
|
|
|
// There is guaranteed to be enough room at the top of the to space
|
|
|
|
// for the addresses of promoted objects: every object promoted
|
|
|
|
// frees up its size in bytes from the top of the new space, and
|
|
|
|
// objects are at least one pointer in size.
|
|
|
|
Address new_space_front = new_space_.ToSpaceLow();
|
2009-05-15 15:02:03 +00:00
|
|
|
promotion_queue.Initialize(new_space_.ToSpaceHigh());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-10-21 08:48:11 +00:00
|
|
|
ScavengeVisitor scavenge_visitor;
|
2008-07-03 15:10:15 +00:00
|
|
|
// Copy roots.
|
2009-12-09 14:32:45 +00:00
|
|
|
IterateRoots(&scavenge_visitor, VISIT_ALL_IN_SCAVENGE);
|
2009-05-14 08:55:34 +00:00
|
|
|
|
|
|
|
// Copy objects reachable from the old generation. By definition,
|
|
|
|
// there are no intergenerational pointers in code or data spaces.
|
2008-10-21 08:48:11 +00:00
|
|
|
IterateRSet(old_pointer_space_, &ScavengePointer);
|
|
|
|
IterateRSet(map_space_, &ScavengePointer);
|
|
|
|
lo_space_->IterateRSet(&ScavengePointer);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-09 13:28:22 +00:00
|
|
|
// Copy objects reachable from cells by scavenging cell values directly.
|
|
|
|
HeapObjectIterator cell_iterator(cell_space_);
|
2010-01-25 22:53:18 +00:00
|
|
|
for (HeapObject* cell = cell_iterator.next();
|
|
|
|
cell != NULL; cell = cell_iterator.next()) {
|
2009-07-09 13:28:22 +00:00
|
|
|
if (cell->IsJSGlobalPropertyCell()) {
|
|
|
|
Address value_address =
|
|
|
|
reinterpret_cast<Address>(cell) +
|
|
|
|
(JSGlobalPropertyCell::kValueOffset - kHeapObjectTag);
|
|
|
|
scavenge_visitor.VisitPointer(reinterpret_cast<Object**>(value_address));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-09 14:32:45 +00:00
|
|
|
new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
|
|
|
|
|
|
|
|
ScavengeExternalStringTable();
|
|
|
|
ASSERT(new_space_front == new_space_.top());
|
|
|
|
|
|
|
|
// Set age mark.
|
|
|
|
new_space_.set_age_mark(new_space_.top());
|
|
|
|
|
|
|
|
// Update how much has survived scavenge.
|
|
|
|
survived_since_last_expansion_ +=
|
|
|
|
(PromotedSpaceSize() - survived_watermark) + new_space_.Size();
|
|
|
|
|
|
|
|
LOG(ResourceEvent("scavenge", "end"));
|
|
|
|
|
|
|
|
gc_state_ = NOT_IN_GC;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::ScavengeExternalStringTable() {
|
|
|
|
ExternalStringTable::Verify();
|
|
|
|
|
|
|
|
if (ExternalStringTable::new_space_strings_.is_empty()) return;
|
|
|
|
|
|
|
|
Object** start = &ExternalStringTable::new_space_strings_[0];
|
|
|
|
Object** end = start + ExternalStringTable::new_space_strings_.length();
|
|
|
|
Object** last = start;
|
|
|
|
|
|
|
|
for (Object** p = start; p < end; ++p) {
|
|
|
|
ASSERT(Heap::InFromSpace(*p));
|
|
|
|
MapWord first_word = HeapObject::cast(*p)->map_word();
|
|
|
|
|
|
|
|
if (!first_word.IsForwardingAddress()) {
|
|
|
|
// Unreachable external string can be finalized.
|
|
|
|
FinalizeExternalString(String::cast(*p));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// String is still reachable.
|
|
|
|
String* target = String::cast(first_word.ToForwardingAddress());
|
|
|
|
ASSERT(target->IsExternalString());
|
|
|
|
|
|
|
|
if (Heap::InNewSpace(target)) {
|
|
|
|
// String is still in new space. Update the table entry.
|
|
|
|
*last = target;
|
|
|
|
++last;
|
|
|
|
} else {
|
|
|
|
// String got promoted. Move it to the old string list.
|
|
|
|
ExternalStringTable::AddOldString(target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-07 13:17:18 +00:00
|
|
|
ASSERT(last <= end);
|
|
|
|
ExternalStringTable::ShrinkNewStrings(static_cast<int>(last - start));
|
2009-12-09 14:32:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Address Heap::DoScavenge(ObjectVisitor* scavenge_visitor,
|
|
|
|
Address new_space_front) {
|
2009-05-14 08:55:34 +00:00
|
|
|
do {
|
|
|
|
ASSERT(new_space_front <= new_space_.top());
|
|
|
|
|
|
|
|
// The addresses new_space_front and new_space_.top() define a
|
|
|
|
// queue of unprocessed copied objects. Process them until the
|
|
|
|
// queue is empty.
|
|
|
|
while (new_space_front < new_space_.top()) {
|
|
|
|
HeapObject* object = HeapObject::FromAddress(new_space_front);
|
2009-12-09 14:32:45 +00:00
|
|
|
object->Iterate(scavenge_visitor);
|
2009-05-14 08:55:34 +00:00
|
|
|
new_space_front += object->Size();
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-15 15:02:03 +00:00
|
|
|
// Promote and process all the to-be-promoted objects.
|
|
|
|
while (!promotion_queue.is_empty()) {
|
|
|
|
HeapObject* source;
|
|
|
|
Map* map;
|
|
|
|
promotion_queue.remove(&source, &map);
|
|
|
|
// Copy the from-space object to its new location (given by the
|
|
|
|
// forwarding address) and fix its map.
|
|
|
|
HeapObject* target = source->map_word().ToForwardingAddress();
|
|
|
|
CopyBlock(reinterpret_cast<Object**>(target->address()),
|
|
|
|
reinterpret_cast<Object**>(source->address()),
|
|
|
|
source->SizeFromMap(map));
|
|
|
|
target->set_map(map);
|
|
|
|
|
|
|
|
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
// Update NewSpace stats if necessary.
|
|
|
|
RecordCopiedObject(target);
|
|
|
|
#endif
|
|
|
|
// Visit the newly copied object for pointers to new space.
|
2009-12-09 14:32:45 +00:00
|
|
|
target->Iterate(scavenge_visitor);
|
2009-05-15 15:02:03 +00:00
|
|
|
UpdateRSet(target);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2009-05-14 08:55:34 +00:00
|
|
|
// Take another spin if there are now unswept objects in new space
|
|
|
|
// (there are currently no more unswept promoted objects).
|
|
|
|
} while (new_space_front < new_space_.top());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-12-09 14:32:45 +00:00
|
|
|
return new_space_front;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::ClearRSetRange(Address start, int size_in_bytes) {
|
|
|
|
uint32_t start_bit;
|
|
|
|
Address start_word_address =
|
|
|
|
Page::ComputeRSetBitPosition(start, 0, &start_bit);
|
|
|
|
uint32_t end_bit;
|
|
|
|
Address end_word_address =
|
|
|
|
Page::ComputeRSetBitPosition(start + size_in_bytes - kIntSize,
|
|
|
|
0,
|
|
|
|
&end_bit);
|
|
|
|
|
|
|
|
// We want to clear the bits in the starting word starting with the
|
|
|
|
// first bit, and in the ending word up to and including the last
|
|
|
|
// bit. Build a pair of bitmasks to do that.
|
|
|
|
uint32_t start_bitmask = start_bit - 1;
|
|
|
|
uint32_t end_bitmask = ~((end_bit << 1) - 1);
|
|
|
|
|
|
|
|
// If the start address and end address are the same, we mask that
|
|
|
|
// word once, otherwise mask the starting and ending word
|
|
|
|
// separately and all the ones in between.
|
|
|
|
if (start_word_address == end_word_address) {
|
|
|
|
Memory::uint32_at(start_word_address) &= (start_bitmask | end_bitmask);
|
|
|
|
} else {
|
|
|
|
Memory::uint32_at(start_word_address) &= start_bitmask;
|
|
|
|
Memory::uint32_at(end_word_address) &= end_bitmask;
|
|
|
|
start_word_address += kIntSize;
|
|
|
|
memset(start_word_address, 0, end_word_address - start_word_address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class UpdateRSetVisitor: public ObjectVisitor {
|
|
|
|
public:
|
|
|
|
|
|
|
|
void VisitPointer(Object** p) {
|
|
|
|
UpdateRSet(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitPointers(Object** start, Object** end) {
|
|
|
|
// Update a store into slots [start, end), used (a) to update remembered
|
|
|
|
// set when promoting a young object to old space or (b) to rebuild
|
|
|
|
// remembered sets after a mark-compact collection.
|
|
|
|
for (Object** p = start; p < end; p++) UpdateRSet(p);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
|
|
|
|
void UpdateRSet(Object** p) {
|
|
|
|
// The remembered set should not be set. It should be clear for objects
|
|
|
|
// newly copied to old space, and it is cleared before rebuilding in the
|
|
|
|
// mark-compact collector.
|
|
|
|
ASSERT(!Page::IsRSetSet(reinterpret_cast<Address>(p), 0));
|
|
|
|
if (Heap::InNewSpace(*p)) {
|
|
|
|
Page::SetRSet(reinterpret_cast<Address>(p), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int Heap::UpdateRSet(HeapObject* obj) {
|
|
|
|
ASSERT(!InNewSpace(obj));
|
|
|
|
// Special handling of fixed arrays to iterate the body based on the start
|
|
|
|
// address and offset. Just iterating the pointers as in UpdateRSetVisitor
|
|
|
|
// will not work because Page::SetRSet needs to have the start of the
|
2009-08-03 11:05:26 +00:00
|
|
|
// object for large object pages.
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFixedArray()) {
|
|
|
|
FixedArray* array = FixedArray::cast(obj);
|
|
|
|
int length = array->length();
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
int offset = FixedArray::kHeaderSize + i * kPointerSize;
|
|
|
|
ASSERT(!Page::IsRSetSet(obj->address(), offset));
|
|
|
|
if (Heap::InNewSpace(array->get(i))) {
|
|
|
|
Page::SetRSet(obj->address(), offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (!obj->IsCode()) {
|
|
|
|
// Skip code object, we know it does not contain inter-generational
|
|
|
|
// pointers.
|
|
|
|
UpdateRSetVisitor v;
|
|
|
|
obj->Iterate(&v);
|
|
|
|
}
|
|
|
|
return obj->Size();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::RebuildRSets() {
|
2009-07-09 13:28:22 +00:00
|
|
|
// By definition, we do not care about remembered set bits in code,
|
|
|
|
// data, or cell spaces.
|
2008-07-03 15:10:15 +00:00
|
|
|
map_space_->ClearRSet();
|
|
|
|
RebuildRSets(map_space_);
|
|
|
|
|
2008-09-05 12:34:09 +00:00
|
|
|
old_pointer_space_->ClearRSet();
|
|
|
|
RebuildRSets(old_pointer_space_);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Heap::lo_space_->ClearRSet();
|
|
|
|
RebuildRSets(lo_space_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::RebuildRSets(PagedSpace* space) {
|
|
|
|
HeapObjectIterator it(space);
|
2010-01-25 22:53:18 +00:00
|
|
|
for (HeapObject* obj = it.next(); obj != NULL; obj = it.next())
|
|
|
|
Heap::UpdateRSet(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::RebuildRSets(LargeObjectSpace* space) {
|
|
|
|
LargeObjectIterator it(space);
|
2010-01-25 22:53:18 +00:00
|
|
|
for (HeapObject* obj = it.next(); obj != NULL; obj = it.next())
|
|
|
|
Heap::UpdateRSet(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
void Heap::RecordCopiedObject(HeapObject* obj) {
|
|
|
|
bool should_record = false;
|
|
|
|
#ifdef DEBUG
|
|
|
|
should_record = FLAG_heap_stats;
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_LOGGING_AND_PROFILING
|
|
|
|
should_record = should_record || FLAG_log_gc;
|
|
|
|
#endif
|
|
|
|
if (should_record) {
|
2008-10-17 09:13:27 +00:00
|
|
|
if (new_space_.Contains(obj)) {
|
|
|
|
new_space_.RecordAllocation(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
2008-10-17 09:13:27 +00:00
|
|
|
new_space_.RecordPromotion(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
|
|
|
|
|
2008-10-22 08:21:18 +00:00
|
|
|
|
2008-10-17 09:13:27 +00:00
|
|
|
HeapObject* Heap::MigrateObject(HeapObject* source,
|
2008-07-03 15:10:15 +00:00
|
|
|
HeapObject* target,
|
|
|
|
int size) {
|
2008-10-22 08:21:18 +00:00
|
|
|
// Copy the content of source to target.
|
|
|
|
CopyBlock(reinterpret_cast<Object**>(target->address()),
|
|
|
|
reinterpret_cast<Object**>(source->address()),
|
|
|
|
size);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
// Set the forwarding address.
|
2008-10-17 09:13:27 +00:00
|
|
|
source->set_map_word(MapWord::FromForwardingAddress(target));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-15 14:39:34 +00:00
|
|
|
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
|
2009-05-15 15:02:03 +00:00
|
|
|
// Update NewSpace stats if necessary.
|
2008-07-03 15:10:15 +00:00
|
|
|
RecordCopiedObject(target);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-21 08:48:11 +00:00
|
|
|
static inline bool IsShortcutCandidate(HeapObject* object, Map* map) {
|
2009-04-14 12:10:32 +00:00
|
|
|
STATIC_ASSERT(kNotStringTag != 0 && kSymbolTag != 0);
|
2008-10-21 08:48:11 +00:00
|
|
|
ASSERT(object->map() == map);
|
2009-04-14 09:58:42 +00:00
|
|
|
InstanceType type = map->instance_type();
|
2009-04-14 12:00:56 +00:00
|
|
|
if ((type & kShortcutTypeMask) != kShortcutTypeTag) return false;
|
2009-04-14 09:58:42 +00:00
|
|
|
ASSERT(object->IsString() && !object->IsSymbol());
|
|
|
|
return ConsString::cast(object)->unchecked_second() == Heap::empty_string();
|
2008-10-21 08:48:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::ScavengeObjectSlow(HeapObject** p, HeapObject* object) {
|
|
|
|
ASSERT(InFromSpace(object));
|
|
|
|
MapWord first_word = object->map_word();
|
|
|
|
ASSERT(!first_word.IsForwardingAddress());
|
|
|
|
|
|
|
|
// Optimization: Bypass flattened ConsString objects.
|
|
|
|
if (IsShortcutCandidate(object, first_word.ToMap())) {
|
2008-11-03 10:16:05 +00:00
|
|
|
object = HeapObject::cast(ConsString::cast(object)->unchecked_first());
|
2008-07-03 15:10:15 +00:00
|
|
|
*p = object;
|
|
|
|
// After patching *p we have to repeat the checks that object is in the
|
|
|
|
// active semispace of the young generation and not already copied.
|
2008-10-17 09:13:27 +00:00
|
|
|
if (!InNewSpace(object)) return;
|
2008-07-30 08:49:36 +00:00
|
|
|
first_word = object->map_word();
|
|
|
|
if (first_word.IsForwardingAddress()) {
|
|
|
|
*p = first_word.ToForwardingAddress();
|
2008-07-03 15:10:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
int object_size = object->SizeFromMap(first_word.ToMap());
|
2009-05-15 15:02:03 +00:00
|
|
|
// We rely on live objects in new space to be at least two pointers,
|
|
|
|
// so we can store the from-space address and map pointer of promoted
|
|
|
|
// objects in the to space.
|
|
|
|
ASSERT(object_size >= 2 * kPointerSize);
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// If the object should be promoted, we try to copy it to old space.
|
|
|
|
if (ShouldBePromoted(object->address(), object_size)) {
|
2009-06-18 14:06:36 +00:00
|
|
|
Object* result;
|
|
|
|
if (object_size > MaxObjectSizeInPagedSpace()) {
|
|
|
|
result = lo_space_->AllocateRawFixedArray(object_size);
|
|
|
|
if (!result->IsFailure()) {
|
2009-05-15 15:02:03 +00:00
|
|
|
// Save the from-space object pointer and its map pointer at the
|
|
|
|
// top of the to space to be swept and copied later. Write the
|
|
|
|
// forwarding address over the map word of the from-space
|
|
|
|
// object.
|
2009-06-18 14:06:36 +00:00
|
|
|
HeapObject* target = HeapObject::cast(result);
|
2009-05-15 15:02:03 +00:00
|
|
|
promotion_queue.insert(object, first_word.ToMap());
|
|
|
|
object->set_map_word(MapWord::FromForwardingAddress(target));
|
|
|
|
|
|
|
|
// Give the space allocated for the result a proper map by
|
|
|
|
// treating it as a free list node (not linked into the free
|
|
|
|
// list).
|
|
|
|
FreeListNode* node = FreeListNode::FromAddress(target->address());
|
|
|
|
node->set_size(object_size);
|
|
|
|
|
|
|
|
*p = target;
|
2009-06-18 14:06:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
OldSpace* target_space = Heap::TargetSpace(object);
|
|
|
|
ASSERT(target_space == Heap::old_pointer_space_ ||
|
|
|
|
target_space == Heap::old_data_space_);
|
|
|
|
result = target_space->AllocateRaw(object_size);
|
|
|
|
if (!result->IsFailure()) {
|
|
|
|
HeapObject* target = HeapObject::cast(result);
|
|
|
|
if (target_space == Heap::old_pointer_space_) {
|
|
|
|
// Save the from-space object pointer and its map pointer at the
|
|
|
|
// top of the to space to be swept and copied later. Write the
|
|
|
|
// forwarding address over the map word of the from-space
|
|
|
|
// object.
|
|
|
|
promotion_queue.insert(object, first_word.ToMap());
|
|
|
|
object->set_map_word(MapWord::FromForwardingAddress(target));
|
|
|
|
|
|
|
|
// Give the space allocated for the result a proper map by
|
|
|
|
// treating it as a free list node (not linked into the free
|
|
|
|
// list).
|
|
|
|
FreeListNode* node = FreeListNode::FromAddress(target->address());
|
|
|
|
node->set_size(object_size);
|
|
|
|
|
|
|
|
*p = target;
|
|
|
|
} else {
|
|
|
|
// Objects promoted to the data space can be copied immediately
|
|
|
|
// and not revisited---we will never sweep that space for
|
|
|
|
// pointers and the copied objects do not contain pointers to
|
|
|
|
// new space objects.
|
|
|
|
*p = MigrateObject(object, target, object_size);
|
2008-07-03 15:10:15 +00:00
|
|
|
#ifdef DEBUG
|
2009-06-18 14:06:36 +00:00
|
|
|
VerifyNonPointerSpacePointersVisitor v;
|
|
|
|
(*p)->Iterate(&v);
|
2008-07-03 15:10:15 +00:00
|
|
|
#endif
|
2009-06-18 14:06:36 +00:00
|
|
|
}
|
|
|
|
return;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// The object should remain in new space or the old space allocation failed.
|
2008-10-21 08:48:11 +00:00
|
|
|
Object* result = new_space_.AllocateRaw(object_size);
|
2008-07-03 15:10:15 +00:00
|
|
|
// Failed allocation at this point is utterly unexpected.
|
|
|
|
ASSERT(!result->IsFailure());
|
2008-10-17 09:13:27 +00:00
|
|
|
*p = MigrateObject(object, HeapObject::cast(result), object_size);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-21 08:48:11 +00:00
|
|
|
void Heap::ScavengePointer(HeapObject** p) {
|
|
|
|
ScavengeObject(p, *p);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* Heap::AllocatePartialMap(InstanceType instance_type,
|
|
|
|
int instance_size) {
|
2009-07-09 11:13:08 +00:00
|
|
|
Object* result = AllocateRawMap();
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
// Map::cast cannot be used due to uninitialized map field.
|
2009-07-08 19:12:58 +00:00
|
|
|
reinterpret_cast<Map*>(result)->set_map(raw_unchecked_meta_map());
|
2008-07-03 15:10:15 +00:00
|
|
|
reinterpret_cast<Map*>(result)->set_instance_type(instance_type);
|
|
|
|
reinterpret_cast<Map*>(result)->set_instance_size(instance_size);
|
2008-10-15 06:03:26 +00:00
|
|
|
reinterpret_cast<Map*>(result)->set_inobject_properties(0);
|
2008-07-03 15:10:15 +00:00
|
|
|
reinterpret_cast<Map*>(result)->set_unused_property_fields(0);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateMap(InstanceType instance_type, int instance_size) {
|
2009-07-09 11:13:08 +00:00
|
|
|
Object* result = AllocateRawMap();
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
Map* map = reinterpret_cast<Map*>(result);
|
|
|
|
map->set_map(meta_map());
|
|
|
|
map->set_instance_type(instance_type);
|
|
|
|
map->set_prototype(null_value());
|
|
|
|
map->set_constructor(null_value());
|
|
|
|
map->set_instance_size(instance_size);
|
2008-10-15 06:03:26 +00:00
|
|
|
map->set_inobject_properties(0);
|
2009-08-19 07:30:20 +00:00
|
|
|
map->set_pre_allocated_property_fields(0);
|
2008-08-27 10:11:39 +00:00
|
|
|
map->set_instance_descriptors(empty_descriptor_array());
|
2008-07-03 15:10:15 +00:00
|
|
|
map->set_code_cache(empty_fixed_array());
|
|
|
|
map->set_unused_property_fields(0);
|
|
|
|
map->set_bit_field(0);
|
2010-01-27 13:51:04 +00:00
|
|
|
map->set_bit_field2(1 << Map::kIsExtensible);
|
2009-12-17 08:53:18 +00:00
|
|
|
|
|
|
|
// If the map object is aligned fill the padding area with Smi 0 objects.
|
|
|
|
if (Map::kPadStart < Map::kSize) {
|
|
|
|
memset(reinterpret_cast<byte*>(map) + Map::kPadStart - kHeapObjectTag,
|
|
|
|
0,
|
|
|
|
Map::kSize - Map::kPadStart);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-08 19:12:58 +00:00
|
|
|
const Heap::StringTypeTable Heap::string_type_table[] = {
|
|
|
|
#define STRING_TYPE_ELEMENT(type, size, name, camel_name) \
|
|
|
|
{type, size, k##camel_name##MapRootIndex},
|
|
|
|
STRING_TYPE_LIST(STRING_TYPE_ELEMENT)
|
|
|
|
#undef STRING_TYPE_ELEMENT
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const Heap::ConstantSymbolTable Heap::constant_symbol_table[] = {
|
|
|
|
#define CONSTANT_SYMBOL_ELEMENT(name, contents) \
|
|
|
|
{contents, k##name##RootIndex},
|
|
|
|
SYMBOL_LIST(CONSTANT_SYMBOL_ELEMENT)
|
|
|
|
#undef CONSTANT_SYMBOL_ELEMENT
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const Heap::StructTable Heap::struct_table[] = {
|
|
|
|
#define STRUCT_TABLE_ELEMENT(NAME, Name, name) \
|
|
|
|
{ NAME##_TYPE, Name::kSize, k##Name##MapRootIndex },
|
|
|
|
STRUCT_LIST(STRUCT_TABLE_ELEMENT)
|
|
|
|
#undef STRUCT_TABLE_ELEMENT
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
bool Heap::CreateInitialMaps() {
|
|
|
|
Object* obj = AllocatePartialMap(MAP_TYPE, Map::kSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
// Map::cast cannot be used due to uninitialized map field.
|
2009-07-08 19:12:58 +00:00
|
|
|
Map* new_meta_map = reinterpret_cast<Map*>(obj);
|
|
|
|
set_meta_map(new_meta_map);
|
|
|
|
new_meta_map->set_map(new_meta_map);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-20 08:05:12 +00:00
|
|
|
obj = AllocatePartialMap(FIXED_ARRAY_TYPE, FixedArray::kHeaderSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_fixed_array_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
obj = AllocatePartialMap(ODDBALL_TYPE, Oddball::kSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_oddball_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Allocate the empty array
|
|
|
|
obj = AllocateEmptyFixedArray();
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_empty_fixed_array(FixedArray::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-09-05 12:34:09 +00:00
|
|
|
obj = Allocate(oddball_map(), OLD_DATA_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_null_value(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-09 11:13:08 +00:00
|
|
|
// Allocate the empty descriptor array.
|
2008-08-27 10:11:39 +00:00
|
|
|
obj = AllocateEmptyFixedArray();
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-09 11:13:08 +00:00
|
|
|
set_empty_descriptor_array(DescriptorArray::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-08-27 10:11:39 +00:00
|
|
|
// Fix the instance_descriptors for the existing maps.
|
|
|
|
meta_map()->set_instance_descriptors(empty_descriptor_array());
|
2008-07-03 15:10:15 +00:00
|
|
|
meta_map()->set_code_cache(empty_fixed_array());
|
|
|
|
|
2008-08-27 10:11:39 +00:00
|
|
|
fixed_array_map()->set_instance_descriptors(empty_descriptor_array());
|
2008-07-03 15:10:15 +00:00
|
|
|
fixed_array_map()->set_code_cache(empty_fixed_array());
|
|
|
|
|
2008-08-27 10:11:39 +00:00
|
|
|
oddball_map()->set_instance_descriptors(empty_descriptor_array());
|
2008-07-03 15:10:15 +00:00
|
|
|
oddball_map()->set_code_cache(empty_fixed_array());
|
|
|
|
|
|
|
|
// Fix prototype object for existing maps.
|
|
|
|
meta_map()->set_prototype(null_value());
|
|
|
|
meta_map()->set_constructor(null_value());
|
|
|
|
|
|
|
|
fixed_array_map()->set_prototype(null_value());
|
|
|
|
fixed_array_map()->set_constructor(null_value());
|
2009-07-09 11:13:08 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
oddball_map()->set_prototype(null_value());
|
|
|
|
oddball_map()->set_constructor(null_value());
|
|
|
|
|
|
|
|
obj = AllocateMap(HEAP_NUMBER_TYPE, HeapNumber::kSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_heap_number_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
obj = AllocateMap(PROXY_TYPE, Proxy::kSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_proxy_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-08 19:12:58 +00:00
|
|
|
for (unsigned i = 0; i < ARRAY_SIZE(string_type_table); i++) {
|
|
|
|
const StringTypeTable& entry = string_type_table[i];
|
|
|
|
obj = AllocateMap(entry.type, entry.size);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
roots_[entry.index] = Map::cast(obj);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-11-24 14:10:06 +00:00
|
|
|
obj = AllocateMap(STRING_TYPE, SeqTwoByteString::kAlignedSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-11-24 14:10:06 +00:00
|
|
|
set_undetectable_string_map(Map::cast(obj));
|
2009-07-08 19:12:58 +00:00
|
|
|
Map::cast(obj)->set_is_undetectable();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-11-24 14:10:06 +00:00
|
|
|
obj = AllocateMap(ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-11-24 14:10:06 +00:00
|
|
|
set_undetectable_ascii_string_map(Map::cast(obj));
|
2009-07-08 19:12:58 +00:00
|
|
|
Map::cast(obj)->set_is_undetectable();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-17 12:12:24 +00:00
|
|
|
obj = AllocateMap(BYTE_ARRAY_TYPE, ByteArray::kAlignedSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_byte_array_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-28 08:43:51 +00:00
|
|
|
obj = AllocateMap(PIXEL_ARRAY_TYPE, PixelArray::kAlignedSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_pixel_array_map(Map::cast(obj));
|
|
|
|
|
2009-10-20 15:26:17 +00:00
|
|
|
obj = AllocateMap(EXTERNAL_BYTE_ARRAY_TYPE,
|
|
|
|
ExternalArray::kAlignedSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_external_byte_array_map(Map::cast(obj));
|
|
|
|
|
|
|
|
obj = AllocateMap(EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE,
|
|
|
|
ExternalArray::kAlignedSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_external_unsigned_byte_array_map(Map::cast(obj));
|
|
|
|
|
|
|
|
obj = AllocateMap(EXTERNAL_SHORT_ARRAY_TYPE,
|
|
|
|
ExternalArray::kAlignedSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_external_short_array_map(Map::cast(obj));
|
|
|
|
|
|
|
|
obj = AllocateMap(EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE,
|
|
|
|
ExternalArray::kAlignedSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_external_unsigned_short_array_map(Map::cast(obj));
|
|
|
|
|
|
|
|
obj = AllocateMap(EXTERNAL_INT_ARRAY_TYPE,
|
|
|
|
ExternalArray::kAlignedSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_external_int_array_map(Map::cast(obj));
|
|
|
|
|
|
|
|
obj = AllocateMap(EXTERNAL_UNSIGNED_INT_ARRAY_TYPE,
|
|
|
|
ExternalArray::kAlignedSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_external_unsigned_int_array_map(Map::cast(obj));
|
|
|
|
|
|
|
|
obj = AllocateMap(EXTERNAL_FLOAT_ARRAY_TYPE,
|
|
|
|
ExternalArray::kAlignedSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_external_float_array_map(Map::cast(obj));
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
obj = AllocateMap(CODE_TYPE, Code::kHeaderSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_code_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-09 11:13:08 +00:00
|
|
|
obj = AllocateMap(JS_GLOBAL_PROPERTY_CELL_TYPE,
|
|
|
|
JSGlobalPropertyCell::kSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_global_property_cell_map(Map::cast(obj));
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
obj = AllocateMap(FILLER_TYPE, kPointerSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-09 11:13:08 +00:00
|
|
|
set_one_pointer_filler_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
obj = AllocateMap(FILLER_TYPE, 2 * kPointerSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-09 11:13:08 +00:00
|
|
|
set_two_pointer_filler_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-08 19:12:58 +00:00
|
|
|
for (unsigned i = 0; i < ARRAY_SIZE(struct_table); i++) {
|
|
|
|
const StructTable& entry = struct_table[i];
|
|
|
|
obj = AllocateMap(entry.type, entry.size);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
roots_[entry.index] = Map::cast(obj);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-09-23 11:45:43 +00:00
|
|
|
obj = AllocateMap(FIXED_ARRAY_TYPE, HeapObject::kHeaderSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_hash_table_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-09-23 11:45:43 +00:00
|
|
|
obj = AllocateMap(FIXED_ARRAY_TYPE, HeapObject::kHeaderSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_context_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-12-18 11:28:13 +00:00
|
|
|
obj = AllocateMap(FIXED_ARRAY_TYPE, HeapObject::kHeaderSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_catch_context_map(Map::cast(obj));
|
2008-12-18 11:28:13 +00:00
|
|
|
|
2008-09-23 11:45:43 +00:00
|
|
|
obj = AllocateMap(FIXED_ARRAY_TYPE, HeapObject::kHeaderSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_global_context_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
obj = AllocateMap(JS_FUNCTION_TYPE, JSFunction::kSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_boilerplate_function_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
obj = AllocateMap(SHARED_FUNCTION_INFO_TYPE, SharedFunctionInfo::kSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_shared_function_info_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-08-27 10:11:39 +00:00
|
|
|
ASSERT(!Heap::InNewSpace(Heap::empty_fixed_array()));
|
2008-07-03 15:10:15 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateHeapNumber(double value, PretenureFlag pretenure) {
|
|
|
|
// Statically ensure that it is safe to allocate heap numbers in paged
|
|
|
|
// spaces.
|
|
|
|
STATIC_ASSERT(HeapNumber::kSize <= Page::kMaxHeapObjectSize);
|
2008-09-05 12:34:09 +00:00
|
|
|
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
|
2009-10-02 13:35:37 +00:00
|
|
|
|
2008-10-30 09:15:58 +00:00
|
|
|
Object* result = AllocateRaw(HeapNumber::kSize, space, OLD_DATA_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
HeapObject::cast(result)->set_map(heap_number_map());
|
|
|
|
HeapNumber::cast(result)->set_value(value);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateHeapNumber(double value) {
|
2008-10-30 09:15:58 +00:00
|
|
|
// Use general version, if we're forced to always allocate.
|
2009-10-02 13:35:37 +00:00
|
|
|
if (always_allocate()) return AllocateHeapNumber(value, TENURED);
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// This version of AllocateHeapNumber is optimized for
|
|
|
|
// allocation in new space.
|
|
|
|
STATIC_ASSERT(HeapNumber::kSize <= Page::kMaxHeapObjectSize);
|
|
|
|
ASSERT(allocation_allowed_ && gc_state_ == NOT_IN_GC);
|
2008-10-17 09:13:27 +00:00
|
|
|
Object* result = new_space_.AllocateRaw(HeapNumber::kSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
HeapObject::cast(result)->set_map(heap_number_map());
|
|
|
|
HeapNumber::cast(result)->set_value(value);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-30 10:05:36 +00:00
|
|
|
Object* Heap::AllocateJSGlobalPropertyCell(Object* value) {
|
2009-07-09 11:13:08 +00:00
|
|
|
Object* result = AllocateRawCell();
|
2009-06-30 10:05:36 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
HeapObject::cast(result)->set_map(global_property_cell_map());
|
|
|
|
JSGlobalPropertyCell::cast(result)->set_value(value);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* Heap::CreateOddball(Map* map,
|
|
|
|
const char* to_string,
|
|
|
|
Object* to_number) {
|
2008-09-05 12:34:09 +00:00
|
|
|
Object* result = Allocate(map, OLD_DATA_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
return Oddball::cast(result)->Initialize(to_string, to_number);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Heap::CreateApiObjects() {
|
|
|
|
Object* obj;
|
|
|
|
|
|
|
|
obj = AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_neander_map(Map::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-08 19:12:58 +00:00
|
|
|
obj = Heap::AllocateJSObjectFromMap(neander_map());
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
Object* elements = AllocateFixedArray(2);
|
|
|
|
if (elements->IsFailure()) return false;
|
|
|
|
FixedArray::cast(elements)->set(0, Smi::FromInt(0));
|
|
|
|
JSObject::cast(obj)->set_elements(FixedArray::cast(elements));
|
2009-07-08 19:12:58 +00:00
|
|
|
set_message_listeners(JSObject::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-06-29 21:15:03 +00:00
|
|
|
|
|
|
|
void Heap::CreateCEntryStub() {
|
2009-09-08 11:52:05 +00:00
|
|
|
CEntryStub stub(1);
|
2009-07-08 19:12:58 +00:00
|
|
|
set_c_entry_code(*stub.GetCode());
|
2009-06-29 21:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-31 12:40:37 +00:00
|
|
|
#if V8_TARGET_ARCH_ARM && V8_NATIVE_REGEXP
|
|
|
|
void Heap::CreateRegExpCEntryStub() {
|
|
|
|
RegExpCEntryStub stub;
|
|
|
|
set_re_c_entry_code(*stub.GetCode());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2009-06-29 21:15:03 +00:00
|
|
|
void Heap::CreateCEntryDebugBreakStub() {
|
|
|
|
CEntryDebugBreakStub stub;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_c_entry_debug_break_code(*stub.GetCode());
|
2009-06-29 21:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::CreateJSEntryStub() {
|
|
|
|
JSEntryStub stub;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_js_entry_code(*stub.GetCode());
|
2009-06-29 21:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::CreateJSConstructEntryStub() {
|
|
|
|
JSConstructEntryStub stub;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_js_construct_entry_code(*stub.GetCode());
|
2009-06-29 21:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Heap::CreateFixedStubs() {
|
|
|
|
// Here we create roots for fixed stubs. They are needed at GC
|
|
|
|
// for cooking and uncooking (check out frames.cc).
|
|
|
|
// The eliminates the need for doing dictionary lookup in the
|
|
|
|
// stub cache for these stubs.
|
|
|
|
HandleScope scope;
|
2009-06-29 21:15:03 +00:00
|
|
|
// gcc-4.4 has problem generating correct code of following snippet:
|
|
|
|
// { CEntryStub stub;
|
|
|
|
// c_entry_code_ = *stub.GetCode();
|
|
|
|
// }
|
|
|
|
// { CEntryDebugBreakStub stub;
|
|
|
|
// c_entry_debug_break_code_ = *stub.GetCode();
|
|
|
|
// }
|
|
|
|
// To workaround the problem, make separate functions without inlining.
|
|
|
|
Heap::CreateCEntryStub();
|
|
|
|
Heap::CreateCEntryDebugBreakStub();
|
|
|
|
Heap::CreateJSEntryStub();
|
|
|
|
Heap::CreateJSConstructEntryStub();
|
2009-08-31 12:40:37 +00:00
|
|
|
#if V8_TARGET_ARCH_ARM && V8_NATIVE_REGEXP
|
|
|
|
Heap::CreateRegExpCEntryStub();
|
|
|
|
#endif
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Heap::CreateInitialObjects() {
|
|
|
|
Object* obj;
|
|
|
|
|
|
|
|
// The -0 value must be set before NumberFromDouble works.
|
|
|
|
obj = AllocateHeapNumber(-0.0, TENURED);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_minus_zero_value(obj);
|
|
|
|
ASSERT(signbit(minus_zero_value()->Number()) != 0);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
obj = AllocateHeapNumber(OS::nan_value(), TENURED);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_nan_value(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-09-05 12:34:09 +00:00
|
|
|
obj = Allocate(oddball_map(), OLD_DATA_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_undefined_value(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
ASSERT(!InNewSpace(undefined_value()));
|
|
|
|
|
|
|
|
// Allocate initial symbol table.
|
|
|
|
obj = SymbolTable::Allocate(kInitialSymbolTableSize);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
// Don't use set_symbol_table() due to asserts.
|
|
|
|
roots_[kSymbolTableRootIndex] = obj;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Assign the print strings for oddballs after creating symboltable.
|
|
|
|
Object* symbol = LookupAsciiSymbol("undefined");
|
|
|
|
if (symbol->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
Oddball::cast(undefined_value())->set_to_string(String::cast(symbol));
|
|
|
|
Oddball::cast(undefined_value())->set_to_number(nan_value());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Assign the print strings for oddballs after creating symboltable.
|
|
|
|
symbol = LookupAsciiSymbol("null");
|
|
|
|
if (symbol->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
Oddball::cast(null_value())->set_to_string(String::cast(symbol));
|
|
|
|
Oddball::cast(null_value())->set_to_number(Smi::FromInt(0));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Allocate the null_value
|
|
|
|
obj = Oddball::cast(null_value())->Initialize("null", Smi::FromInt(0));
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
|
|
|
|
obj = CreateOddball(oddball_map(), "true", Smi::FromInt(1));
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_true_value(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
obj = CreateOddball(oddball_map(), "false", Smi::FromInt(0));
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_false_value(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
obj = CreateOddball(oddball_map(), "hole", Smi::FromInt(-1));
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_the_hole_value(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-29 12:34:21 +00:00
|
|
|
obj = CreateOddball(
|
|
|
|
oddball_map(), "no_interceptor_result_sentinel", Smi::FromInt(-2));
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_no_interceptor_result_sentinel(obj);
|
|
|
|
|
2009-08-19 15:14:11 +00:00
|
|
|
obj = CreateOddball(oddball_map(), "termination_exception", Smi::FromInt(-3));
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
set_termination_exception(obj);
|
2009-07-29 12:34:21 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// Allocate the empty string.
|
|
|
|
obj = AllocateRawAsciiString(0, TENURED);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_empty_string(String::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-08 19:12:58 +00:00
|
|
|
for (unsigned i = 0; i < ARRAY_SIZE(constant_symbol_table); i++) {
|
|
|
|
obj = LookupAsciiSymbol(constant_symbol_table[i].contents);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
roots_[constant_symbol_table[i].index] = String::cast(obj);
|
|
|
|
}
|
2009-03-19 19:29:23 +00:00
|
|
|
|
2009-03-19 18:50:00 +00:00
|
|
|
// Allocate the hidden symbol which is used to identify the hidden properties
|
|
|
|
// in JSObjects. The hash code has a special value so that it will not match
|
|
|
|
// the empty string when searching for the property. It cannot be part of the
|
2009-07-08 19:12:58 +00:00
|
|
|
// loop above because it needs to be allocated manually with the special
|
2009-03-19 18:50:00 +00:00
|
|
|
// hash code in place. The hash code for the hidden_symbol is zero to ensure
|
|
|
|
// that it will always be at the first entry in property descriptors.
|
|
|
|
obj = AllocateSymbol(CStrVector(""), 0, String::kHashComputedMask);
|
|
|
|
if (obj->IsFailure()) return false;
|
|
|
|
hidden_symbol_ = String::cast(obj);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Allocate the proxy for __proto__.
|
|
|
|
obj = AllocateProxy((Address) &Accessors::ObjectPrototype);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_prototype_accessors(Proxy::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-30 09:13:48 +00:00
|
|
|
// Allocate the code_stubs dictionary. The initial size is set to avoid
|
|
|
|
// expanding the dictionary during bootstrapping.
|
|
|
|
obj = NumberDictionary::Allocate(128);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_code_stubs(NumberDictionary::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-30 09:13:48 +00:00
|
|
|
// Allocate the non_monomorphic_cache used in stub-cache.cc. The initial size
|
|
|
|
// is set to avoid expanding the dictionary during bootstrapping.
|
|
|
|
obj = NumberDictionary::Allocate(64);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_non_monomorphic_cache(NumberDictionary::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
CreateFixedStubs();
|
|
|
|
|
2010-01-06 11:19:28 +00:00
|
|
|
if (InitializeNumberStringCache()->IsFailure()) return false;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Allocate cache for single character strings.
|
|
|
|
obj = AllocateFixedArray(String::kMaxAsciiCharCode+1);
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_single_character_string_cache(FixedArray::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Allocate cache for external strings pointing to native source code.
|
|
|
|
obj = AllocateFixedArray(Natives::GetBuiltinsCount());
|
|
|
|
if (obj->IsFailure()) return false;
|
2009-07-08 19:12:58 +00:00
|
|
|
set_natives_source_cache(FixedArray::cast(obj));
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-03-10 12:05:20 +00:00
|
|
|
// Handling of script id generation is in Factory::NewScript.
|
2009-07-08 19:12:58 +00:00
|
|
|
set_last_script_id(undefined_value());
|
2009-03-10 12:05:20 +00:00
|
|
|
|
2008-10-23 07:04:56 +00:00
|
|
|
// Initialize keyed lookup cache.
|
2009-06-17 06:07:49 +00:00
|
|
|
KeyedLookupCache::Clear();
|
2008-10-23 07:04:56 +00:00
|
|
|
|
2009-06-22 08:09:57 +00:00
|
|
|
// Initialize context slot cache.
|
|
|
|
ContextSlotCache::Clear();
|
|
|
|
|
2009-06-22 14:29:35 +00:00
|
|
|
// Initialize descriptor cache.
|
|
|
|
DescriptorLookupCache::Clear();
|
|
|
|
|
2008-09-11 10:51:52 +00:00
|
|
|
// Initialize compilation cache.
|
|
|
|
CompilationCache::Clear();
|
2008-09-05 16:27:56 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-06 11:19:28 +00:00
|
|
|
Object* Heap::InitializeNumberStringCache() {
|
|
|
|
// Compute the size of the number string cache based on the max heap size.
|
|
|
|
// max_semispace_size_ == 512 KB => number_string_cache_size = 32.
|
|
|
|
// max_semispace_size_ == 8 MB => number_string_cache_size = 16KB.
|
|
|
|
int number_string_cache_size = max_semispace_size_ / 512;
|
|
|
|
number_string_cache_size = Max(32, Min(16*KB, number_string_cache_size));
|
|
|
|
Object* obj = AllocateFixedArray(number_string_cache_size * 2);
|
|
|
|
if (!obj->IsFailure()) set_number_string_cache(FixedArray::cast(obj));
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::FlushNumberStringCache() {
|
|
|
|
// Flush the number to string cache.
|
|
|
|
int len = number_string_cache()->length();
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
number_string_cache()->set_undefined(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-22 13:07:27 +00:00
|
|
|
static inline int double_get_hash(double d) {
|
2009-12-22 11:35:05 +00:00
|
|
|
DoubleRepresentation rep(d);
|
2010-01-06 11:19:28 +00:00
|
|
|
return static_cast<int>(rep.bits) ^ static_cast<int>(rep.bits >> 32);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-22 13:07:27 +00:00
|
|
|
static inline int smi_get_hash(Smi* smi) {
|
2010-01-06 11:19:28 +00:00
|
|
|
return smi->value();
|
2009-12-22 11:35:05 +00:00
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Object* Heap::GetNumberStringCache(Object* number) {
|
|
|
|
int hash;
|
2010-01-06 11:19:28 +00:00
|
|
|
int mask = (number_string_cache()->length() >> 1) - 1;
|
2008-07-03 15:10:15 +00:00
|
|
|
if (number->IsSmi()) {
|
2010-01-06 11:19:28 +00:00
|
|
|
hash = smi_get_hash(Smi::cast(number)) & mask;
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
2010-01-06 11:19:28 +00:00
|
|
|
hash = double_get_hash(number->Number()) & mask;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2009-07-08 19:12:58 +00:00
|
|
|
Object* key = number_string_cache()->get(hash * 2);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (key == number) {
|
2009-07-08 19:12:58 +00:00
|
|
|
return String::cast(number_string_cache()->get(hash * 2 + 1));
|
2008-07-03 15:10:15 +00:00
|
|
|
} else if (key->IsHeapNumber() &&
|
|
|
|
number->IsHeapNumber() &&
|
|
|
|
key->Number() == number->Number()) {
|
2009-07-08 19:12:58 +00:00
|
|
|
return String::cast(number_string_cache()->get(hash * 2 + 1));
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
return undefined_value();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::SetNumberStringCache(Object* number, String* string) {
|
|
|
|
int hash;
|
2010-01-06 11:19:28 +00:00
|
|
|
int mask = (number_string_cache()->length() >> 1) - 1;
|
2008-07-03 15:10:15 +00:00
|
|
|
if (number->IsSmi()) {
|
2010-01-06 11:19:28 +00:00
|
|
|
hash = smi_get_hash(Smi::cast(number)) & mask;
|
2009-07-08 19:12:58 +00:00
|
|
|
number_string_cache()->set(hash * 2, number, SKIP_WRITE_BARRIER);
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
2010-01-06 11:19:28 +00:00
|
|
|
hash = double_get_hash(number->Number()) & mask;
|
2009-07-08 19:12:58 +00:00
|
|
|
number_string_cache()->set(hash * 2, number);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2009-07-08 19:12:58 +00:00
|
|
|
number_string_cache()->set(hash * 2 + 1, string);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::SmiOrNumberFromDouble(double value,
|
|
|
|
bool new_object,
|
|
|
|
PretenureFlag pretenure) {
|
|
|
|
// We need to distinguish the minus zero value and this cannot be
|
|
|
|
// done after conversion to int. Doing this by comparing bit
|
|
|
|
// patterns is faster than using fpclassify() et al.
|
|
|
|
static const DoubleRepresentation plus_zero(0.0);
|
|
|
|
static const DoubleRepresentation minus_zero(-0.0);
|
|
|
|
static const DoubleRepresentation nan(OS::nan_value());
|
2009-07-08 19:12:58 +00:00
|
|
|
ASSERT(minus_zero_value() != NULL);
|
2008-07-03 15:10:15 +00:00
|
|
|
ASSERT(sizeof(plus_zero.value) == sizeof(plus_zero.bits));
|
|
|
|
|
|
|
|
DoubleRepresentation rep(value);
|
|
|
|
if (rep.bits == plus_zero.bits) return Smi::FromInt(0); // not uncommon
|
|
|
|
if (rep.bits == minus_zero.bits) {
|
|
|
|
return new_object ? AllocateHeapNumber(-0.0, pretenure)
|
2009-07-08 19:12:58 +00:00
|
|
|
: minus_zero_value();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
if (rep.bits == nan.bits) {
|
|
|
|
return new_object
|
|
|
|
? AllocateHeapNumber(OS::nan_value(), pretenure)
|
2009-07-08 19:12:58 +00:00
|
|
|
: nan_value();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Try to represent the value as a tagged small integer.
|
|
|
|
int int_value = FastD2I(value);
|
|
|
|
if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
|
|
|
|
return Smi::FromInt(int_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Materialize the value in the heap.
|
|
|
|
return AllocateHeapNumber(value, pretenure);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-02 13:43:16 +00:00
|
|
|
Object* Heap::NumberToString(Object* number) {
|
|
|
|
Object* cached = GetNumberStringCache(number);
|
|
|
|
if (cached != undefined_value()) {
|
|
|
|
return cached;
|
|
|
|
}
|
|
|
|
|
|
|
|
char arr[100];
|
|
|
|
Vector<char> buffer(arr, ARRAY_SIZE(arr));
|
|
|
|
const char* str;
|
|
|
|
if (number->IsSmi()) {
|
|
|
|
int num = Smi::cast(number)->value();
|
|
|
|
str = IntToCString(num, buffer);
|
|
|
|
} else {
|
|
|
|
double num = HeapNumber::cast(number)->value();
|
|
|
|
str = DoubleToCString(num, buffer);
|
|
|
|
}
|
|
|
|
Object* result = AllocateStringFromAscii(CStrVector(str));
|
|
|
|
|
|
|
|
if (!result->IsFailure()) {
|
|
|
|
SetNumberStringCache(number, String::cast(result));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-20 15:26:17 +00:00
|
|
|
Map* Heap::MapForExternalArrayType(ExternalArrayType array_type) {
|
|
|
|
return Map::cast(roots_[RootIndexForExternalArrayType(array_type)]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Heap::RootListIndex Heap::RootIndexForExternalArrayType(
|
|
|
|
ExternalArrayType array_type) {
|
|
|
|
switch (array_type) {
|
|
|
|
case kExternalByteArray:
|
|
|
|
return kExternalByteArrayMapRootIndex;
|
|
|
|
case kExternalUnsignedByteArray:
|
|
|
|
return kExternalUnsignedByteArrayMapRootIndex;
|
|
|
|
case kExternalShortArray:
|
|
|
|
return kExternalShortArrayMapRootIndex;
|
|
|
|
case kExternalUnsignedShortArray:
|
|
|
|
return kExternalUnsignedShortArrayMapRootIndex;
|
|
|
|
case kExternalIntArray:
|
|
|
|
return kExternalIntArrayMapRootIndex;
|
|
|
|
case kExternalUnsignedIntArray:
|
|
|
|
return kExternalUnsignedIntArrayMapRootIndex;
|
|
|
|
case kExternalFloatArray:
|
|
|
|
return kExternalFloatArrayMapRootIndex;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return kUndefinedValueRootIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* Heap::NewNumberFromDouble(double value, PretenureFlag pretenure) {
|
|
|
|
return SmiOrNumberFromDouble(value,
|
|
|
|
true /* number object must be new */,
|
|
|
|
pretenure);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::NumberFromDouble(double value, PretenureFlag pretenure) {
|
|
|
|
return SmiOrNumberFromDouble(value,
|
|
|
|
false /* use preallocated NaN, -0.0 */,
|
|
|
|
pretenure);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateProxy(Address proxy, PretenureFlag pretenure) {
|
|
|
|
// Statically ensure that it is safe to allocate proxies in paged spaces.
|
|
|
|
STATIC_ASSERT(Proxy::kSize <= Page::kMaxHeapObjectSize);
|
2009-07-28 08:43:51 +00:00
|
|
|
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* result = Allocate(proxy_map(), space);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
Proxy::cast(result)->set_proxy(proxy);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateSharedFunctionInfo(Object* name) {
|
2009-07-01 11:44:37 +00:00
|
|
|
Object* result = Allocate(shared_function_info_map(), OLD_POINTER_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
SharedFunctionInfo* share = SharedFunctionInfo::cast(result);
|
|
|
|
share->set_name(name);
|
|
|
|
Code* illegal = Builtins::builtin(Builtins::Illegal);
|
|
|
|
share->set_code(illegal);
|
2009-06-19 07:36:16 +00:00
|
|
|
Code* construct_stub = Builtins::builtin(Builtins::JSConstructStubGeneric);
|
|
|
|
share->set_construct_stub(construct_stub);
|
2008-07-03 15:10:15 +00:00
|
|
|
share->set_expected_nof_properties(0);
|
|
|
|
share->set_length(0);
|
|
|
|
share->set_formal_parameter_count(0);
|
|
|
|
share->set_instance_class_name(Object_symbol());
|
|
|
|
share->set_function_data(undefined_value());
|
|
|
|
share->set_script(undefined_value());
|
|
|
|
share->set_start_position_and_type(0);
|
|
|
|
share->set_debug_info(undefined_value());
|
2009-04-14 00:51:59 +00:00
|
|
|
share->set_inferred_name(empty_string());
|
2009-08-19 07:30:20 +00:00
|
|
|
share->set_compiler_hints(0);
|
|
|
|
share->set_this_property_assignments_count(0);
|
|
|
|
share->set_this_property_assignments(undefined_value());
|
2008-07-03 15:10:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-20 10:11:45 +00:00
|
|
|
// Returns true for a character in a range. Both limits are inclusive.
|
|
|
|
static inline bool Between(uint32_t character, uint32_t from, uint32_t to) {
|
|
|
|
// This makes uses of the the unsigned wraparound.
|
|
|
|
return character - from <= to - from;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline Object* MakeOrFindTwoCharacterString(uint32_t c1, uint32_t c2) {
|
|
|
|
String* symbol;
|
|
|
|
// Numeric strings have a different hash algorithm not known by
|
|
|
|
// LookupTwoCharsSymbolIfExists, so we skip this step for such strings.
|
|
|
|
if ((!Between(c1, '0', '9') || !Between(c2, '0', '9')) &&
|
|
|
|
Heap::symbol_table()->LookupTwoCharsSymbolIfExists(c1, c2, &symbol)) {
|
|
|
|
return symbol;
|
|
|
|
// Now we know the length is 2, we might as well make use of that fact
|
|
|
|
// when building the new string.
|
|
|
|
} else if ((c1 | c2) <= String::kMaxAsciiCharCodeU) { // We can do this
|
|
|
|
ASSERT(IsPowerOf2(String::kMaxAsciiCharCodeU + 1)); // because of this.
|
|
|
|
Object* result = Heap::AllocateRawAsciiString(2);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
char* dest = SeqAsciiString::cast(result)->GetChars();
|
|
|
|
dest[0] = c1;
|
|
|
|
dest[1] = c2;
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
Object* result = Heap::AllocateRawTwoByteString(2);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
uc16* dest = SeqTwoByteString::cast(result)->GetChars();
|
|
|
|
dest[0] = c1;
|
|
|
|
dest[1] = c2;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-26 13:09:50 +00:00
|
|
|
Object* Heap::AllocateConsString(String* first, String* second) {
|
2009-03-17 09:33:06 +00:00
|
|
|
int first_length = first->length();
|
2009-11-10 13:23:05 +00:00
|
|
|
if (first_length == 0) {
|
|
|
|
return second;
|
|
|
|
}
|
2009-06-26 13:09:50 +00:00
|
|
|
|
2009-03-17 09:33:06 +00:00
|
|
|
int second_length = second->length();
|
2009-11-10 13:23:05 +00:00
|
|
|
if (second_length == 0) {
|
|
|
|
return first;
|
|
|
|
}
|
2009-06-26 13:09:50 +00:00
|
|
|
|
2008-10-22 09:09:07 +00:00
|
|
|
int length = first_length + second_length;
|
2009-11-20 10:11:45 +00:00
|
|
|
|
|
|
|
// Optimization for 2-byte strings often used as keys in a decompression
|
|
|
|
// dictionary. Check whether we already have the string in the symbol
|
|
|
|
// table to prevent creation of many unneccesary strings.
|
|
|
|
if (length == 2) {
|
|
|
|
unsigned c1 = first->Get(0);
|
|
|
|
unsigned c2 = second->Get(0);
|
|
|
|
return MakeOrFindTwoCharacterString(c1, c2);
|
|
|
|
}
|
|
|
|
|
2009-05-01 11:16:29 +00:00
|
|
|
bool is_ascii = first->IsAsciiRepresentation()
|
|
|
|
&& second->IsAsciiRepresentation();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-06-26 13:09:50 +00:00
|
|
|
// Make sure that an out of memory exception is thrown if the length
|
2009-10-08 12:36:12 +00:00
|
|
|
// of the new cons string is too large.
|
|
|
|
if (length > String::kMaxLength || length < 0) {
|
2009-06-26 13:09:50 +00:00
|
|
|
Top::context()->mark_out_of_memory();
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// If the resulting string is small make a flat string.
|
2008-10-22 09:09:07 +00:00
|
|
|
if (length < String::kMinNonFlatLength) {
|
2009-03-17 09:33:06 +00:00
|
|
|
ASSERT(first->IsFlat());
|
|
|
|
ASSERT(second->IsFlat());
|
2008-10-22 09:09:07 +00:00
|
|
|
if (is_ascii) {
|
|
|
|
Object* result = AllocateRawAsciiString(length);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
// Copy the characters into the new object.
|
|
|
|
char* dest = SeqAsciiString::cast(result)->GetChars();
|
2009-06-26 13:09:50 +00:00
|
|
|
// Copy first part.
|
2009-12-02 12:58:10 +00:00
|
|
|
const char* src;
|
|
|
|
if (first->IsExternalString()) {
|
|
|
|
src = ExternalAsciiString::cast(first)->resource()->data();
|
|
|
|
} else {
|
|
|
|
src = SeqAsciiString::cast(first)->GetChars();
|
|
|
|
}
|
2009-06-26 13:09:50 +00:00
|
|
|
for (int i = 0; i < first_length; i++) *dest++ = src[i];
|
|
|
|
// Copy second part.
|
2009-12-02 12:58:10 +00:00
|
|
|
if (second->IsExternalString()) {
|
|
|
|
src = ExternalAsciiString::cast(second)->resource()->data();
|
|
|
|
} else {
|
|
|
|
src = SeqAsciiString::cast(second)->GetChars();
|
|
|
|
}
|
2009-06-26 13:09:50 +00:00
|
|
|
for (int i = 0; i < second_length; i++) *dest++ = src[i];
|
2008-10-22 09:09:07 +00:00
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
Object* result = AllocateRawTwoByteString(length);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
// Copy the characters into the new object.
|
|
|
|
uc16* dest = SeqTwoByteString::cast(result)->GetChars();
|
2009-03-17 09:33:06 +00:00
|
|
|
String::WriteToFlat(first, dest, 0, first_length);
|
|
|
|
String::WriteToFlat(second, dest + first_length, 0, second_length);
|
2008-10-22 09:09:07 +00:00
|
|
|
return result;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-24 14:10:06 +00:00
|
|
|
Map* map = is_ascii ? cons_ascii_string_map() : cons_string_map();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2010-01-05 11:30:05 +00:00
|
|
|
Object* result = Allocate(map, NEW_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
ConsString* cons_string = ConsString::cast(result);
|
2009-11-11 15:25:51 +00:00
|
|
|
WriteBarrierMode mode = cons_string->GetWriteBarrierMode();
|
2009-11-24 14:10:06 +00:00
|
|
|
cons_string->set_length(length);
|
|
|
|
cons_string->set_hash_field(String::kEmptyHashField);
|
2009-11-11 15:25:51 +00:00
|
|
|
cons_string->set_first(first, mode);
|
|
|
|
cons_string->set_second(second, mode);
|
2008-07-03 15:10:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-03 10:16:05 +00:00
|
|
|
Object* Heap::AllocateSubString(String* buffer,
|
|
|
|
int start,
|
|
|
|
int end) {
|
2008-07-03 15:10:15 +00:00
|
|
|
int length = end - start;
|
|
|
|
|
2008-10-07 13:04:56 +00:00
|
|
|
if (length == 1) {
|
2008-11-03 10:16:05 +00:00
|
|
|
return Heap::LookupSingleCharacterStringFromCode(
|
2009-03-17 09:33:06 +00:00
|
|
|
buffer->Get(start));
|
2009-11-20 10:11:45 +00:00
|
|
|
} else if (length == 2) {
|
|
|
|
// Optimization for 2-byte strings often used as keys in a decompression
|
|
|
|
// dictionary. Check whether we already have the string in the symbol
|
|
|
|
// table to prevent creation of many unneccesary strings.
|
|
|
|
unsigned c1 = buffer->Get(start);
|
|
|
|
unsigned c2 = buffer->Get(start + 1);
|
|
|
|
return MakeOrFindTwoCharacterString(c1, c2);
|
2008-10-07 13:04:56 +00:00
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// Make an attempt to flatten the buffer to reduce access time.
|
2009-03-17 09:33:06 +00:00
|
|
|
if (!buffer->IsFlat()) {
|
|
|
|
buffer->TryFlatten();
|
2008-11-03 10:16:05 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-01 11:16:29 +00:00
|
|
|
Object* result = buffer->IsAsciiRepresentation()
|
2008-07-03 15:10:15 +00:00
|
|
|
? AllocateRawAsciiString(length)
|
|
|
|
: AllocateRawTwoByteString(length);
|
|
|
|
if (result->IsFailure()) return result;
|
2009-11-10 13:23:05 +00:00
|
|
|
String* string_result = String::cast(result);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Copy the characters into the new object.
|
2009-11-10 13:23:05 +00:00
|
|
|
if (buffer->IsAsciiRepresentation()) {
|
|
|
|
ASSERT(string_result->IsAsciiRepresentation());
|
|
|
|
char* dest = SeqAsciiString::cast(string_result)->GetChars();
|
|
|
|
String::WriteToFlat(buffer, dest, start, end);
|
|
|
|
} else {
|
|
|
|
ASSERT(string_result->IsTwoByteRepresentation());
|
|
|
|
uc16* dest = SeqTwoByteString::cast(string_result)->GetChars();
|
|
|
|
String::WriteToFlat(buffer, dest, start, end);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2009-11-10 13:23:05 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateExternalStringFromAscii(
|
|
|
|
ExternalAsciiString::Resource* resource) {
|
2009-11-11 09:50:06 +00:00
|
|
|
size_t length = resource->length();
|
2009-11-24 14:10:06 +00:00
|
|
|
if (length > static_cast<size_t>(String::kMaxLength)) {
|
2009-11-11 09:50:06 +00:00
|
|
|
Top::context()->mark_out_of_memory();
|
|
|
|
return Failure::OutOfMemoryException();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2009-11-24 14:10:06 +00:00
|
|
|
Map* map = external_ascii_string_map();
|
2010-01-05 11:30:05 +00:00
|
|
|
Object* result = Allocate(map, NEW_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
ExternalAsciiString* external_string = ExternalAsciiString::cast(result);
|
2009-11-11 09:50:06 +00:00
|
|
|
external_string->set_length(static_cast<int>(length));
|
2009-11-24 14:10:06 +00:00
|
|
|
external_string->set_hash_field(String::kEmptyHashField);
|
2008-07-03 15:10:15 +00:00
|
|
|
external_string->set_resource(resource);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateExternalStringFromTwoByte(
|
|
|
|
ExternalTwoByteString::Resource* resource) {
|
2009-11-11 09:50:06 +00:00
|
|
|
size_t length = resource->length();
|
|
|
|
if (length > static_cast<size_t>(String::kMaxLength)) {
|
|
|
|
Top::context()->mark_out_of_memory();
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
2009-11-24 14:10:06 +00:00
|
|
|
|
|
|
|
Map* map = Heap::external_string_map();
|
2010-01-05 11:30:05 +00:00
|
|
|
Object* result = Allocate(map, NEW_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
ExternalTwoByteString* external_string = ExternalTwoByteString::cast(result);
|
2009-11-11 09:50:06 +00:00
|
|
|
external_string->set_length(static_cast<int>(length));
|
2009-11-24 14:10:06 +00:00
|
|
|
external_string->set_hash_field(String::kEmptyHashField);
|
2008-07-03 15:10:15 +00:00
|
|
|
external_string->set_resource(resource);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-21 16:28:44 +00:00
|
|
|
Object* Heap::LookupSingleCharacterStringFromCode(uint16_t code) {
|
2008-07-03 15:10:15 +00:00
|
|
|
if (code <= String::kMaxAsciiCharCode) {
|
|
|
|
Object* value = Heap::single_character_string_cache()->get(code);
|
|
|
|
if (value != Heap::undefined_value()) return value;
|
2008-10-21 16:28:44 +00:00
|
|
|
|
|
|
|
char buffer[1];
|
|
|
|
buffer[0] = static_cast<char>(code);
|
|
|
|
Object* result = LookupSymbol(Vector<const char>(buffer, 1));
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
Heap::single_character_string_cache()->set(code, result);
|
|
|
|
return result;
|
|
|
|
}
|
2008-10-21 16:28:44 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* result = Heap::AllocateRawTwoByteString(1);
|
|
|
|
if (result->IsFailure()) return result;
|
2008-11-03 10:16:05 +00:00
|
|
|
String* answer = String::cast(result);
|
2009-03-17 09:33:06 +00:00
|
|
|
answer->Set(0, code);
|
2008-11-03 10:16:05 +00:00
|
|
|
return answer;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-25 11:07:48 +00:00
|
|
|
Object* Heap::AllocateByteArray(int length, PretenureFlag pretenure) {
|
2010-01-07 13:17:18 +00:00
|
|
|
if (length < 0 || length > ByteArray::kMaxLength) {
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
2008-11-25 11:07:48 +00:00
|
|
|
if (pretenure == NOT_TENURED) {
|
|
|
|
return AllocateByteArray(length);
|
|
|
|
}
|
|
|
|
int size = ByteArray::SizeFor(length);
|
2010-01-05 11:30:05 +00:00
|
|
|
Object* result = (size <= MaxObjectSizeInPagedSpace())
|
|
|
|
? old_data_space_->AllocateRaw(size)
|
|
|
|
: lo_space_->AllocateRaw(size);
|
2008-11-25 11:07:48 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
reinterpret_cast<Array*>(result)->set_map(byte_array_map());
|
|
|
|
reinterpret_cast<Array*>(result)->set_length(length);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* Heap::AllocateByteArray(int length) {
|
2010-01-07 13:17:18 +00:00
|
|
|
if (length < 0 || length > ByteArray::kMaxLength) {
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
int size = ByteArray::SizeFor(length);
|
2008-09-05 12:34:09 +00:00
|
|
|
AllocationSpace space =
|
2010-01-05 11:30:05 +00:00
|
|
|
(size > MaxObjectSizeInPagedSpace()) ? LO_SPACE : NEW_SPACE;
|
2008-10-30 09:15:58 +00:00
|
|
|
Object* result = AllocateRaw(size, space, OLD_DATA_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
reinterpret_cast<Array*>(result)->set_map(byte_array_map());
|
|
|
|
reinterpret_cast<Array*>(result)->set_length(length);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-11 23:52:52 +00:00
|
|
|
void Heap::CreateFillerObjectAt(Address addr, int size) {
|
|
|
|
if (size == 0) return;
|
|
|
|
HeapObject* filler = HeapObject::FromAddress(addr);
|
|
|
|
if (size == kPointerSize) {
|
2009-07-09 11:13:08 +00:00
|
|
|
filler->set_map(Heap::one_pointer_filler_map());
|
2009-02-11 23:52:52 +00:00
|
|
|
} else {
|
|
|
|
filler->set_map(Heap::byte_array_map());
|
|
|
|
ByteArray::cast(filler)->set_length(ByteArray::LengthFor(size));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-28 08:43:51 +00:00
|
|
|
Object* Heap::AllocatePixelArray(int length,
|
|
|
|
uint8_t* external_pointer,
|
|
|
|
PretenureFlag pretenure) {
|
|
|
|
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
|
|
|
|
Object* result = AllocateRaw(PixelArray::kAlignedSize, space, OLD_DATA_SPACE);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
reinterpret_cast<PixelArray*>(result)->set_map(pixel_array_map());
|
|
|
|
reinterpret_cast<PixelArray*>(result)->set_length(length);
|
|
|
|
reinterpret_cast<PixelArray*>(result)->set_external_pointer(external_pointer);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-20 15:26:17 +00:00
|
|
|
Object* Heap::AllocateExternalArray(int length,
|
|
|
|
ExternalArrayType array_type,
|
|
|
|
void* external_pointer,
|
|
|
|
PretenureFlag pretenure) {
|
|
|
|
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
|
|
|
|
Object* result = AllocateRaw(ExternalArray::kAlignedSize,
|
|
|
|
space,
|
|
|
|
OLD_DATA_SPACE);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
reinterpret_cast<ExternalArray*>(result)->set_map(
|
|
|
|
MapForExternalArrayType(array_type));
|
|
|
|
reinterpret_cast<ExternalArray*>(result)->set_length(length);
|
|
|
|
reinterpret_cast<ExternalArray*>(result)->set_external_pointer(
|
|
|
|
external_pointer);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* Heap::CreateCode(const CodeDesc& desc,
|
2009-05-18 09:41:16 +00:00
|
|
|
ZoneScopeInfo* sinfo,
|
2008-11-25 11:07:48 +00:00
|
|
|
Code::Flags flags,
|
2009-02-25 16:52:15 +00:00
|
|
|
Handle<Object> self_reference) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// Compute size
|
|
|
|
int body_size = RoundUp(desc.instr_size + desc.reloc_size, kObjectAlignment);
|
|
|
|
int sinfo_size = 0;
|
|
|
|
if (sinfo != NULL) sinfo_size = sinfo->Serialize(NULL);
|
|
|
|
int obj_size = Code::SizeFor(body_size, sinfo_size);
|
2009-02-27 11:19:31 +00:00
|
|
|
ASSERT(IsAligned(obj_size, Code::kCodeAlignment));
|
2008-09-05 12:34:09 +00:00
|
|
|
Object* result;
|
2009-06-18 14:06:36 +00:00
|
|
|
if (obj_size > MaxObjectSizeInPagedSpace()) {
|
2008-09-05 12:34:09 +00:00
|
|
|
result = lo_space_->AllocateRawCode(obj_size);
|
|
|
|
} else {
|
|
|
|
result = code_space_->AllocateRaw(obj_size);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
// Initialize the object
|
|
|
|
HeapObject::cast(result)->set_map(code_map());
|
|
|
|
Code* code = Code::cast(result);
|
2009-10-05 11:16:25 +00:00
|
|
|
ASSERT(!CodeRange::exists() || CodeRange::contains(code->address()));
|
2008-07-03 15:10:15 +00:00
|
|
|
code->set_instruction_size(desc.instr_size);
|
|
|
|
code->set_relocation_size(desc.reloc_size);
|
|
|
|
code->set_sinfo_size(sinfo_size);
|
|
|
|
code->set_flags(flags);
|
2009-02-25 16:52:15 +00:00
|
|
|
// Allow self references to created code object by patching the handle to
|
|
|
|
// point to the newly allocated Code object.
|
|
|
|
if (!self_reference.is_null()) {
|
|
|
|
*(self_reference.location()) = code;
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
// Migrate generated code.
|
|
|
|
// The generated code can contain Object** values (typically from handles)
|
|
|
|
// that are dereferenced during the copy to point directly to the actual heap
|
|
|
|
// objects. These pointers can include references to the code object itself,
|
|
|
|
// through the self_reference parameter.
|
|
|
|
code->CopyFrom(desc);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (sinfo != NULL) sinfo->Serialize(code); // write scope info
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
code->Verify();
|
|
|
|
#endif
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::CopyCode(Code* code) {
|
|
|
|
// Allocate an object the same size as the code object.
|
|
|
|
int obj_size = code->Size();
|
2008-09-05 12:34:09 +00:00
|
|
|
Object* result;
|
2009-06-18 14:06:36 +00:00
|
|
|
if (obj_size > MaxObjectSizeInPagedSpace()) {
|
2008-09-05 12:34:09 +00:00
|
|
|
result = lo_space_->AllocateRawCode(obj_size);
|
|
|
|
} else {
|
|
|
|
result = code_space_->AllocateRaw(obj_size);
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
// Copy code object.
|
|
|
|
Address old_addr = code->address();
|
|
|
|
Address new_addr = reinterpret_cast<HeapObject*>(result)->address();
|
2008-10-22 08:21:18 +00:00
|
|
|
CopyBlock(reinterpret_cast<Object**>(new_addr),
|
|
|
|
reinterpret_cast<Object**>(old_addr),
|
|
|
|
obj_size);
|
2008-07-03 15:10:15 +00:00
|
|
|
// Relocate the copy.
|
|
|
|
Code* new_code = Code::cast(result);
|
2009-10-05 11:16:25 +00:00
|
|
|
ASSERT(!CodeRange::exists() || CodeRange::contains(code->address()));
|
2008-07-03 15:10:15 +00:00
|
|
|
new_code->Relocate(new_addr - old_addr);
|
|
|
|
return new_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::Allocate(Map* map, AllocationSpace space) {
|
|
|
|
ASSERT(gc_state_ == NOT_IN_GC);
|
|
|
|
ASSERT(map->instance_type() != MAP_TYPE);
|
2009-12-22 13:34:02 +00:00
|
|
|
// If allocation failures are disallowed, we may allocate in a different
|
|
|
|
// space when new space is full and the object is not a large object.
|
|
|
|
AllocationSpace retry_space =
|
|
|
|
(space != NEW_SPACE) ? space : TargetSpaceId(map->instance_type());
|
|
|
|
Object* result =
|
|
|
|
AllocateRaw(map->instance_size(), space, retry_space);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
HeapObject::cast(result)->set_map(map);
|
2009-10-21 13:04:53 +00:00
|
|
|
#ifdef ENABLE_LOGGING_AND_PROFILING
|
2009-10-15 07:50:23 +00:00
|
|
|
ProducerHeapProfile::RecordJSObjectAllocation(result);
|
2009-10-21 13:04:53 +00:00
|
|
|
#endif
|
2008-07-03 15:10:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::InitializeFunction(JSFunction* function,
|
|
|
|
SharedFunctionInfo* shared,
|
|
|
|
Object* prototype) {
|
|
|
|
ASSERT(!prototype->IsMap());
|
|
|
|
function->initialize_properties();
|
|
|
|
function->initialize_elements();
|
|
|
|
function->set_shared(shared);
|
|
|
|
function->set_prototype_or_initial_map(prototype);
|
|
|
|
function->set_context(undefined_value());
|
2008-10-23 14:55:45 +00:00
|
|
|
function->set_literals(empty_fixed_array(), SKIP_WRITE_BARRIER);
|
2008-07-03 15:10:15 +00:00
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateFunctionPrototype(JSFunction* function) {
|
2009-01-26 15:09:44 +00:00
|
|
|
// Allocate the prototype. Make sure to use the object function
|
|
|
|
// from the function's context, since the function can be from a
|
|
|
|
// different context.
|
|
|
|
JSFunction* object_function =
|
|
|
|
function->context()->global_context()->object_function();
|
|
|
|
Object* prototype = AllocateJSObject(object_function);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (prototype->IsFailure()) return prototype;
|
|
|
|
// When creating the prototype for the function we must set its
|
|
|
|
// constructor to the function.
|
|
|
|
Object* result =
|
|
|
|
JSObject::cast(prototype)->SetProperty(constructor_symbol(),
|
|
|
|
function,
|
|
|
|
DONT_ENUM);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
return prototype;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateFunction(Map* function_map,
|
|
|
|
SharedFunctionInfo* shared,
|
2009-12-16 15:43:20 +00:00
|
|
|
Object* prototype,
|
|
|
|
PretenureFlag pretenure) {
|
|
|
|
AllocationSpace space =
|
|
|
|
(pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
|
|
|
|
Object* result = Allocate(function_map, space);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
return InitializeFunction(JSFunction::cast(result), shared, prototype);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateArgumentsObject(Object* callee, int length) {
|
2008-08-22 13:33:59 +00:00
|
|
|
// To get fast allocation and map sharing for arguments objects we
|
|
|
|
// allocate them based on an arguments boilerplate.
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// This calls Copy directly rather than using Heap::AllocateRaw so we
|
|
|
|
// duplicate the check here.
|
|
|
|
ASSERT(allocation_allowed_ && gc_state_ == NOT_IN_GC);
|
|
|
|
|
|
|
|
JSObject* boilerplate =
|
|
|
|
Top::context()->global_context()->arguments_boilerplate();
|
2008-10-23 08:46:32 +00:00
|
|
|
|
2009-12-17 15:35:15 +00:00
|
|
|
// Check that the size of the boilerplate matches our
|
|
|
|
// expectations. The ArgumentsAccessStub::GenerateNewObject relies
|
|
|
|
// on the size being a known constant.
|
|
|
|
ASSERT(kArgumentsObjectSize == boilerplate->map()->instance_size());
|
|
|
|
|
|
|
|
// Do the allocation.
|
|
|
|
Object* result =
|
|
|
|
AllocateRaw(kArgumentsObjectSize, NEW_SPACE, OLD_POINTER_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
2008-10-23 08:46:32 +00:00
|
|
|
|
2008-10-30 09:15:58 +00:00
|
|
|
// Copy the content. The arguments boilerplate doesn't have any
|
|
|
|
// fields that point to new space so it's safe to skip the write
|
|
|
|
// barrier here.
|
2008-10-23 08:46:32 +00:00
|
|
|
CopyBlock(reinterpret_cast<Object**>(HeapObject::cast(result)->address()),
|
|
|
|
reinterpret_cast<Object**>(boilerplate->address()),
|
2009-12-17 15:35:15 +00:00
|
|
|
kArgumentsObjectSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-10-23 08:46:32 +00:00
|
|
|
// Set the two properties.
|
|
|
|
JSObject::cast(result)->InObjectPropertyAtPut(arguments_callee_index,
|
2008-10-30 09:15:58 +00:00
|
|
|
callee);
|
2008-10-23 08:46:32 +00:00
|
|
|
JSObject::cast(result)->InObjectPropertyAtPut(arguments_length_index,
|
|
|
|
Smi::FromInt(length),
|
|
|
|
SKIP_WRITE_BARRIER);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Check the state of the object
|
|
|
|
ASSERT(JSObject::cast(result)->HasFastProperties());
|
|
|
|
ASSERT(JSObject::cast(result)->HasFastElements());
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateInitialMap(JSFunction* fun) {
|
|
|
|
ASSERT(!fun->has_initial_map());
|
|
|
|
|
2009-08-19 07:30:20 +00:00
|
|
|
// First create a new map with the size and number of in-object properties
|
|
|
|
// suggested by the function.
|
|
|
|
int instance_size = fun->shared()->CalculateInstanceSize();
|
|
|
|
int in_object_properties = fun->shared()->CalculateInObjectProperties();
|
2008-10-16 05:45:33 +00:00
|
|
|
Object* map_obj = Heap::AllocateMap(JS_OBJECT_TYPE, instance_size);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (map_obj->IsFailure()) return map_obj;
|
|
|
|
|
|
|
|
// Fetch or allocate prototype.
|
|
|
|
Object* prototype;
|
|
|
|
if (fun->has_instance_prototype()) {
|
|
|
|
prototype = fun->instance_prototype();
|
|
|
|
} else {
|
|
|
|
prototype = AllocateFunctionPrototype(fun);
|
|
|
|
if (prototype->IsFailure()) return prototype;
|
|
|
|
}
|
|
|
|
Map* map = Map::cast(map_obj);
|
2009-08-19 07:30:20 +00:00
|
|
|
map->set_inobject_properties(in_object_properties);
|
|
|
|
map->set_unused_property_fields(in_object_properties);
|
2008-07-03 15:10:15 +00:00
|
|
|
map->set_prototype(prototype);
|
2009-08-19 07:30:20 +00:00
|
|
|
|
|
|
|
// If the function has only simple this property assignments add field
|
|
|
|
// descriptors for these to the initial map as the object cannot be
|
|
|
|
// constructed without having these properties.
|
|
|
|
ASSERT(in_object_properties <= Map::kMaxPreAllocatedPropertyFields);
|
2009-10-23 12:54:48 +00:00
|
|
|
if (fun->shared()->has_only_simple_this_property_assignments() &&
|
|
|
|
fun->shared()->this_property_assignments_count() > 0) {
|
2009-08-19 07:30:20 +00:00
|
|
|
int count = fun->shared()->this_property_assignments_count();
|
|
|
|
if (count > in_object_properties) {
|
|
|
|
count = in_object_properties;
|
|
|
|
}
|
2009-08-21 08:44:21 +00:00
|
|
|
Object* descriptors_obj = DescriptorArray::Allocate(count);
|
|
|
|
if (descriptors_obj->IsFailure()) return descriptors_obj;
|
|
|
|
DescriptorArray* descriptors = DescriptorArray::cast(descriptors_obj);
|
2009-08-19 07:30:20 +00:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
String* name = fun->shared()->GetThisPropertyAssignmentName(i);
|
|
|
|
ASSERT(name->IsSymbol());
|
|
|
|
FieldDescriptor field(name, i, NONE);
|
|
|
|
descriptors->Set(i, &field);
|
|
|
|
}
|
|
|
|
descriptors->Sort();
|
|
|
|
map->set_instance_descriptors(descriptors);
|
|
|
|
map->set_pre_allocated_property_fields(count);
|
|
|
|
map->set_unused_property_fields(in_object_properties - count);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::InitializeJSObjectFromMap(JSObject* obj,
|
|
|
|
FixedArray* properties,
|
|
|
|
Map* map) {
|
|
|
|
obj->set_properties(properties);
|
|
|
|
obj->initialize_elements();
|
|
|
|
// TODO(1240798): Initialize the object's body using valid initial values
|
|
|
|
// according to the object's initial map. For example, if the map's
|
|
|
|
// instance type is JS_ARRAY_TYPE, the length field should be initialized
|
|
|
|
// to a number (eg, Smi::FromInt(0)) and the elements initialized to a
|
|
|
|
// fixed array (eg, Heap::empty_fixed_array()). Currently, the object
|
|
|
|
// verification code has to cope with (temporarily) invalid objects. See
|
|
|
|
// for example, JSArray::JSArrayVerify).
|
|
|
|
obj->InitializeBody(map->instance_size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) {
|
|
|
|
// JSFunctions should be allocated using AllocateFunction to be
|
|
|
|
// properly initialized.
|
|
|
|
ASSERT(map->instance_type() != JS_FUNCTION_TYPE);
|
|
|
|
|
2009-07-30 07:33:05 +00:00
|
|
|
// Both types of globla objects should be allocated using
|
|
|
|
// AllocateGloblaObject to be properly initialized.
|
|
|
|
ASSERT(map->instance_type() != JS_GLOBAL_OBJECT_TYPE);
|
|
|
|
ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE);
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// Allocate the backing storage for the properties.
|
2009-08-19 07:30:20 +00:00
|
|
|
int prop_size =
|
|
|
|
map->pre_allocated_property_fields() +
|
|
|
|
map->unused_property_fields() -
|
|
|
|
map->inobject_properties();
|
|
|
|
ASSERT(prop_size >= 0);
|
2009-07-01 11:44:37 +00:00
|
|
|
Object* properties = AllocateFixedArray(prop_size, pretenure);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (properties->IsFailure()) return properties;
|
|
|
|
|
|
|
|
// Allocate the JSObject.
|
2008-09-05 12:34:09 +00:00
|
|
|
AllocationSpace space =
|
|
|
|
(pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
|
2009-06-18 14:06:36 +00:00
|
|
|
if (map->instance_size() > MaxObjectSizeInPagedSpace()) space = LO_SPACE;
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* obj = Allocate(map, space);
|
|
|
|
if (obj->IsFailure()) return obj;
|
|
|
|
|
|
|
|
// Initialize the JSObject.
|
|
|
|
InitializeJSObjectFromMap(JSObject::cast(obj),
|
|
|
|
FixedArray::cast(properties),
|
|
|
|
map);
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateJSObject(JSFunction* constructor,
|
|
|
|
PretenureFlag pretenure) {
|
|
|
|
// Allocate the initial map if absent.
|
|
|
|
if (!constructor->has_initial_map()) {
|
|
|
|
Object* initial_map = AllocateInitialMap(constructor);
|
|
|
|
if (initial_map->IsFailure()) return initial_map;
|
|
|
|
constructor->set_initial_map(Map::cast(initial_map));
|
|
|
|
Map::cast(initial_map)->set_constructor(constructor);
|
|
|
|
}
|
|
|
|
// Allocate the object based on the constructors initial map.
|
2009-06-30 10:05:36 +00:00
|
|
|
Object* result =
|
|
|
|
AllocateJSObjectFromMap(constructor->initial_map(), pretenure);
|
2009-07-01 11:44:37 +00:00
|
|
|
// Make sure result is NOT a global object if valid.
|
|
|
|
ASSERT(result->IsFailure() || !result->IsGlobalObject());
|
2009-06-30 10:05:36 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-01 11:44:37 +00:00
|
|
|
Object* Heap::AllocateGlobalObject(JSFunction* constructor) {
|
2009-06-30 10:05:36 +00:00
|
|
|
ASSERT(constructor->has_initial_map());
|
2009-07-30 07:33:05 +00:00
|
|
|
Map* map = constructor->initial_map();
|
|
|
|
|
2009-06-30 10:05:36 +00:00
|
|
|
// Make sure no field properties are described in the initial map.
|
|
|
|
// This guarantees us that normalizing the properties does not
|
|
|
|
// require us to change property values to JSGlobalPropertyCells.
|
2009-07-30 07:33:05 +00:00
|
|
|
ASSERT(map->NextFreePropertyIndex() == 0);
|
2009-06-30 10:05:36 +00:00
|
|
|
|
2009-07-01 11:44:37 +00:00
|
|
|
// Make sure we don't have a ton of pre-allocated slots in the
|
|
|
|
// global objects. They will be unused once we normalize the object.
|
2009-07-30 07:33:05 +00:00
|
|
|
ASSERT(map->unused_property_fields() == 0);
|
|
|
|
ASSERT(map->inobject_properties() == 0);
|
|
|
|
|
|
|
|
// Initial size of the backing store to avoid resize of the storage during
|
|
|
|
// bootstrapping. The size differs between the JS global object ad the
|
|
|
|
// builtins object.
|
|
|
|
int initial_size = map->instance_type() == JS_GLOBAL_OBJECT_TYPE ? 64 : 512;
|
|
|
|
|
|
|
|
// Allocate a dictionary object for backing storage.
|
|
|
|
Object* obj =
|
|
|
|
StringDictionary::Allocate(
|
|
|
|
map->NumberOfDescribedProperties() * 2 + initial_size);
|
|
|
|
if (obj->IsFailure()) return obj;
|
|
|
|
StringDictionary* dictionary = StringDictionary::cast(obj);
|
|
|
|
|
|
|
|
// The global object might be created from an object template with accessors.
|
|
|
|
// Fill these accessors into the dictionary.
|
|
|
|
DescriptorArray* descs = map->instance_descriptors();
|
|
|
|
for (int i = 0; i < descs->number_of_descriptors(); i++) {
|
|
|
|
PropertyDetails details = descs->GetDetails(i);
|
|
|
|
ASSERT(details.type() == CALLBACKS); // Only accessors are expected.
|
|
|
|
PropertyDetails d =
|
|
|
|
PropertyDetails(details.attributes(), CALLBACKS, details.index());
|
|
|
|
Object* value = descs->GetCallbacksObject(i);
|
|
|
|
value = Heap::AllocateJSGlobalPropertyCell(value);
|
|
|
|
if (value->IsFailure()) return value;
|
|
|
|
|
|
|
|
Object* result = dictionary->Add(descs->GetKey(i), value, d);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
dictionary = StringDictionary::cast(result);
|
|
|
|
}
|
2009-07-01 11:44:37 +00:00
|
|
|
|
2009-07-30 07:33:05 +00:00
|
|
|
// Allocate the global object and initialize it with the backing store.
|
|
|
|
obj = Allocate(map, OLD_POINTER_SPACE);
|
|
|
|
if (obj->IsFailure()) return obj;
|
|
|
|
JSObject* global = JSObject::cast(obj);
|
|
|
|
InitializeJSObjectFromMap(global, dictionary, map);
|
2009-06-30 10:05:36 +00:00
|
|
|
|
2009-07-30 07:33:05 +00:00
|
|
|
// Create a new map for the global object.
|
|
|
|
obj = map->CopyDropDescriptors();
|
|
|
|
if (obj->IsFailure()) return obj;
|
|
|
|
Map* new_map = Map::cast(obj);
|
|
|
|
|
|
|
|
// Setup the global object as a normalized object.
|
|
|
|
global->set_map(new_map);
|
|
|
|
global->map()->set_instance_descriptors(Heap::empty_descriptor_array());
|
|
|
|
global->set_properties(dictionary);
|
2009-06-30 10:05:36 +00:00
|
|
|
|
2009-07-01 11:44:37 +00:00
|
|
|
// Make sure result is a global object with properties in dictionary.
|
|
|
|
ASSERT(global->IsGlobalObject());
|
2009-06-30 10:05:36 +00:00
|
|
|
ASSERT(!global->HasFastProperties());
|
|
|
|
return global;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-22 08:21:18 +00:00
|
|
|
Object* Heap::CopyJSObject(JSObject* source) {
|
|
|
|
// Never used to copy functions. If functions need to be copied we
|
|
|
|
// have to be careful to clear the literals array.
|
|
|
|
ASSERT(!source->IsJSFunction());
|
|
|
|
|
|
|
|
// Make the clone.
|
|
|
|
Map* map = source->map();
|
|
|
|
int object_size = map->instance_size();
|
2008-10-30 09:15:58 +00:00
|
|
|
Object* clone;
|
|
|
|
|
|
|
|
// If we're forced to always allocate, we use the general allocation
|
|
|
|
// functions which may leave us with an object in old space.
|
|
|
|
if (always_allocate()) {
|
|
|
|
clone = AllocateRaw(object_size, NEW_SPACE, OLD_POINTER_SPACE);
|
|
|
|
if (clone->IsFailure()) return clone;
|
|
|
|
Address clone_address = HeapObject::cast(clone)->address();
|
|
|
|
CopyBlock(reinterpret_cast<Object**>(clone_address),
|
|
|
|
reinterpret_cast<Object**>(source->address()),
|
|
|
|
object_size);
|
|
|
|
// Update write barrier for all fields that lie beyond the header.
|
|
|
|
for (int offset = JSObject::kHeaderSize;
|
|
|
|
offset < object_size;
|
|
|
|
offset += kPointerSize) {
|
|
|
|
RecordWrite(clone_address, offset);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
clone = new_space_.AllocateRaw(object_size);
|
|
|
|
if (clone->IsFailure()) return clone;
|
|
|
|
ASSERT(Heap::InNewSpace(clone));
|
|
|
|
// Since we know the clone is allocated in new space, we can copy
|
2009-01-15 19:08:34 +00:00
|
|
|
// the contents without worrying about updating the write barrier.
|
2008-10-30 09:15:58 +00:00
|
|
|
CopyBlock(reinterpret_cast<Object**>(HeapObject::cast(clone)->address()),
|
|
|
|
reinterpret_cast<Object**>(source->address()),
|
|
|
|
object_size);
|
|
|
|
}
|
2008-10-22 08:21:18 +00:00
|
|
|
|
|
|
|
FixedArray* elements = FixedArray::cast(source->elements());
|
|
|
|
FixedArray* properties = FixedArray::cast(source->properties());
|
|
|
|
// Update elements if necessary.
|
|
|
|
if (elements->length()> 0) {
|
2008-10-22 09:47:20 +00:00
|
|
|
Object* elem = CopyFixedArray(elements);
|
2008-10-22 08:21:18 +00:00
|
|
|
if (elem->IsFailure()) return elem;
|
|
|
|
JSObject::cast(clone)->set_elements(FixedArray::cast(elem));
|
|
|
|
}
|
|
|
|
// Update properties if necessary.
|
|
|
|
if (properties->length() > 0) {
|
2008-10-22 09:47:20 +00:00
|
|
|
Object* prop = CopyFixedArray(properties);
|
2008-10-22 08:21:18 +00:00
|
|
|
if (prop->IsFailure()) return prop;
|
|
|
|
JSObject::cast(clone)->set_properties(FixedArray::cast(prop));
|
|
|
|
}
|
|
|
|
// Return the new clone.
|
2009-10-21 13:04:53 +00:00
|
|
|
#ifdef ENABLE_LOGGING_AND_PROFILING
|
2009-10-15 07:50:23 +00:00
|
|
|
ProducerHeapProfile::RecordJSObjectAllocation(clone);
|
2009-10-21 13:04:53 +00:00
|
|
|
#endif
|
2008-10-22 08:21:18 +00:00
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Split window support from V8.
Here is a description of the background and design of split window in Chrome and V8:
https://docs.google.com/a/google.com/Doc?id=chhjkpg_47fwddxbfr
This change list splits the window object into two parts: 1) an inner window object used as the global object of contexts; 2) an outer window object exposed to JavaScript and accessible by the name 'window'. Firefox did it awhile ago, here are some discussions: https://wiki.mozilla.org/Gecko:SplitWindow. One additional benefit of splitting window in Chrome is that accessing global variables don't need security checks anymore, it can improve applications that use many global variables.
V8 support of split window:
There are a small number of changes on V8 api to support split window:
Security context is removed from V8, so does related API functions;
A global object can be detached from its context and reused by a new context;
Access checks on an object template can be turned on/off by default;
An object can turn on its access checks later;
V8 has a new object type, ApiGlobalObject, which is the outer window object type. The existing JSGlobalObject becomes the inner window object type. Security checks are moved from JSGlobalObject to ApiGlobalObject. ApiGlobalObject is the one exposed to JavaScript, it is accessible through Context::Global(). ApiGlobalObject's prototype is set to JSGlobalObject so that property lookups are forwarded to JSGlobalObject. ApiGlobalObject forwards all other property access requests to JSGlobalObject, such as SetProperty, DeleteProperty, etc.
Security token is moved to a global context, and ApiGlobalObject has a reference to its global context. JSGlobalObject has a reference to its global context as well. When accessing properties on a global object in JavaScript, the domain security check is performed by comparing the security token of the lexical context (Top::global_context()) to the token of global object's context. The check is only needed when the receiver is a window object, such as 'window.document'. Accessing global variables, such as 'var foo = 3; foo' does not need checks because the receiver is the inner window object.
When an outer window is detached from its global context (when a frame navigates away from a page), it is completely detached from the inner window. A new context is created for the new page, and the outer global object is reused. At this point, the access check on the DOMWindow wrapper of the old context is turned on. The code in old context is still able to access DOMWindow properties, but it has to go through domain security checks.
It is debatable on how to implement the outer window object. Currently each property access function has to check if the receiver is ApiGlobalObject type. This approach might be error-prone that one may forget to check the receiver when adding new functions. It is unlikely a performance issue because accessing global variables are more common than 'window.foo' style coding.
I am still working on the ARM port, and I'd like to hear comments and suggestions on the best way to support it in V8.
Review URL: http://codereview.chromium.org/7366
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@540 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2008-10-21 19:07:58 +00:00
|
|
|
Object* Heap::ReinitializeJSGlobalProxy(JSFunction* constructor,
|
|
|
|
JSGlobalProxy* object) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// Allocate initial map if absent.
|
|
|
|
if (!constructor->has_initial_map()) {
|
|
|
|
Object* initial_map = AllocateInitialMap(constructor);
|
|
|
|
if (initial_map->IsFailure()) return initial_map;
|
|
|
|
constructor->set_initial_map(Map::cast(initial_map));
|
|
|
|
Map::cast(initial_map)->set_constructor(constructor);
|
|
|
|
}
|
|
|
|
|
|
|
|
Map* map = constructor->initial_map();
|
|
|
|
|
|
|
|
// Check that the already allocated object has the same size as
|
|
|
|
// objects allocated using the constructor.
|
|
|
|
ASSERT(map->instance_size() == object->map()->instance_size());
|
|
|
|
|
|
|
|
// Allocate the backing storage for the properties.
|
2008-10-15 06:03:26 +00:00
|
|
|
int prop_size = map->unused_property_fields() - map->inobject_properties();
|
2009-07-01 11:44:37 +00:00
|
|
|
Object* properties = AllocateFixedArray(prop_size, TENURED);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (properties->IsFailure()) return properties;
|
|
|
|
|
|
|
|
// Reset the map for the object.
|
|
|
|
object->set_map(constructor->initial_map());
|
|
|
|
|
|
|
|
// Reinitialize the object from the constructor map.
|
|
|
|
InitializeJSObjectFromMap(object, FixedArray::cast(properties), map);
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateStringFromAscii(Vector<const char> string,
|
|
|
|
PretenureFlag pretenure) {
|
|
|
|
Object* result = AllocateRawAsciiString(string.length(), pretenure);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
// Copy the characters into the new object.
|
2008-10-09 08:08:04 +00:00
|
|
|
SeqAsciiString* string_result = SeqAsciiString::cast(result);
|
2008-07-03 15:10:15 +00:00
|
|
|
for (int i = 0; i < string.length(); i++) {
|
2008-10-09 08:08:04 +00:00
|
|
|
string_result->SeqAsciiStringSet(i, string[i]);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateStringFromUtf8(Vector<const char> string,
|
|
|
|
PretenureFlag pretenure) {
|
|
|
|
// Count the number of characters in the UTF-8 string and check if
|
|
|
|
// it is an ASCII string.
|
|
|
|
Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
|
|
|
|
decoder->Reset(string.start(), string.length());
|
|
|
|
int chars = 0;
|
|
|
|
bool is_ascii = true;
|
|
|
|
while (decoder->has_more()) {
|
|
|
|
uc32 r = decoder->GetNext();
|
|
|
|
if (r > String::kMaxAsciiCharCode) is_ascii = false;
|
|
|
|
chars++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the string is ascii, we do not need to convert the characters
|
|
|
|
// since UTF8 is backwards compatible with ascii.
|
|
|
|
if (is_ascii) return AllocateStringFromAscii(string, pretenure);
|
|
|
|
|
|
|
|
Object* result = AllocateRawTwoByteString(chars, pretenure);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
// Convert and copy the characters into the new object.
|
|
|
|
String* string_result = String::cast(result);
|
|
|
|
decoder->Reset(string.start(), string.length());
|
|
|
|
for (int i = 0; i < chars; i++) {
|
|
|
|
uc32 r = decoder->GetNext();
|
2009-03-17 09:33:06 +00:00
|
|
|
string_result->Set(i, r);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateStringFromTwoByte(Vector<const uc16> string,
|
|
|
|
PretenureFlag pretenure) {
|
|
|
|
// Check if the string is an ASCII string.
|
|
|
|
int i = 0;
|
|
|
|
while (i < string.length() && string[i] <= String::kMaxAsciiCharCode) i++;
|
|
|
|
|
|
|
|
Object* result;
|
|
|
|
if (i == string.length()) { // It's an ASCII string.
|
|
|
|
result = AllocateRawAsciiString(string.length(), pretenure);
|
|
|
|
} else { // It's not an ASCII string.
|
|
|
|
result = AllocateRawTwoByteString(string.length(), pretenure);
|
|
|
|
}
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
// Copy the characters into the new object, which may be either ASCII or
|
|
|
|
// UTF-16.
|
|
|
|
String* string_result = String::cast(result);
|
|
|
|
for (int i = 0; i < string.length(); i++) {
|
2009-03-17 09:33:06 +00:00
|
|
|
string_result->Set(i, string[i]);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Map* Heap::SymbolMapForString(String* string) {
|
|
|
|
// If the string is in new space it cannot be used as a symbol.
|
|
|
|
if (InNewSpace(string)) return NULL;
|
|
|
|
|
|
|
|
// Find the corresponding symbol map for strings.
|
|
|
|
Map* map = string->map();
|
2009-11-24 14:10:06 +00:00
|
|
|
if (map == ascii_string_map()) return ascii_symbol_map();
|
|
|
|
if (map == string_map()) return symbol_map();
|
|
|
|
if (map == cons_string_map()) return cons_symbol_map();
|
|
|
|
if (map == cons_ascii_string_map()) return cons_ascii_symbol_map();
|
|
|
|
if (map == external_string_map()) return external_symbol_map();
|
|
|
|
if (map == external_ascii_string_map()) return external_ascii_symbol_map();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// No match found.
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-17 15:13:40 +00:00
|
|
|
Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer,
|
|
|
|
int chars,
|
2009-11-24 14:10:06 +00:00
|
|
|
uint32_t hash_field) {
|
2010-01-07 13:17:18 +00:00
|
|
|
ASSERT(chars >= 0);
|
2008-07-03 15:10:15 +00:00
|
|
|
// Ensure the chars matches the number of characters in the buffer.
|
|
|
|
ASSERT(static_cast<unsigned>(chars) == buffer->Length());
|
|
|
|
// Determine whether the string is ascii.
|
|
|
|
bool is_ascii = true;
|
2010-01-07 13:17:18 +00:00
|
|
|
while (buffer->has_more()) {
|
|
|
|
if (buffer->GetNext() > unibrow::Utf8::kMaxOneByteChar) {
|
|
|
|
is_ascii = false;
|
|
|
|
break;
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
buffer->Rewind();
|
|
|
|
|
|
|
|
// Compute map and object size.
|
|
|
|
int size;
|
|
|
|
Map* map;
|
|
|
|
|
|
|
|
if (is_ascii) {
|
2010-01-07 13:17:18 +00:00
|
|
|
if (chars > SeqAsciiString::kMaxLength) {
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
2009-11-24 14:10:06 +00:00
|
|
|
map = ascii_symbol_map();
|
2008-10-09 08:08:04 +00:00
|
|
|
size = SeqAsciiString::SizeFor(chars);
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
2010-01-07 13:17:18 +00:00
|
|
|
if (chars > SeqTwoByteString::kMaxLength) {
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
2009-11-24 14:10:06 +00:00
|
|
|
map = symbol_map();
|
2008-10-09 08:08:04 +00:00
|
|
|
size = SeqTwoByteString::SizeFor(chars);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate string.
|
2010-01-05 11:30:05 +00:00
|
|
|
Object* result = (size > MaxObjectSizeInPagedSpace())
|
|
|
|
? lo_space_->AllocateRaw(size)
|
|
|
|
: old_data_space_->AllocateRaw(size);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
reinterpret_cast<HeapObject*>(result)->set_map(map);
|
2009-11-24 14:10:06 +00:00
|
|
|
// Set length and hash fields of the allocated string.
|
2008-11-03 10:16:05 +00:00
|
|
|
String* answer = String::cast(result);
|
2009-11-24 14:10:06 +00:00
|
|
|
answer->set_length(chars);
|
|
|
|
answer->set_hash_field(hash_field);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-11-03 10:16:05 +00:00
|
|
|
ASSERT_EQ(size, answer->Size());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Fill in the characters.
|
|
|
|
for (int i = 0; i < chars; i++) {
|
2009-03-17 09:33:06 +00:00
|
|
|
answer->Set(i, buffer->GetNext());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2008-11-03 10:16:05 +00:00
|
|
|
return answer;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateRawAsciiString(int length, PretenureFlag pretenure) {
|
2010-01-07 13:17:18 +00:00
|
|
|
if (length < 0 || length > SeqAsciiString::kMaxLength) {
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
|
|
|
|
2008-10-09 08:08:04 +00:00
|
|
|
int size = SeqAsciiString::SizeFor(length);
|
2010-01-07 13:17:18 +00:00
|
|
|
ASSERT(size <= SeqAsciiString::kMaxSize);
|
|
|
|
|
2010-01-05 11:30:05 +00:00
|
|
|
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
|
|
|
|
AllocationSpace retry_space = OLD_DATA_SPACE;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-06-18 14:06:36 +00:00
|
|
|
if (space == NEW_SPACE) {
|
2010-01-05 11:30:05 +00:00
|
|
|
if (size > kMaxObjectSizeInNewSpace) {
|
|
|
|
// Allocate in large object space, retry space will be ignored.
|
|
|
|
space = LO_SPACE;
|
|
|
|
} else if (size > MaxObjectSizeInPagedSpace()) {
|
|
|
|
// Allocate in new space, retry in large object space.
|
|
|
|
retry_space = LO_SPACE;
|
|
|
|
}
|
|
|
|
} else if (space == OLD_DATA_SPACE && size > MaxObjectSizeInPagedSpace()) {
|
|
|
|
space = LO_SPACE;
|
2009-06-18 14:06:36 +00:00
|
|
|
}
|
2010-01-05 11:30:05 +00:00
|
|
|
Object* result = AllocateRaw(size, space, retry_space);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
// Partially initialize the object.
|
2009-11-24 14:10:06 +00:00
|
|
|
HeapObject::cast(result)->set_map(ascii_string_map());
|
2008-07-03 15:10:15 +00:00
|
|
|
String::cast(result)->set_length(length);
|
2009-11-24 14:10:06 +00:00
|
|
|
String::cast(result)->set_hash_field(String::kEmptyHashField);
|
2008-07-03 15:10:15 +00:00
|
|
|
ASSERT_EQ(size, HeapObject::cast(result)->Size());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateRawTwoByteString(int length, PretenureFlag pretenure) {
|
2010-01-07 13:17:18 +00:00
|
|
|
if (length < 0 || length > SeqTwoByteString::kMaxLength) {
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
2008-10-09 08:08:04 +00:00
|
|
|
int size = SeqTwoByteString::SizeFor(length);
|
2010-01-07 13:17:18 +00:00
|
|
|
ASSERT(size <= SeqTwoByteString::kMaxSize);
|
2010-01-05 11:30:05 +00:00
|
|
|
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
|
|
|
|
AllocationSpace retry_space = OLD_DATA_SPACE;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-06-18 14:06:36 +00:00
|
|
|
if (space == NEW_SPACE) {
|
2010-01-05 11:30:05 +00:00
|
|
|
if (size > kMaxObjectSizeInNewSpace) {
|
|
|
|
// Allocate in large object space, retry space will be ignored.
|
|
|
|
space = LO_SPACE;
|
|
|
|
} else if (size > MaxObjectSizeInPagedSpace()) {
|
|
|
|
// Allocate in new space, retry in large object space.
|
|
|
|
retry_space = LO_SPACE;
|
|
|
|
}
|
|
|
|
} else if (space == OLD_DATA_SPACE && size > MaxObjectSizeInPagedSpace()) {
|
|
|
|
space = LO_SPACE;
|
2009-06-18 14:06:36 +00:00
|
|
|
}
|
2010-01-05 11:30:05 +00:00
|
|
|
Object* result = AllocateRaw(size, space, retry_space);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
|
|
|
// Partially initialize the object.
|
2009-11-24 14:10:06 +00:00
|
|
|
HeapObject::cast(result)->set_map(string_map());
|
2008-07-03 15:10:15 +00:00
|
|
|
String::cast(result)->set_length(length);
|
2009-11-24 14:10:06 +00:00
|
|
|
String::cast(result)->set_hash_field(String::kEmptyHashField);
|
2008-07-03 15:10:15 +00:00
|
|
|
ASSERT_EQ(size, HeapObject::cast(result)->Size());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateEmptyFixedArray() {
|
|
|
|
int size = FixedArray::SizeFor(0);
|
2008-10-30 09:15:58 +00:00
|
|
|
Object* result = AllocateRaw(size, OLD_DATA_SPACE, OLD_DATA_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
// Initialize the object.
|
|
|
|
reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
|
|
|
|
reinterpret_cast<Array*>(result)->set_length(0);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-20 06:35:28 +00:00
|
|
|
Object* Heap::AllocateRawFixedArray(int length) {
|
2010-01-07 13:17:18 +00:00
|
|
|
if (length < 0 || length > FixedArray::kMaxLength) {
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
2008-10-30 09:15:58 +00:00
|
|
|
// Use the general function if we're forced to always allocate.
|
2009-10-02 13:35:37 +00:00
|
|
|
if (always_allocate()) return AllocateFixedArray(length, TENURED);
|
2008-10-20 06:35:28 +00:00
|
|
|
// Allocate the raw data for a fixed array.
|
|
|
|
int size = FixedArray::SizeFor(length);
|
2009-06-18 14:06:36 +00:00
|
|
|
return size <= kMaxObjectSizeInNewSpace
|
|
|
|
? new_space_.AllocateRaw(size)
|
|
|
|
: lo_space_->AllocateRawFixedArray(size);
|
2008-10-20 06:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::CopyFixedArray(FixedArray* src) {
|
|
|
|
int len = src->length();
|
2008-10-22 08:21:18 +00:00
|
|
|
Object* obj = AllocateRawFixedArray(len);
|
2008-10-20 06:35:28 +00:00
|
|
|
if (obj->IsFailure()) return obj;
|
2008-10-22 08:21:18 +00:00
|
|
|
if (Heap::InNewSpace(obj)) {
|
|
|
|
HeapObject* dst = HeapObject::cast(obj);
|
|
|
|
CopyBlock(reinterpret_cast<Object**>(dst->address()),
|
|
|
|
reinterpret_cast<Object**>(src->address()),
|
|
|
|
FixedArray::SizeFor(len));
|
|
|
|
return obj;
|
|
|
|
}
|
2008-10-20 06:35:28 +00:00
|
|
|
HeapObject::cast(obj)->set_map(src->map());
|
|
|
|
FixedArray* result = FixedArray::cast(obj);
|
|
|
|
result->set_length(len);
|
|
|
|
// Copy the content
|
2008-10-23 08:46:32 +00:00
|
|
|
WriteBarrierMode mode = result->GetWriteBarrierMode();
|
2008-10-22 09:47:20 +00:00
|
|
|
for (int i = 0; i < len; i++) result->set(i, src->get(i), mode);
|
2008-10-20 06:35:28 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateFixedArray(int length) {
|
2009-08-19 07:30:20 +00:00
|
|
|
ASSERT(length >= 0);
|
2009-01-12 10:59:58 +00:00
|
|
|
if (length == 0) return empty_fixed_array();
|
2008-10-20 06:35:28 +00:00
|
|
|
Object* result = AllocateRawFixedArray(length);
|
|
|
|
if (!result->IsFailure()) {
|
|
|
|
// Initialize header.
|
|
|
|
reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
|
|
|
|
FixedArray* array = FixedArray::cast(result);
|
|
|
|
array->set_length(length);
|
2008-10-23 08:46:32 +00:00
|
|
|
Object* value = undefined_value();
|
2008-10-20 06:35:28 +00:00
|
|
|
// Initialize body.
|
2008-10-23 08:46:32 +00:00
|
|
|
for (int index = 0; index < length; index++) {
|
|
|
|
array->set(index, value, SKIP_WRITE_BARRIER);
|
|
|
|
}
|
2008-10-20 06:35:28 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* Heap::AllocateFixedArray(int length, PretenureFlag pretenure) {
|
2010-01-07 13:17:18 +00:00
|
|
|
ASSERT(length >= 0);
|
2008-07-03 15:10:15 +00:00
|
|
|
ASSERT(empty_fixed_array()->IsFixedArray());
|
2010-01-07 13:17:18 +00:00
|
|
|
if (length < 0 || length > FixedArray::kMaxLength) {
|
|
|
|
return Failure::OutOfMemoryException();
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
if (length == 0) return empty_fixed_array();
|
|
|
|
|
2010-01-05 11:30:05 +00:00
|
|
|
AllocationSpace space =
|
|
|
|
(pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
|
2008-07-03 15:10:15 +00:00
|
|
|
int size = FixedArray::SizeFor(length);
|
2010-01-05 11:30:05 +00:00
|
|
|
if (space == NEW_SPACE && size > kMaxObjectSizeInNewSpace) {
|
|
|
|
// Too big for new space.
|
|
|
|
space = LO_SPACE;
|
|
|
|
} else if (space == OLD_POINTER_SPACE &&
|
|
|
|
size > MaxObjectSizeInPagedSpace()) {
|
|
|
|
// Too big for old pointer space.
|
|
|
|
space = LO_SPACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Specialize allocation for the space.
|
2009-06-18 14:06:36 +00:00
|
|
|
Object* result = Failure::OutOfMemoryException();
|
2010-01-05 11:30:05 +00:00
|
|
|
if (space == NEW_SPACE) {
|
|
|
|
// We cannot use Heap::AllocateRaw() because it will not properly
|
|
|
|
// allocate extra remembered set bits if always_allocate() is true and
|
|
|
|
// new space allocation fails.
|
|
|
|
result = new_space_.AllocateRaw(size);
|
|
|
|
if (result->IsFailure() && always_allocate()) {
|
|
|
|
if (size <= MaxObjectSizeInPagedSpace()) {
|
|
|
|
result = old_pointer_space_->AllocateRaw(size);
|
|
|
|
} else {
|
|
|
|
result = lo_space_->AllocateRawFixedArray(size);
|
|
|
|
}
|
2009-06-18 14:06:36 +00:00
|
|
|
}
|
2010-01-05 11:30:05 +00:00
|
|
|
} else if (space == OLD_POINTER_SPACE) {
|
|
|
|
result = old_pointer_space_->AllocateRaw(size);
|
|
|
|
} else {
|
|
|
|
ASSERT(space == LO_SPACE);
|
|
|
|
result = lo_space_->AllocateRawFixedArray(size);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2010-01-05 11:30:05 +00:00
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// Initialize the object.
|
|
|
|
reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
|
|
|
|
FixedArray* array = FixedArray::cast(result);
|
|
|
|
array->set_length(length);
|
2008-10-23 08:46:32 +00:00
|
|
|
Object* value = undefined_value();
|
|
|
|
for (int index = 0; index < length; index++) {
|
|
|
|
array->set(index, value, SKIP_WRITE_BARRIER);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateFixedArrayWithHoles(int length) {
|
|
|
|
if (length == 0) return empty_fixed_array();
|
2008-10-20 06:35:28 +00:00
|
|
|
Object* result = AllocateRawFixedArray(length);
|
|
|
|
if (!result->IsFailure()) {
|
|
|
|
// Initialize header.
|
|
|
|
reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
|
|
|
|
FixedArray* array = FixedArray::cast(result);
|
|
|
|
array->set_length(length);
|
|
|
|
// Initialize body.
|
2008-10-23 08:46:32 +00:00
|
|
|
Object* value = the_hole_value();
|
|
|
|
for (int index = 0; index < length; index++) {
|
|
|
|
array->set(index, value, SKIP_WRITE_BARRIER);
|
|
|
|
}
|
2008-10-20 06:35:28 +00:00
|
|
|
}
|
|
|
|
return result;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateHashTable(int length) {
|
|
|
|
Object* result = Heap::AllocateFixedArray(length);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
reinterpret_cast<Array*>(result)->set_map(hash_table_map());
|
2009-07-02 06:50:43 +00:00
|
|
|
ASSERT(result->IsHashTable());
|
2008-07-03 15:10:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateGlobalContext() {
|
|
|
|
Object* result = Heap::AllocateFixedArray(Context::GLOBAL_CONTEXT_SLOTS);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
Context* context = reinterpret_cast<Context*>(result);
|
|
|
|
context->set_map(global_context_map());
|
|
|
|
ASSERT(context->IsGlobalContext());
|
|
|
|
ASSERT(result->IsContext());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateFunctionContext(int length, JSFunction* function) {
|
|
|
|
ASSERT(length >= Context::MIN_CONTEXT_SLOTS);
|
|
|
|
Object* result = Heap::AllocateFixedArray(length);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
Context* context = reinterpret_cast<Context*>(result);
|
|
|
|
context->set_map(context_map());
|
|
|
|
context->set_closure(function);
|
|
|
|
context->set_fcontext(context);
|
|
|
|
context->set_previous(NULL);
|
|
|
|
context->set_extension(NULL);
|
|
|
|
context->set_global(function->context()->global());
|
|
|
|
ASSERT(!context->IsGlobalContext());
|
|
|
|
ASSERT(context->is_function_context());
|
|
|
|
ASSERT(result->IsContext());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-18 11:28:13 +00:00
|
|
|
Object* Heap::AllocateWithContext(Context* previous,
|
|
|
|
JSObject* extension,
|
|
|
|
bool is_catch_context) {
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* result = Heap::AllocateFixedArray(Context::MIN_CONTEXT_SLOTS);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
Context* context = reinterpret_cast<Context*>(result);
|
2008-12-18 11:28:13 +00:00
|
|
|
context->set_map(is_catch_context ? catch_context_map() : context_map());
|
2008-07-03 15:10:15 +00:00
|
|
|
context->set_closure(previous->closure());
|
|
|
|
context->set_fcontext(previous->fcontext());
|
|
|
|
context->set_previous(previous);
|
|
|
|
context->set_extension(extension);
|
|
|
|
context->set_global(previous->global());
|
|
|
|
ASSERT(!context->IsGlobalContext());
|
|
|
|
ASSERT(!context->is_function_context());
|
|
|
|
ASSERT(result->IsContext());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::AllocateStruct(InstanceType type) {
|
|
|
|
Map* map;
|
|
|
|
switch (type) {
|
|
|
|
#define MAKE_CASE(NAME, Name, name) case NAME##_TYPE: map = name##_map(); break;
|
|
|
|
STRUCT_LIST(MAKE_CASE)
|
|
|
|
#undef MAKE_CASE
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return Failure::InternalError();
|
|
|
|
}
|
|
|
|
int size = map->instance_size();
|
|
|
|
AllocationSpace space =
|
2009-06-18 14:06:36 +00:00
|
|
|
(size > MaxObjectSizeInPagedSpace()) ? LO_SPACE : OLD_POINTER_SPACE;
|
2008-07-03 15:10:15 +00:00
|
|
|
Object* result = Heap::Allocate(map, space);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
Struct::cast(result)->InitializeBody(size);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-26 08:13:27 +00:00
|
|
|
bool Heap::IdleNotification() {
|
2009-09-09 11:21:54 +00:00
|
|
|
static const int kIdlesBeforeScavenge = 4;
|
|
|
|
static const int kIdlesBeforeMarkSweep = 7;
|
|
|
|
static const int kIdlesBeforeMarkCompact = 8;
|
2009-08-26 08:13:27 +00:00
|
|
|
static int number_idle_notifications = 0;
|
|
|
|
static int last_gc_count = gc_count_;
|
|
|
|
|
|
|
|
bool finished = false;
|
|
|
|
|
|
|
|
if (last_gc_count == gc_count_) {
|
|
|
|
number_idle_notifications++;
|
|
|
|
} else {
|
|
|
|
number_idle_notifications = 0;
|
|
|
|
last_gc_count = gc_count_;
|
|
|
|
}
|
|
|
|
|
2009-09-09 11:21:54 +00:00
|
|
|
if (number_idle_notifications == kIdlesBeforeScavenge) {
|
|
|
|
CollectGarbage(0, NEW_SPACE);
|
|
|
|
new_space_.Shrink();
|
2009-08-26 08:13:27 +00:00
|
|
|
last_gc_count = gc_count_;
|
2009-09-09 11:21:54 +00:00
|
|
|
|
|
|
|
} else if (number_idle_notifications == kIdlesBeforeMarkSweep) {
|
2009-11-30 07:57:32 +00:00
|
|
|
// Before doing the mark-sweep collections we clear the
|
|
|
|
// compilation cache to avoid hanging on to source code and
|
|
|
|
// generated code for cached functions.
|
|
|
|
CompilationCache::Clear();
|
|
|
|
|
2009-09-09 11:21:54 +00:00
|
|
|
CollectAllGarbage(false);
|
|
|
|
new_space_.Shrink();
|
|
|
|
last_gc_count = gc_count_;
|
|
|
|
|
|
|
|
} else if (number_idle_notifications == kIdlesBeforeMarkCompact) {
|
|
|
|
CollectAllGarbage(true);
|
|
|
|
new_space_.Shrink();
|
|
|
|
last_gc_count = gc_count_;
|
|
|
|
number_idle_notifications = 0;
|
|
|
|
finished = true;
|
2009-08-26 08:13:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Uncommit unused memory in new space.
|
|
|
|
Heap::UncommitFromSpace();
|
|
|
|
return finished;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
void Heap::Print() {
|
|
|
|
if (!HasBeenSetup()) return;
|
|
|
|
Top::PrintStack();
|
2008-09-05 12:34:09 +00:00
|
|
|
AllSpaces spaces;
|
2010-01-25 22:53:18 +00:00
|
|
|
for (Space* space = spaces.next(); space != NULL; space = spaces.next())
|
|
|
|
space->Print();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::ReportCodeStatistics(const char* title) {
|
|
|
|
PrintF(">>>>>> Code Stats (%s) >>>>>>\n", title);
|
|
|
|
PagedSpace::ResetCodeStatistics();
|
|
|
|
// We do not look for code in new space, map space, or old space. If code
|
|
|
|
// somehow ends up in those spaces, we would miss it here.
|
|
|
|
code_space_->CollectCodeStatistics();
|
|
|
|
lo_space_->CollectCodeStatistics();
|
|
|
|
PagedSpace::ReportCodeStatistics();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This function expects that NewSpace's allocated objects histogram is
|
|
|
|
// populated (via a call to CollectStatistics or else as a side effect of a
|
|
|
|
// just-completed scavenge collection).
|
|
|
|
void Heap::ReportHeapStatistics(const char* title) {
|
|
|
|
USE(title);
|
|
|
|
PrintF(">>>>>> =============== %s (%d) =============== >>>>>>\n",
|
|
|
|
title, gc_count_);
|
|
|
|
PrintF("mark-compact GC : %d\n", mc_count_);
|
2008-10-29 09:27:59 +00:00
|
|
|
PrintF("old_gen_promotion_limit_ %d\n", old_gen_promotion_limit_);
|
|
|
|
PrintF("old_gen_allocation_limit_ %d\n", old_gen_allocation_limit_);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
PrintF("\n");
|
|
|
|
PrintF("Number of handles : %d\n", HandleScope::NumberOfHandles());
|
|
|
|
GlobalHandles::PrintStats();
|
|
|
|
PrintF("\n");
|
|
|
|
|
|
|
|
PrintF("Heap statistics : ");
|
|
|
|
MemoryAllocator::ReportStatistics();
|
|
|
|
PrintF("To space : ");
|
2008-10-17 09:13:27 +00:00
|
|
|
new_space_.ReportStatistics();
|
2008-09-05 12:34:09 +00:00
|
|
|
PrintF("Old pointer space : ");
|
|
|
|
old_pointer_space_->ReportStatistics();
|
|
|
|
PrintF("Old data space : ");
|
|
|
|
old_data_space_->ReportStatistics();
|
2008-07-03 15:10:15 +00:00
|
|
|
PrintF("Code space : ");
|
|
|
|
code_space_->ReportStatistics();
|
|
|
|
PrintF("Map space : ");
|
|
|
|
map_space_->ReportStatistics();
|
2009-07-09 11:13:08 +00:00
|
|
|
PrintF("Cell space : ");
|
|
|
|
cell_space_->ReportStatistics();
|
2008-07-03 15:10:15 +00:00
|
|
|
PrintF("Large object space : ");
|
|
|
|
lo_space_->ReportStatistics();
|
|
|
|
PrintF(">>>>>> ========================================= >>>>>>\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
bool Heap::Contains(HeapObject* value) {
|
|
|
|
return Contains(value->address());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Heap::Contains(Address addr) {
|
|
|
|
if (OS::IsOutsideAllocatedSpace(addr)) return false;
|
|
|
|
return HasBeenSetup() &&
|
2008-10-17 09:13:27 +00:00
|
|
|
(new_space_.ToSpaceContains(addr) ||
|
2008-09-05 12:34:09 +00:00
|
|
|
old_pointer_space_->Contains(addr) ||
|
|
|
|
old_data_space_->Contains(addr) ||
|
2008-07-03 15:10:15 +00:00
|
|
|
code_space_->Contains(addr) ||
|
|
|
|
map_space_->Contains(addr) ||
|
2009-07-09 11:13:08 +00:00
|
|
|
cell_space_->Contains(addr) ||
|
2008-07-03 15:10:15 +00:00
|
|
|
lo_space_->SlowContains(addr));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Heap::InSpace(HeapObject* value, AllocationSpace space) {
|
|
|
|
return InSpace(value->address(), space);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Heap::InSpace(Address addr, AllocationSpace space) {
|
|
|
|
if (OS::IsOutsideAllocatedSpace(addr)) return false;
|
|
|
|
if (!HasBeenSetup()) return false;
|
|
|
|
|
|
|
|
switch (space) {
|
|
|
|
case NEW_SPACE:
|
2008-10-17 09:13:27 +00:00
|
|
|
return new_space_.ToSpaceContains(addr);
|
2008-09-05 12:34:09 +00:00
|
|
|
case OLD_POINTER_SPACE:
|
|
|
|
return old_pointer_space_->Contains(addr);
|
|
|
|
case OLD_DATA_SPACE:
|
|
|
|
return old_data_space_->Contains(addr);
|
2008-07-03 15:10:15 +00:00
|
|
|
case CODE_SPACE:
|
|
|
|
return code_space_->Contains(addr);
|
|
|
|
case MAP_SPACE:
|
|
|
|
return map_space_->Contains(addr);
|
2009-07-09 11:13:08 +00:00
|
|
|
case CELL_SPACE:
|
|
|
|
return cell_space_->Contains(addr);
|
2008-07-03 15:10:15 +00:00
|
|
|
case LO_SPACE:
|
|
|
|
return lo_space_->SlowContains(addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
void Heap::Verify() {
|
|
|
|
ASSERT(HasBeenSetup());
|
|
|
|
|
|
|
|
VerifyPointersVisitor visitor;
|
2009-11-05 15:12:36 +00:00
|
|
|
IterateRoots(&visitor, VISIT_ONLY_STRONG);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-07-09 14:34:08 +00:00
|
|
|
new_space_.Verify();
|
|
|
|
|
|
|
|
VerifyPointersAndRSetVisitor rset_visitor;
|
|
|
|
old_pointer_space_->Verify(&rset_visitor);
|
|
|
|
map_space_->Verify(&rset_visitor);
|
|
|
|
|
|
|
|
VerifyPointersVisitor no_rset_visitor;
|
|
|
|
old_data_space_->Verify(&no_rset_visitor);
|
|
|
|
code_space_->Verify(&no_rset_visitor);
|
|
|
|
cell_space_->Verify(&no_rset_visitor);
|
|
|
|
|
|
|
|
lo_space_->Verify();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::LookupSymbol(Vector<const char> string) {
|
|
|
|
Object* symbol = NULL;
|
2009-07-08 19:12:58 +00:00
|
|
|
Object* new_table = symbol_table()->LookupSymbol(string, &symbol);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (new_table->IsFailure()) return new_table;
|
2009-07-08 19:12:58 +00:00
|
|
|
// Can't use set_symbol_table because SymbolTable::cast knows that
|
|
|
|
// SymbolTable is a singleton and checks for identity.
|
|
|
|
roots_[kSymbolTableRootIndex] = new_table;
|
2008-07-03 15:10:15 +00:00
|
|
|
ASSERT(symbol != NULL);
|
|
|
|
return symbol;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object* Heap::LookupSymbol(String* string) {
|
|
|
|
if (string->IsSymbol()) return string;
|
|
|
|
Object* symbol = NULL;
|
2009-07-08 19:12:58 +00:00
|
|
|
Object* new_table = symbol_table()->LookupString(string, &symbol);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (new_table->IsFailure()) return new_table;
|
2009-07-08 19:12:58 +00:00
|
|
|
// Can't use set_symbol_table because SymbolTable::cast knows that
|
|
|
|
// SymbolTable is a singleton and checks for identity.
|
|
|
|
roots_[kSymbolTableRootIndex] = new_table;
|
2008-07-03 15:10:15 +00:00
|
|
|
ASSERT(symbol != NULL);
|
|
|
|
return symbol;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-07 10:10:03 +00:00
|
|
|
bool Heap::LookupSymbolIfExists(String* string, String** symbol) {
|
|
|
|
if (string->IsSymbol()) {
|
|
|
|
*symbol = string;
|
|
|
|
return true;
|
|
|
|
}
|
2009-07-08 19:12:58 +00:00
|
|
|
return symbol_table()->LookupSymbolIfExists(string, symbol);
|
2008-10-07 10:10:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
void Heap::ZapFromSpace() {
|
2009-08-26 10:33:11 +00:00
|
|
|
ASSERT(reinterpret_cast<Object*>(kFromSpaceZapValue)->IsHeapObject());
|
2008-10-17 09:13:27 +00:00
|
|
|
for (Address a = new_space_.FromSpaceLow();
|
|
|
|
a < new_space_.FromSpaceHigh();
|
2008-07-03 15:10:15 +00:00
|
|
|
a += kPointerSize) {
|
|
|
|
Memory::Address_at(a) = kFromSpaceZapValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
|
2009-05-25 16:33:00 +00:00
|
|
|
int Heap::IterateRSetRange(Address object_start,
|
|
|
|
Address object_end,
|
|
|
|
Address rset_start,
|
|
|
|
ObjectSlotCallback copy_object_func) {
|
2008-07-03 15:10:15 +00:00
|
|
|
Address object_address = object_start;
|
|
|
|
Address rset_address = rset_start;
|
2009-05-25 16:33:00 +00:00
|
|
|
int set_bits_count = 0;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Loop over all the pointers in [object_start, object_end).
|
|
|
|
while (object_address < object_end) {
|
|
|
|
uint32_t rset_word = Memory::uint32_at(rset_address);
|
|
|
|
if (rset_word != 0) {
|
|
|
|
uint32_t result_rset = rset_word;
|
2008-10-17 09:13:27 +00:00
|
|
|
for (uint32_t bitmask = 1; bitmask != 0; bitmask = bitmask << 1) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// Do not dereference pointers at or past object_end.
|
|
|
|
if ((rset_word & bitmask) != 0 && object_address < object_end) {
|
|
|
|
Object** object_p = reinterpret_cast<Object**>(object_address);
|
2008-10-17 09:13:27 +00:00
|
|
|
if (Heap::InNewSpace(*object_p)) {
|
2008-07-03 15:10:15 +00:00
|
|
|
copy_object_func(reinterpret_cast<HeapObject**>(object_p));
|
|
|
|
}
|
|
|
|
// If this pointer does not need to be remembered anymore, clear
|
|
|
|
// the remembered set bit.
|
2008-10-17 09:13:27 +00:00
|
|
|
if (!Heap::InNewSpace(*object_p)) result_rset &= ~bitmask;
|
2009-05-25 16:33:00 +00:00
|
|
|
set_bits_count++;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
object_address += kPointerSize;
|
|
|
|
}
|
|
|
|
// Update the remembered set if it has changed.
|
|
|
|
if (result_rset != rset_word) {
|
|
|
|
Memory::uint32_at(rset_address) = result_rset;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// No bits in the word were set. This is the common case.
|
|
|
|
object_address += kPointerSize * kBitsPerInt;
|
|
|
|
}
|
|
|
|
rset_address += kIntSize;
|
|
|
|
}
|
2009-05-25 16:33:00 +00:00
|
|
|
return set_bits_count;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::IterateRSet(PagedSpace* space, ObjectSlotCallback copy_object_func) {
|
|
|
|
ASSERT(Page::is_rset_in_use());
|
2009-07-09 13:28:22 +00:00
|
|
|
ASSERT(space == old_pointer_space_ || space == map_space_);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-25 16:33:00 +00:00
|
|
|
static void* paged_rset_histogram = StatsTable::CreateHistogram(
|
|
|
|
"V8.RSetPaged",
|
|
|
|
0,
|
|
|
|
Page::kObjectAreaSize / kPointerSize,
|
|
|
|
30);
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
PageIterator it(space, PageIterator::PAGES_IN_USE);
|
|
|
|
while (it.has_next()) {
|
|
|
|
Page* page = it.next();
|
2009-05-25 16:33:00 +00:00
|
|
|
int count = IterateRSetRange(page->ObjectAreaStart(), page->AllocationTop(),
|
|
|
|
page->RSetStart(), copy_object_func);
|
|
|
|
if (paged_rset_histogram != NULL) {
|
|
|
|
StatsTable::AddHistogramSample(paged_rset_histogram, count);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-05 15:12:36 +00:00
|
|
|
void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) {
|
|
|
|
IterateStrongRoots(v, mode);
|
2010-01-27 08:25:48 +00:00
|
|
|
IterateWeakRoots(v, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::IterateWeakRoots(ObjectVisitor* v, VisitMode mode) {
|
2009-07-08 19:12:58 +00:00
|
|
|
v->VisitPointer(reinterpret_cast<Object**>(&roots_[kSymbolTableRootIndex]));
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("symbol_table");
|
2009-12-09 14:32:45 +00:00
|
|
|
if (mode != VISIT_ALL_IN_SCAVENGE) {
|
|
|
|
// Scavenge collections have special processing for this.
|
|
|
|
ExternalStringTable::Iterate(v);
|
|
|
|
}
|
|
|
|
v->Synchronize("external_string_table");
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-05 15:12:36 +00:00
|
|
|
void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
|
2009-07-08 19:12:58 +00:00
|
|
|
v->VisitPointers(&roots_[0], &roots_[kStrongRootListLength]);
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("strong_root_list");
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-03-19 18:50:00 +00:00
|
|
|
v->VisitPointer(bit_cast<Object**, String**>(&hidden_symbol_));
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("symbol");
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Bootstrapper::Iterate(v);
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("bootstrapper");
|
2008-07-03 15:10:15 +00:00
|
|
|
Top::Iterate(v);
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("top");
|
2009-09-30 12:25:46 +00:00
|
|
|
Relocatable::Iterate(v);
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("relocatable");
|
2009-04-20 16:36:13 +00:00
|
|
|
|
|
|
|
#ifdef ENABLE_DEBUGGER_SUPPORT
|
2008-07-03 15:10:15 +00:00
|
|
|
Debug::Iterate(v);
|
2009-04-20 16:36:13 +00:00
|
|
|
#endif
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("debug");
|
2008-09-11 10:51:52 +00:00
|
|
|
CompilationCache::Iterate(v);
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("compilationcache");
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Iterate over local handles in handle scopes.
|
|
|
|
HandleScopeImplementer::Iterate(v);
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("handlescope");
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-12-09 14:32:45 +00:00
|
|
|
// Iterate over the builtin code objects and code stubs in the
|
|
|
|
// heap. Note that it is not necessary to iterate over code objects
|
|
|
|
// on scavenge collections.
|
|
|
|
if (mode != VISIT_ALL_IN_SCAVENGE) {
|
|
|
|
Builtins::IterateBuiltins(v);
|
|
|
|
}
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("builtins");
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Iterate over global handles.
|
2009-11-05 15:12:36 +00:00
|
|
|
if (mode == VISIT_ONLY_STRONG) {
|
|
|
|
GlobalHandles::IterateStrongRoots(v);
|
|
|
|
} else {
|
|
|
|
GlobalHandles::IterateAllRoots(v);
|
|
|
|
}
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("globalhandles");
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Iterate over pointers being held by inactive threads.
|
|
|
|
ThreadManager::Iterate(v);
|
2009-10-27 11:54:01 +00:00
|
|
|
v->Synchronize("threadmanager");
|
2010-01-27 08:25:48 +00:00
|
|
|
|
|
|
|
// Iterate over the pointers the Serialization/Deserialization code is
|
|
|
|
// holding.
|
|
|
|
// During garbage collection this keeps the partial snapshot cache alive.
|
|
|
|
// During deserialization of the startup snapshot this creates the partial
|
|
|
|
// snapshot cache and deserializes the objects it refers to. During
|
|
|
|
// serialization this does nothing, since the partial snapshot cache is
|
|
|
|
// empty. However the next thing we do is create the partial snapshot,
|
|
|
|
// filling up the partial snapshot cache with objects it needs as we go.
|
|
|
|
SerializerDeserializer::Iterate(v);
|
|
|
|
// We don't do a v->Synchronize call here, because in debug mode that will
|
|
|
|
// output a flag to the snapshot. However at this point the serializer and
|
|
|
|
// deserializer are deliberately a little unsynchronized (see above) so the
|
|
|
|
// checking of the sync flag in the snapshot would fail.
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Flag is set when the heap has been configured. The heap can be repeatedly
|
|
|
|
// configured through the API until it is setup.
|
|
|
|
static bool heap_configured = false;
|
|
|
|
|
|
|
|
// TODO(1236194): Since the heap size is configurable on the command line
|
|
|
|
// and through the API, we should gracefully handle the case that the heap
|
|
|
|
// size is not big enough to fit all the initial objects.
|
2009-10-21 15:03:34 +00:00
|
|
|
bool Heap::ConfigureHeap(int max_semispace_size, int max_old_gen_size) {
|
2008-07-03 15:10:15 +00:00
|
|
|
if (HasBeenSetup()) return false;
|
|
|
|
|
2009-10-21 15:03:34 +00:00
|
|
|
if (max_semispace_size > 0) max_semispace_size_ = max_semispace_size;
|
|
|
|
|
|
|
|
if (Snapshot::IsEnabled()) {
|
|
|
|
// If we are using a snapshot we always reserve the default amount
|
|
|
|
// of memory for each semispace because code in the snapshot has
|
|
|
|
// write-barrier code that relies on the size and alignment of new
|
|
|
|
// space. We therefore cannot use a larger max semispace size
|
|
|
|
// than the default reserved semispace size.
|
|
|
|
if (max_semispace_size_ > reserved_semispace_size_) {
|
|
|
|
max_semispace_size_ = reserved_semispace_size_;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If we are not using snapshots we reserve space for the actual
|
|
|
|
// max semispace size.
|
|
|
|
reserved_semispace_size_ = max_semispace_size_;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (max_old_gen_size > 0) max_old_generation_size_ = max_old_gen_size;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// The new space size must be a power of two to support single-bit testing
|
|
|
|
// for containment.
|
2009-10-21 15:03:34 +00:00
|
|
|
max_semispace_size_ = RoundUpToPowerOf2(max_semispace_size_);
|
|
|
|
reserved_semispace_size_ = RoundUpToPowerOf2(reserved_semispace_size_);
|
|
|
|
initial_semispace_size_ = Min(initial_semispace_size_, max_semispace_size_);
|
|
|
|
external_allocation_limit_ = 10 * max_semispace_size_;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// The old generation is paged.
|
2009-10-21 15:03:34 +00:00
|
|
|
max_old_generation_size_ = RoundUp(max_old_generation_size_, Page::kPageSize);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
heap_configured = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
bool Heap::ConfigureHeapDefault() {
|
2009-10-21 15:03:34 +00:00
|
|
|
return ConfigureHeap(FLAG_max_new_space_size / 2, FLAG_max_old_space_size);
|
2008-07-30 08:49:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-03 10:16:37 +00:00
|
|
|
void Heap::RecordStats(HeapStats* stats) {
|
2009-12-04 10:18:30 +00:00
|
|
|
*stats->start_marker = 0xDECADE00;
|
|
|
|
*stats->end_marker = 0xDECADE01;
|
|
|
|
*stats->new_space_size = new_space_.Size();
|
|
|
|
*stats->new_space_capacity = new_space_.Capacity();
|
|
|
|
*stats->old_pointer_space_size = old_pointer_space_->Size();
|
|
|
|
*stats->old_pointer_space_capacity = old_pointer_space_->Capacity();
|
|
|
|
*stats->old_data_space_size = old_data_space_->Size();
|
|
|
|
*stats->old_data_space_capacity = old_data_space_->Capacity();
|
|
|
|
*stats->code_space_size = code_space_->Size();
|
|
|
|
*stats->code_space_capacity = code_space_->Capacity();
|
|
|
|
*stats->map_space_size = map_space_->Size();
|
|
|
|
*stats->map_space_capacity = map_space_->Capacity();
|
|
|
|
*stats->cell_space_size = cell_space_->Size();
|
|
|
|
*stats->cell_space_capacity = cell_space_->Capacity();
|
|
|
|
*stats->lo_space_size = lo_space_->Size();
|
2009-12-03 10:16:37 +00:00
|
|
|
GlobalHandles::RecordStats(stats);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
int Heap::PromotedSpaceSize() {
|
2008-09-05 12:34:09 +00:00
|
|
|
return old_pointer_space_->Size()
|
|
|
|
+ old_data_space_->Size()
|
2008-07-03 15:10:15 +00:00
|
|
|
+ code_space_->Size()
|
|
|
|
+ map_space_->Size()
|
2009-07-09 11:13:08 +00:00
|
|
|
+ cell_space_->Size()
|
2008-07-03 15:10:15 +00:00
|
|
|
+ lo_space_->Size();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
int Heap::PromotedExternalMemorySize() {
|
|
|
|
if (amount_of_external_allocated_memory_
|
|
|
|
<= amount_of_external_allocated_memory_at_last_global_gc_) return 0;
|
|
|
|
return amount_of_external_allocated_memory_
|
|
|
|
- amount_of_external_allocated_memory_at_last_global_gc_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
bool Heap::Setup(bool create_heap_objects) {
|
|
|
|
// Initialize heap spaces and initial maps and objects. Whenever something
|
|
|
|
// goes wrong, just return false. The caller should check the results and
|
|
|
|
// call Heap::TearDown() to release allocated memory.
|
|
|
|
//
|
|
|
|
// If the heap is not yet configured (eg, through the API), configure it.
|
|
|
|
// Configuration is based on the flags new-space-size (really the semispace
|
|
|
|
// size) and old-space-size if set or the initial values of semispace_size_
|
|
|
|
// and old_generation_size_ otherwise.
|
|
|
|
if (!heap_configured) {
|
2008-07-30 08:49:36 +00:00
|
|
|
if (!ConfigureHeapDefault()) return false;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2009-09-09 08:45:32 +00:00
|
|
|
// Setup memory allocator and reserve a chunk of memory for new
|
2009-10-21 15:03:34 +00:00
|
|
|
// space. The chunk is double the size of the requested reserved
|
|
|
|
// new space size to ensure that we can find a pair of semispaces that
|
|
|
|
// are contiguous and aligned to their size.
|
|
|
|
if (!MemoryAllocator::Setup(MaxReserved())) return false;
|
2009-09-09 08:45:32 +00:00
|
|
|
void* chunk =
|
2009-10-21 15:03:34 +00:00
|
|
|
MemoryAllocator::ReserveInitialChunk(4 * reserved_semispace_size_);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (chunk == NULL) return false;
|
|
|
|
|
2009-09-09 08:45:32 +00:00
|
|
|
// Align the pair of semispaces to their size, which must be a power
|
|
|
|
// of 2.
|
|
|
|
Address new_space_start =
|
2009-10-21 15:03:34 +00:00
|
|
|
RoundUp(reinterpret_cast<byte*>(chunk), 2 * reserved_semispace_size_);
|
2009-10-21 15:37:14 +00:00
|
|
|
if (!new_space_.Setup(new_space_start, 2 * reserved_semispace_size_)) {
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-09-09 08:45:32 +00:00
|
|
|
// Initialize old pointer space.
|
2008-09-05 12:34:09 +00:00
|
|
|
old_pointer_space_ =
|
2009-10-21 15:03:34 +00:00
|
|
|
new OldSpace(max_old_generation_size_, OLD_POINTER_SPACE, NOT_EXECUTABLE);
|
2008-09-05 12:34:09 +00:00
|
|
|
if (old_pointer_space_ == NULL) return false;
|
2009-09-09 08:45:32 +00:00
|
|
|
if (!old_pointer_space_->Setup(NULL, 0)) return false;
|
|
|
|
|
|
|
|
// Initialize old data space.
|
2008-09-05 12:34:09 +00:00
|
|
|
old_data_space_ =
|
2009-10-21 15:03:34 +00:00
|
|
|
new OldSpace(max_old_generation_size_, OLD_DATA_SPACE, NOT_EXECUTABLE);
|
2008-09-05 12:34:09 +00:00
|
|
|
if (old_data_space_ == NULL) return false;
|
2009-09-09 08:45:32 +00:00
|
|
|
if (!old_data_space_->Setup(NULL, 0)) return false;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Initialize the code space, set its maximum capacity to the old
|
2008-07-30 08:49:36 +00:00
|
|
|
// generation size. It needs executable memory.
|
2009-10-05 11:16:25 +00:00
|
|
|
// On 64-bit platform(s), we put all code objects in a 2 GB range of
|
|
|
|
// virtual address space, so that they can call each other with near calls.
|
|
|
|
if (code_range_size_ > 0) {
|
|
|
|
if (!CodeRange::Setup(code_range_size_)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-05 12:34:09 +00:00
|
|
|
code_space_ =
|
2009-10-21 15:03:34 +00:00
|
|
|
new OldSpace(max_old_generation_size_, CODE_SPACE, EXECUTABLE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (code_space_ == NULL) return false;
|
2009-09-09 08:45:32 +00:00
|
|
|
if (!code_space_->Setup(NULL, 0)) return false;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Initialize map space.
|
2009-12-18 13:38:09 +00:00
|
|
|
map_space_ = new MapSpace(FLAG_use_big_map_space
|
|
|
|
? max_old_generation_size_
|
2010-01-19 16:34:37 +00:00
|
|
|
: MapSpace::kMaxMapPageIndex * Page::kPageSize,
|
|
|
|
FLAG_max_map_space_pages,
|
2009-12-18 13:38:09 +00:00
|
|
|
MAP_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (map_space_ == NULL) return false;
|
|
|
|
if (!map_space_->Setup(NULL, 0)) return false;
|
|
|
|
|
2009-07-09 11:13:08 +00:00
|
|
|
// Initialize global property cell space.
|
2009-10-21 15:03:34 +00:00
|
|
|
cell_space_ = new CellSpace(max_old_generation_size_, CELL_SPACE);
|
2009-07-09 11:13:08 +00:00
|
|
|
if (cell_space_ == NULL) return false;
|
|
|
|
if (!cell_space_->Setup(NULL, 0)) return false;
|
|
|
|
|
2008-09-05 12:34:09 +00:00
|
|
|
// The large object code space may contain code or data. We set the memory
|
|
|
|
// to be non-executable here for safety, but this means we need to enable it
|
|
|
|
// explicitly when allocating large code objects.
|
|
|
|
lo_space_ = new LargeObjectSpace(LO_SPACE);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (lo_space_ == NULL) return false;
|
|
|
|
if (!lo_space_->Setup()) return false;
|
|
|
|
|
|
|
|
if (create_heap_objects) {
|
|
|
|
// Create initial maps.
|
|
|
|
if (!CreateInitialMaps()) return false;
|
|
|
|
if (!CreateApiObjects()) return false;
|
|
|
|
|
|
|
|
// Create initial objects
|
|
|
|
if (!CreateInitialObjects()) return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(IntEvent("heap-capacity", Capacity()));
|
|
|
|
LOG(IntEvent("heap-available", Available()));
|
|
|
|
|
2009-10-21 13:04:53 +00:00
|
|
|
#ifdef ENABLE_LOGGING_AND_PROFILING
|
2009-10-15 07:50:23 +00:00
|
|
|
// This should be called only after initial objects have been created.
|
|
|
|
ProducerHeapProfile::Setup();
|
2009-10-21 13:04:53 +00:00
|
|
|
#endif
|
2009-10-15 07:50:23 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-05 13:59:40 +00:00
|
|
|
void Heap::SetStackLimits() {
|
2009-08-31 08:57:36 +00:00
|
|
|
// On 64 bit machines, pointers are generally out of range of Smis. We write
|
|
|
|
// something that looks like an out of range Smi to the GC.
|
|
|
|
|
2009-11-05 13:59:40 +00:00
|
|
|
// Set up the special root array entries containing the stack limits.
|
|
|
|
// These are actually addresses, but the tag makes the GC ignore it.
|
2009-08-31 08:57:36 +00:00
|
|
|
roots_[kStackLimitRootIndex] =
|
2009-11-05 13:59:40 +00:00
|
|
|
reinterpret_cast<Object*>(
|
|
|
|
(StackGuard::jslimit() & ~kSmiTagMask) | kSmiTag);
|
|
|
|
roots_[kRealStackLimitRootIndex] =
|
|
|
|
reinterpret_cast<Object*>(
|
|
|
|
(StackGuard::real_jslimit() & ~kSmiTagMask) | kSmiTag);
|
2009-08-26 10:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Heap::TearDown() {
|
|
|
|
GlobalHandles::TearDown();
|
|
|
|
|
2009-12-09 14:32:45 +00:00
|
|
|
ExternalStringTable::TearDown();
|
|
|
|
|
2008-10-17 09:13:27 +00:00
|
|
|
new_space_.TearDown();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2008-09-05 12:34:09 +00:00
|
|
|
if (old_pointer_space_ != NULL) {
|
|
|
|
old_pointer_space_->TearDown();
|
|
|
|
delete old_pointer_space_;
|
|
|
|
old_pointer_space_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_data_space_ != NULL) {
|
|
|
|
old_data_space_->TearDown();
|
|
|
|
delete old_data_space_;
|
|
|
|
old_data_space_ = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (code_space_ != NULL) {
|
|
|
|
code_space_->TearDown();
|
|
|
|
delete code_space_;
|
|
|
|
code_space_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (map_space_ != NULL) {
|
|
|
|
map_space_->TearDown();
|
|
|
|
delete map_space_;
|
|
|
|
map_space_ = NULL;
|
|
|
|
}
|
|
|
|
|
2009-07-09 11:13:08 +00:00
|
|
|
if (cell_space_ != NULL) {
|
|
|
|
cell_space_->TearDown();
|
|
|
|
delete cell_space_;
|
|
|
|
cell_space_ = NULL;
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
if (lo_space_ != NULL) {
|
|
|
|
lo_space_->TearDown();
|
|
|
|
delete lo_space_;
|
|
|
|
lo_space_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
MemoryAllocator::TearDown();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::Shrink() {
|
2009-07-09 11:13:08 +00:00
|
|
|
// Try to shrink all paged spaces.
|
|
|
|
PagedSpaces spaces;
|
2010-01-25 22:53:18 +00:00
|
|
|
for (PagedSpace* space = spaces.next(); space != NULL; space = spaces.next())
|
|
|
|
space->Shrink();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-24 12:47:53 +00:00
|
|
|
#ifdef ENABLE_HEAP_PROTECTION
|
|
|
|
|
|
|
|
void Heap::Protect() {
|
2009-03-30 13:32:28 +00:00
|
|
|
if (HasBeenSetup()) {
|
2009-07-09 11:13:08 +00:00
|
|
|
AllSpaces spaces;
|
2010-01-25 22:53:18 +00:00
|
|
|
for (Space* space = spaces.next(); space != NULL; space = spaces.next())
|
|
|
|
space->Protect();
|
2009-03-30 13:32:28 +00:00
|
|
|
}
|
2009-03-24 12:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Heap::Unprotect() {
|
2009-03-30 13:32:28 +00:00
|
|
|
if (HasBeenSetup()) {
|
2009-07-09 11:13:08 +00:00
|
|
|
AllSpaces spaces;
|
2010-01-25 22:53:18 +00:00
|
|
|
for (Space* space = spaces.next(); space != NULL; space = spaces.next())
|
|
|
|
space->Unprotect();
|
2009-03-30 13:32:28 +00:00
|
|
|
}
|
2009-03-24 12:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
class PrintHandleVisitor: public ObjectVisitor {
|
|
|
|
public:
|
|
|
|
void VisitPointers(Object** start, Object** end) {
|
|
|
|
for (Object** p = start; p < end; p++)
|
|
|
|
PrintF(" handle %p to %p\n", p, *p);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void Heap::PrintHandles() {
|
|
|
|
PrintF("Handles:\n");
|
|
|
|
PrintHandleVisitor v;
|
|
|
|
HandleScopeImplementer::Iterate(&v);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2008-09-05 12:34:09 +00:00
|
|
|
Space* AllSpaces::next() {
|
|
|
|
switch (counter_++) {
|
|
|
|
case NEW_SPACE:
|
|
|
|
return Heap::new_space();
|
|
|
|
case OLD_POINTER_SPACE:
|
|
|
|
return Heap::old_pointer_space();
|
|
|
|
case OLD_DATA_SPACE:
|
|
|
|
return Heap::old_data_space();
|
|
|
|
case CODE_SPACE:
|
|
|
|
return Heap::code_space();
|
|
|
|
case MAP_SPACE:
|
|
|
|
return Heap::map_space();
|
2009-07-09 11:13:08 +00:00
|
|
|
case CELL_SPACE:
|
|
|
|
return Heap::cell_space();
|
2008-09-05 12:34:09 +00:00
|
|
|
case LO_SPACE:
|
|
|
|
return Heap::lo_space();
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PagedSpace* PagedSpaces::next() {
|
|
|
|
switch (counter_++) {
|
|
|
|
case OLD_POINTER_SPACE:
|
|
|
|
return Heap::old_pointer_space();
|
|
|
|
case OLD_DATA_SPACE:
|
|
|
|
return Heap::old_data_space();
|
|
|
|
case CODE_SPACE:
|
|
|
|
return Heap::code_space();
|
|
|
|
case MAP_SPACE:
|
|
|
|
return Heap::map_space();
|
2009-07-09 11:13:08 +00:00
|
|
|
case CELL_SPACE:
|
|
|
|
return Heap::cell_space();
|
2008-09-05 12:34:09 +00:00
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OldSpace* OldSpaces::next() {
|
|
|
|
switch (counter_++) {
|
|
|
|
case OLD_POINTER_SPACE:
|
|
|
|
return Heap::old_pointer_space();
|
|
|
|
case OLD_DATA_SPACE:
|
|
|
|
return Heap::old_data_space();
|
|
|
|
case CODE_SPACE:
|
|
|
|
return Heap::code_space();
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
SpaceIterator::SpaceIterator() : current_space_(FIRST_SPACE), iterator_(NULL) {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SpaceIterator::~SpaceIterator() {
|
|
|
|
// Delete active iterator if any.
|
|
|
|
delete iterator_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool SpaceIterator::has_next() {
|
|
|
|
// Iterate until no more spaces.
|
|
|
|
return current_space_ != LAST_SPACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ObjectIterator* SpaceIterator::next() {
|
|
|
|
if (iterator_ != NULL) {
|
|
|
|
delete iterator_;
|
|
|
|
iterator_ = NULL;
|
|
|
|
// Move to the next space
|
|
|
|
current_space_++;
|
|
|
|
if (current_space_ > LAST_SPACE) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return iterator for the new current space.
|
|
|
|
return CreateIterator();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Create an iterator for the space to iterate.
|
|
|
|
ObjectIterator* SpaceIterator::CreateIterator() {
|
|
|
|
ASSERT(iterator_ == NULL);
|
|
|
|
|
|
|
|
switch (current_space_) {
|
|
|
|
case NEW_SPACE:
|
|
|
|
iterator_ = new SemiSpaceIterator(Heap::new_space());
|
|
|
|
break;
|
2008-09-05 12:34:09 +00:00
|
|
|
case OLD_POINTER_SPACE:
|
|
|
|
iterator_ = new HeapObjectIterator(Heap::old_pointer_space());
|
|
|
|
break;
|
|
|
|
case OLD_DATA_SPACE:
|
|
|
|
iterator_ = new HeapObjectIterator(Heap::old_data_space());
|
2008-07-30 08:49:36 +00:00
|
|
|
break;
|
|
|
|
case CODE_SPACE:
|
|
|
|
iterator_ = new HeapObjectIterator(Heap::code_space());
|
|
|
|
break;
|
|
|
|
case MAP_SPACE:
|
|
|
|
iterator_ = new HeapObjectIterator(Heap::map_space());
|
|
|
|
break;
|
2009-07-09 11:13:08 +00:00
|
|
|
case CELL_SPACE:
|
|
|
|
iterator_ = new HeapObjectIterator(Heap::cell_space());
|
|
|
|
break;
|
2008-07-30 08:49:36 +00:00
|
|
|
case LO_SPACE:
|
|
|
|
iterator_ = new LargeObjectIterator(Heap::lo_space());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the newly allocated iterator;
|
|
|
|
ASSERT(iterator_ != NULL);
|
|
|
|
return iterator_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
HeapIterator::HeapIterator() {
|
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HeapIterator::~HeapIterator() {
|
|
|
|
Shutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HeapIterator::Init() {
|
|
|
|
// Start the iteration.
|
|
|
|
space_iterator_ = new SpaceIterator();
|
|
|
|
object_iterator_ = space_iterator_->next();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HeapIterator::Shutdown() {
|
|
|
|
// Make sure the last iterator is deallocated.
|
|
|
|
delete space_iterator_;
|
|
|
|
space_iterator_ = NULL;
|
|
|
|
object_iterator_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-25 22:53:18 +00:00
|
|
|
HeapObject* HeapIterator::next() {
|
2008-07-03 15:10:15 +00:00
|
|
|
// No iterator means we are done.
|
2010-01-25 22:53:18 +00:00
|
|
|
if (object_iterator_ == NULL) return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2010-01-25 22:53:18 +00:00
|
|
|
if (HeapObject* obj = object_iterator_->next_object()) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// If the current iterator has more objects we are fine.
|
2010-01-25 22:53:18 +00:00
|
|
|
return obj;
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
|
|
|
// Go though the spaces looking for one that has objects.
|
|
|
|
while (space_iterator_->has_next()) {
|
|
|
|
object_iterator_ = space_iterator_->next();
|
2010-01-25 22:53:18 +00:00
|
|
|
if (HeapObject* obj = object_iterator_->next_object()) {
|
|
|
|
return obj;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Done with the last space.
|
|
|
|
object_iterator_ = NULL;
|
2010-01-25 22:53:18 +00:00
|
|
|
return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HeapIterator::reset() {
|
|
|
|
// Restart the iterator.
|
|
|
|
Shutdown();
|
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
static bool search_for_any_global;
|
|
|
|
static Object* search_target;
|
|
|
|
static bool found_target;
|
|
|
|
static List<Object*> object_stack(20);
|
|
|
|
|
|
|
|
|
|
|
|
// Tags 0, 1, and 3 are used. Use 2 for marking visited HeapObject.
|
|
|
|
static const int kMarkTag = 2;
|
|
|
|
|
|
|
|
static void MarkObjectRecursively(Object** p);
|
|
|
|
class MarkObjectVisitor : public ObjectVisitor {
|
|
|
|
public:
|
|
|
|
void VisitPointers(Object** start, Object** end) {
|
|
|
|
// Copy all HeapObject pointers in [start, end)
|
|
|
|
for (Object** p = start; p < end; p++) {
|
|
|
|
if ((*p)->IsHeapObject())
|
|
|
|
MarkObjectRecursively(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static MarkObjectVisitor mark_visitor;
|
|
|
|
|
|
|
|
static void MarkObjectRecursively(Object** p) {
|
|
|
|
if (!(*p)->IsHeapObject()) return;
|
|
|
|
|
|
|
|
HeapObject* obj = HeapObject::cast(*p);
|
|
|
|
|
|
|
|
Object* map = obj->map();
|
|
|
|
|
|
|
|
if (!map->IsHeapObject()) return; // visited before
|
|
|
|
|
|
|
|
if (found_target) return; // stop if target found
|
|
|
|
object_stack.Add(obj);
|
|
|
|
if ((search_for_any_global && obj->IsJSGlobalObject()) ||
|
|
|
|
(!search_for_any_global && (obj == search_target))) {
|
|
|
|
found_target = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// not visited yet
|
|
|
|
Map* map_p = reinterpret_cast<Map*>(HeapObject::cast(map));
|
|
|
|
|
|
|
|
Address map_addr = map_p->address();
|
|
|
|
|
|
|
|
obj->set_map(reinterpret_cast<Map*>(map_addr + kMarkTag));
|
|
|
|
|
|
|
|
MarkObjectRecursively(&map);
|
|
|
|
|
|
|
|
obj->IterateBody(map_p->instance_type(), obj->SizeFromMap(map_p),
|
|
|
|
&mark_visitor);
|
|
|
|
|
|
|
|
if (!found_target) // don't pop if found the target
|
|
|
|
object_stack.RemoveLast();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void UnmarkObjectRecursively(Object** p);
|
|
|
|
class UnmarkObjectVisitor : public ObjectVisitor {
|
|
|
|
public:
|
|
|
|
void VisitPointers(Object** start, Object** end) {
|
|
|
|
// Copy all HeapObject pointers in [start, end)
|
|
|
|
for (Object** p = start; p < end; p++) {
|
|
|
|
if ((*p)->IsHeapObject())
|
|
|
|
UnmarkObjectRecursively(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static UnmarkObjectVisitor unmark_visitor;
|
|
|
|
|
|
|
|
static void UnmarkObjectRecursively(Object** p) {
|
|
|
|
if (!(*p)->IsHeapObject()) return;
|
|
|
|
|
|
|
|
HeapObject* obj = HeapObject::cast(*p);
|
|
|
|
|
|
|
|
Object* map = obj->map();
|
|
|
|
|
|
|
|
if (map->IsHeapObject()) return; // unmarked already
|
|
|
|
|
|
|
|
Address map_addr = reinterpret_cast<Address>(map);
|
|
|
|
|
|
|
|
map_addr -= kMarkTag;
|
|
|
|
|
|
|
|
ASSERT_TAG_ALIGNED(map_addr);
|
|
|
|
|
|
|
|
HeapObject* map_p = HeapObject::FromAddress(map_addr);
|
|
|
|
|
|
|
|
obj->set_map(reinterpret_cast<Map*>(map_p));
|
|
|
|
|
|
|
|
UnmarkObjectRecursively(reinterpret_cast<Object**>(&map_p));
|
|
|
|
|
|
|
|
obj->IterateBody(Map::cast(map_p)->instance_type(),
|
|
|
|
obj->SizeFromMap(Map::cast(map_p)),
|
|
|
|
&unmark_visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void MarkRootObjectRecursively(Object** root) {
|
|
|
|
if (search_for_any_global) {
|
|
|
|
ASSERT(search_target == NULL);
|
|
|
|
} else {
|
|
|
|
ASSERT(search_target->IsHeapObject());
|
|
|
|
}
|
|
|
|
found_target = false;
|
|
|
|
object_stack.Clear();
|
|
|
|
|
|
|
|
MarkObjectRecursively(root);
|
|
|
|
UnmarkObjectRecursively(root);
|
|
|
|
|
|
|
|
if (found_target) {
|
|
|
|
PrintF("=====================================\n");
|
|
|
|
PrintF("==== Path to object ====\n");
|
|
|
|
PrintF("=====================================\n\n");
|
|
|
|
|
|
|
|
ASSERT(!object_stack.is_empty());
|
|
|
|
for (int i = 0; i < object_stack.length(); i++) {
|
|
|
|
if (i > 0) PrintF("\n |\n |\n V\n\n");
|
|
|
|
Object* obj = object_stack[i];
|
|
|
|
obj->Print();
|
|
|
|
}
|
|
|
|
PrintF("=====================================\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Helper class for visiting HeapObjects recursively.
|
|
|
|
class MarkRootVisitor: public ObjectVisitor {
|
|
|
|
public:
|
|
|
|
void VisitPointers(Object** start, Object** end) {
|
|
|
|
// Visit all HeapObject pointers in [start, end)
|
|
|
|
for (Object** p = start; p < end; p++) {
|
|
|
|
if ((*p)->IsHeapObject())
|
|
|
|
MarkRootObjectRecursively(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Triggers a depth-first traversal of reachable objects from roots
|
|
|
|
// and finds a path to a specific heap object and prints it.
|
2009-12-09 14:32:45 +00:00
|
|
|
void Heap::TracePathToObject(Object* target) {
|
|
|
|
search_target = target;
|
2008-07-03 15:10:15 +00:00
|
|
|
search_for_any_global = false;
|
|
|
|
|
|
|
|
MarkRootVisitor root_visitor;
|
2009-11-05 15:12:36 +00:00
|
|
|
IterateRoots(&root_visitor, VISIT_ONLY_STRONG);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Triggers a depth-first traversal of reachable objects from roots
|
|
|
|
// and finds a path to any global object and prints it. Useful for
|
|
|
|
// determining the source for leaks of global objects.
|
|
|
|
void Heap::TracePathToGlobal() {
|
|
|
|
search_target = NULL;
|
|
|
|
search_for_any_global = true;
|
|
|
|
|
|
|
|
MarkRootVisitor root_visitor;
|
2009-11-05 15:12:36 +00:00
|
|
|
IterateRoots(&root_visitor, VISIT_ONLY_STRONG);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2008-07-30 08:49:36 +00:00
|
|
|
GCTracer::GCTracer()
|
|
|
|
: start_time_(0.0),
|
|
|
|
start_size_(0.0),
|
|
|
|
gc_count_(0),
|
|
|
|
full_gc_count_(0),
|
|
|
|
is_compacting_(false),
|
|
|
|
marked_count_(0) {
|
|
|
|
// These two fields reflect the state of the previous full collection.
|
|
|
|
// Set them before they are changed by the collector.
|
|
|
|
previous_has_compacted_ = MarkCompactCollector::HasCompacted();
|
|
|
|
previous_marked_count_ = MarkCompactCollector::previous_marked_count();
|
|
|
|
if (!FLAG_trace_gc) return;
|
|
|
|
start_time_ = OS::TimeCurrentMillis();
|
|
|
|
start_size_ = SizeOfHeapObjects();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GCTracer::~GCTracer() {
|
|
|
|
if (!FLAG_trace_gc) return;
|
|
|
|
// Printf ONE line iff flag is set.
|
|
|
|
PrintF("%s %.1f -> %.1f MB, %d ms.\n",
|
|
|
|
CollectorString(),
|
|
|
|
start_size_, SizeOfHeapObjects(),
|
|
|
|
static_cast<int>(OS::TimeCurrentMillis() - start_time_));
|
2009-07-13 21:24:54 +00:00
|
|
|
|
|
|
|
#if defined(ENABLE_LOGGING_AND_PROFILING)
|
|
|
|
Heap::PrintShortHeapStatistics();
|
|
|
|
#endif
|
2008-07-30 08:49:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* GCTracer::CollectorString() {
|
|
|
|
switch (collector_) {
|
|
|
|
case SCAVENGER:
|
|
|
|
return "Scavenge";
|
|
|
|
case MARK_COMPACTOR:
|
|
|
|
return MarkCompactCollector::HasCompacted() ? "Mark-compact"
|
|
|
|
: "Mark-sweep";
|
|
|
|
}
|
|
|
|
return "Unknown GC";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-17 06:07:49 +00:00
|
|
|
int KeyedLookupCache::Hash(Map* map, String* name) {
|
|
|
|
// Uses only lower 32 bits if pointers are larger.
|
|
|
|
uintptr_t addr_hash =
|
2009-12-10 15:10:50 +00:00
|
|
|
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(map)) >> kMapHashShift;
|
|
|
|
return (addr_hash ^ name->Hash()) & kCapacityMask;
|
2009-06-17 06:07:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int KeyedLookupCache::Lookup(Map* map, String* name) {
|
|
|
|
int index = Hash(map, name);
|
|
|
|
Key& key = keys_[index];
|
|
|
|
if ((key.map == map) && key.name->Equals(name)) {
|
|
|
|
return field_offsets_[index];
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KeyedLookupCache::Update(Map* map, String* name, int field_offset) {
|
|
|
|
String* symbol;
|
|
|
|
if (Heap::LookupSymbolIfExists(name, &symbol)) {
|
|
|
|
int index = Hash(map, symbol);
|
|
|
|
Key& key = keys_[index];
|
|
|
|
key.map = map;
|
|
|
|
key.name = symbol;
|
|
|
|
field_offsets_[index] = field_offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KeyedLookupCache::Clear() {
|
|
|
|
for (int index = 0; index < kLength; index++) keys_[index].map = NULL;
|
|
|
|
}
|
|
|
|
|
2009-06-22 08:09:57 +00:00
|
|
|
|
2009-06-17 06:07:49 +00:00
|
|
|
KeyedLookupCache::Key KeyedLookupCache::keys_[KeyedLookupCache::kLength];
|
|
|
|
|
2009-06-22 08:09:57 +00:00
|
|
|
|
2009-06-17 06:07:49 +00:00
|
|
|
int KeyedLookupCache::field_offsets_[KeyedLookupCache::kLength];
|
|
|
|
|
|
|
|
|
2009-06-22 14:29:35 +00:00
|
|
|
void DescriptorLookupCache::Clear() {
|
|
|
|
for (int index = 0; index < kLength; index++) keys_[index].array = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DescriptorLookupCache::Key
|
|
|
|
DescriptorLookupCache::keys_[DescriptorLookupCache::kLength];
|
|
|
|
|
|
|
|
int DescriptorLookupCache::results_[DescriptorLookupCache::kLength];
|
|
|
|
|
|
|
|
|
2008-10-20 07:37:51 +00:00
|
|
|
#ifdef DEBUG
|
2008-10-20 07:31:33 +00:00
|
|
|
bool Heap::GarbageCollectionGreedyCheck() {
|
|
|
|
ASSERT(FLAG_gc_greedy);
|
|
|
|
if (Bootstrapper::IsActive()) return true;
|
|
|
|
if (disallow_allocation_failure()) return true;
|
|
|
|
return CollectGarbage(0, NEW_SPACE);
|
|
|
|
}
|
2008-10-20 07:37:51 +00:00
|
|
|
#endif
|
2008-10-20 07:31:33 +00:00
|
|
|
|
2009-09-01 09:03:58 +00:00
|
|
|
|
|
|
|
TranscendentalCache::TranscendentalCache(TranscendentalCache::Type t)
|
|
|
|
: type_(t) {
|
|
|
|
uint32_t in0 = 0xffffffffu; // Bit-pattern for a NaN that isn't
|
|
|
|
uint32_t in1 = 0xffffffffu; // generated by the FPU.
|
|
|
|
for (int i = 0; i < kCacheSize; i++) {
|
|
|
|
elements_[i].in[0] = in0;
|
|
|
|
elements_[i].in[1] = in1;
|
|
|
|
elements_[i].output = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TranscendentalCache* TranscendentalCache::caches_[kNumberOfCaches];
|
|
|
|
|
|
|
|
|
|
|
|
void TranscendentalCache::Clear() {
|
|
|
|
for (int i = 0; i < kNumberOfCaches; i++) {
|
|
|
|
if (caches_[i] != NULL) {
|
|
|
|
delete caches_[i];
|
|
|
|
caches_[i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-09 14:32:45 +00:00
|
|
|
void ExternalStringTable::CleanUp() {
|
|
|
|
int last = 0;
|
|
|
|
for (int i = 0; i < new_space_strings_.length(); ++i) {
|
|
|
|
if (new_space_strings_[i] == Heap::raw_unchecked_null_value()) continue;
|
|
|
|
if (Heap::InNewSpace(new_space_strings_[i])) {
|
|
|
|
new_space_strings_[last++] = new_space_strings_[i];
|
|
|
|
} else {
|
|
|
|
old_space_strings_.Add(new_space_strings_[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
new_space_strings_.Rewind(last);
|
|
|
|
last = 0;
|
|
|
|
for (int i = 0; i < old_space_strings_.length(); ++i) {
|
|
|
|
if (old_space_strings_[i] == Heap::raw_unchecked_null_value()) continue;
|
|
|
|
ASSERT(!Heap::InNewSpace(old_space_strings_[i]));
|
|
|
|
old_space_strings_[last++] = old_space_strings_[i];
|
|
|
|
}
|
|
|
|
old_space_strings_.Rewind(last);
|
|
|
|
Verify();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExternalStringTable::TearDown() {
|
|
|
|
new_space_strings_.Free();
|
|
|
|
old_space_strings_.Free();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
List<Object*> ExternalStringTable::new_space_strings_;
|
|
|
|
List<Object*> ExternalStringTable::old_space_strings_;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
} } // namespace v8::internal
|