diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime')
31 files changed, 1574 insertions, 1296 deletions
diff --git a/Source/JavaScriptCore/runtime/Collector.cpp b/Source/JavaScriptCore/runtime/Collector.cpp deleted file mode 100644 index 38845ce..0000000 --- a/Source/JavaScriptCore/runtime/Collector.cpp +++ /dev/null @@ -1,1100 +0,0 @@ -/* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2007 Eric Seidel <eric@webkit.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "config.h" -#include "Collector.h" - -#include "ArgList.h" -#include "CallFrame.h" -#include "CodeBlock.h" -#include "CollectorHeapIterator.h" -#include "GCActivityCallback.h" -#include "Interpreter.h" -#include "JSArray.h" -#include "JSGlobalObject.h" -#include "JSLock.h" -#include "JSONObject.h" -#include "JSString.h" -#include "JSValue.h" -#include "JSZombie.h" -#include "MarkStack.h" -#include "Nodes.h" -#include "Tracing.h" -#include <algorithm> -#include <limits.h> -#include <setjmp.h> -#include <stdlib.h> -#include <wtf/FastMalloc.h> -#include <wtf/HashCountedSet.h> -#include <wtf/WTFThreadData.h> -#include <wtf/UnusedParam.h> -#include <wtf/VMTags.h> - -#if OS(DARWIN) - -#include <mach/mach_init.h> -#include <mach/mach_port.h> -#include <mach/task.h> -#include <mach/thread_act.h> -#include <mach/vm_map.h> - -#elif OS(WINDOWS) - -#include <windows.h> -#include <malloc.h> - -#elif OS(HAIKU) - -#include <OS.h> - -#elif OS(UNIX) - -#include <stdlib.h> -#if !OS(HAIKU) -#include <sys/mman.h> -#endif -#include <unistd.h> - -#if OS(SOLARIS) -#include <thread.h> -#else -#include <pthread.h> -#endif - -#if HAVE(PTHREAD_NP_H) -#include <pthread_np.h> -#endif - -#if OS(QNX) -#include <fcntl.h> -#include <sys/procfs.h> -#include <stdio.h> -#include <errno.h> -#endif - -#endif - -#define COLLECT_ON_EVERY_ALLOCATION 0 - -using std::max; - -namespace JSC { - -// tunable parameters - -const size_t GROWTH_FACTOR = 2; -const size_t LOW_WATER_FACTOR = 4; -const size_t ALLOCATIONS_PER_COLLECTION = 3600; -// This value has to be a macro to be used in max() without introducing -// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. -#define MIN_ARRAY_SIZE (static_cast<size_t>(14)) - -#if ENABLE(JSC_MULTIPLE_THREADS) - -#if OS(DARWIN) -typedef mach_port_t PlatformThread; -#elif OS(WINDOWS) -typedef HANDLE PlatformThread; -#endif - -class Heap::Thread { -public: - Thread(pthread_t pthread, const PlatformThread& platThread, void* base) - : posixThread(pthread) - , platformThread(platThread) - , stackBase(base) - { - } - - Thread* next; - pthread_t posixThread; - PlatformThread platformThread; - void* stackBase; -}; - -#endif - -Heap::Heap(JSGlobalData* globalData) - : m_markListSet(0) -#if ENABLE(JSC_MULTIPLE_THREADS) - , m_registeredThreads(0) - , m_currentThreadRegistrar(0) -#endif - , m_globalData(globalData) -{ - ASSERT(globalData); - memset(&m_heap, 0, sizeof(CollectorHeap)); - allocateBlock(); - m_activityCallback = DefaultGCActivityCallback::create(this); - (*m_activityCallback)(); -} - -Heap::~Heap() -{ - // The destroy function must already have been called, so assert this. - ASSERT(!m_globalData); -} - -void Heap::destroy() -{ - JSLock lock(SilenceAssertionsOnly); - - if (!m_globalData) - return; - - ASSERT(!m_globalData->dynamicGlobalObject); - ASSERT(!isBusy()); - - // The global object is not GC protected at this point, so sweeping may delete it - // (and thus the global data) before other objects that may use the global data. - RefPtr<JSGlobalData> protect(m_globalData); - - delete m_markListSet; - m_markListSet = 0; - - freeBlocks(); - -#if ENABLE(JSC_MULTIPLE_THREADS) - if (m_currentThreadRegistrar) { - int error = pthread_key_delete(m_currentThreadRegistrar); - ASSERT_UNUSED(error, !error); - } - - MutexLocker registeredThreadsLock(m_registeredThreadsMutex); - for (Heap::Thread* t = m_registeredThreads; t;) { - Heap::Thread* next = t->next; - delete t; - t = next; - } -#endif - m_globalData = 0; -} - -NEVER_INLINE CollectorBlock* Heap::allocateBlock() -{ - PageAllocationAligned allocation = PageAllocationAligned::allocate(BLOCK_SIZE, BLOCK_SIZE, OSAllocator::JSGCHeapPages); - CollectorBlock* block = static_cast<CollectorBlock*>(allocation.base()); - if (!block) - CRASH(); - - // Initialize block. - - block->heap = this; - clearMarkBits(block); - - Structure* dummyMarkableCellStructure = m_globalData->dummyMarkableCellStructure.get(); - for (size_t i = 0; i < HeapConstants::cellsPerBlock; ++i) - new (&block->cells[i]) JSCell(dummyMarkableCellStructure); - - // Add block to blocks vector. - - size_t numBlocks = m_heap.numBlocks; - if (m_heap.usedBlocks == numBlocks) { - static const size_t maxNumBlocks = ULONG_MAX / sizeof(PageAllocationAligned) / GROWTH_FACTOR; - if (numBlocks > maxNumBlocks) - CRASH(); - numBlocks = max(MIN_ARRAY_SIZE, numBlocks * GROWTH_FACTOR); - m_heap.numBlocks = numBlocks; - m_heap.blocks = static_cast<PageAllocationAligned*>(fastRealloc(m_heap.blocks, numBlocks * sizeof(PageAllocationAligned))); - } - m_heap.blocks[m_heap.usedBlocks++] = allocation; - - return block; -} - -NEVER_INLINE void Heap::freeBlock(size_t block) -{ - m_heap.didShrink = true; - - ObjectIterator it(m_heap, block); - ObjectIterator end(m_heap, block + 1); - for ( ; it != end; ++it) - (*it)->~JSCell(); - m_heap.blocks[block].deallocate(); - - // swap with the last block so we compact as we go - m_heap.blocks[block] = m_heap.blocks[m_heap.usedBlocks - 1]; - m_heap.usedBlocks--; - - if (m_heap.numBlocks > MIN_ARRAY_SIZE && m_heap.usedBlocks < m_heap.numBlocks / LOW_WATER_FACTOR) { - m_heap.numBlocks = m_heap.numBlocks / GROWTH_FACTOR; - m_heap.blocks = static_cast<PageAllocationAligned*>(fastRealloc(m_heap.blocks, m_heap.numBlocks * sizeof(PageAllocationAligned))); - } -} - -void Heap::freeBlocks() -{ - ProtectCountSet protectedValuesCopy = m_protectedValues; - - clearMarkBits(); - ProtectCountSet::iterator protectedValuesEnd = protectedValuesCopy.end(); - for (ProtectCountSet::iterator it = protectedValuesCopy.begin(); it != protectedValuesEnd; ++it) - markCell(it->first); - - m_heap.nextCell = 0; - m_heap.nextBlock = 0; - DeadObjectIterator it(m_heap, m_heap.nextBlock, m_heap.nextCell); - DeadObjectIterator end(m_heap, m_heap.usedBlocks); - for ( ; it != end; ++it) - (*it)->~JSCell(); - - ASSERT(!protectedObjectCount()); - - protectedValuesEnd = protectedValuesCopy.end(); - for (ProtectCountSet::iterator it = protectedValuesCopy.begin(); it != protectedValuesEnd; ++it) - it->first->~JSCell(); - - for (size_t block = 0; block < m_heap.usedBlocks; ++block) - m_heap.blocks[block].deallocate(); - - fastFree(m_heap.blocks); - - memset(&m_heap, 0, sizeof(CollectorHeap)); -} - -void Heap::recordExtraCost(size_t cost) -{ - // Our frequency of garbage collection tries to balance memory use against speed - // by collecting based on the number of newly created values. However, for values - // that hold on to a great deal of memory that's not in the form of other JS values, - // that is not good enough - in some cases a lot of those objects can pile up and - // use crazy amounts of memory without a GC happening. So we track these extra - // memory costs. Only unusually large objects are noted, and we only keep track - // of this extra cost until the next GC. In garbage collected languages, most values - // are either very short lived temporaries, or have extremely long lifetimes. So - // if a large value survives one garbage collection, there is not much point to - // collecting more frequently as long as it stays alive. - - if (m_heap.extraCost > maxExtraCost && m_heap.extraCost > m_heap.usedBlocks * BLOCK_SIZE / 2) { - // If the last iteration through the heap deallocated blocks, we need - // to clean up remaining garbage before marking. Otherwise, the conservative - // marking mechanism might follow a pointer to unmapped memory. - if (m_heap.didShrink) - sweep(); - reset(); - } - m_heap.extraCost += cost; -} - -void* Heap::allocate(size_t s) -{ - ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); - typedef HeapConstants::Block Block; - typedef HeapConstants::Cell Cell; - - ASSERT(JSLock::lockCount() > 0); - ASSERT(JSLock::currentThreadIsHoldingLock()); - ASSERT_UNUSED(s, s <= HeapConstants::cellSize); - - ASSERT(m_heap.operationInProgress == NoOperation); - -#if COLLECT_ON_EVERY_ALLOCATION - collectAllGarbage(); - ASSERT(m_heap.operationInProgress == NoOperation); -#endif - -allocate: - - // Fast case: find the next garbage cell and recycle it. - - do { - ASSERT(m_heap.nextBlock < m_heap.usedBlocks); - Block* block = m_heap.collectorBlock(m_heap.nextBlock); - do { - ASSERT(m_heap.nextCell < HeapConstants::cellsPerBlock); - if (!block->marked.get(m_heap.nextCell)) { // Always false for the last cell in the block - Cell* cell = &block->cells[m_heap.nextCell]; - - m_heap.operationInProgress = Allocation; - JSCell* imp = reinterpret_cast<JSCell*>(cell); - imp->~JSCell(); - m_heap.operationInProgress = NoOperation; - - ++m_heap.nextCell; - return cell; - } - block->marked.advanceToNextPossibleFreeCell(m_heap.nextCell); - } while (m_heap.nextCell != HeapConstants::cellsPerBlock); - m_heap.nextCell = 0; - } while (++m_heap.nextBlock != m_heap.usedBlocks); - - // Slow case: reached the end of the heap. Mark live objects and start over. - - reset(); - goto allocate; -} - -void Heap::resizeBlocks() -{ - m_heap.didShrink = false; - - size_t usedCellCount = markedCells(); - size_t minCellCount = usedCellCount + max(ALLOCATIONS_PER_COLLECTION, usedCellCount); - size_t minBlockCount = (minCellCount + HeapConstants::cellsPerBlock - 1) / HeapConstants::cellsPerBlock; - - size_t maxCellCount = 1.25f * minCellCount; - size_t maxBlockCount = (maxCellCount + HeapConstants::cellsPerBlock - 1) / HeapConstants::cellsPerBlock; - - if (m_heap.usedBlocks < minBlockCount) - growBlocks(minBlockCount); - else if (m_heap.usedBlocks > maxBlockCount) - shrinkBlocks(maxBlockCount); -} - -void Heap::growBlocks(size_t neededBlocks) -{ - ASSERT(m_heap.usedBlocks < neededBlocks); - while (m_heap.usedBlocks < neededBlocks) - allocateBlock(); -} - -void Heap::shrinkBlocks(size_t neededBlocks) -{ - ASSERT(m_heap.usedBlocks > neededBlocks); - - // Clear the always-on last bit, so isEmpty() isn't fooled by it. - for (size_t i = 0; i < m_heap.usedBlocks; ++i) - m_heap.collectorBlock(i)->marked.clear(HeapConstants::cellsPerBlock - 1); - - for (size_t i = 0; i != m_heap.usedBlocks && m_heap.usedBlocks != neededBlocks; ) { - if (m_heap.collectorBlock(i)->marked.isEmpty()) { - freeBlock(i); - } else - ++i; - } - - // Reset the always-on last bit. - for (size_t i = 0; i < m_heap.usedBlocks; ++i) - m_heap.collectorBlock(i)->marked.set(HeapConstants::cellsPerBlock - 1); -} - -#if ENABLE(JSC_MULTIPLE_THREADS) - -static inline PlatformThread getCurrentPlatformThread() -{ -#if OS(DARWIN) - return pthread_mach_thread_np(pthread_self()); -#elif OS(WINDOWS) - return pthread_getw32threadhandle_np(pthread_self()); -#endif -} - -void Heap::makeUsableFromMultipleThreads() -{ - if (m_currentThreadRegistrar) - return; - - int error = pthread_key_create(&m_currentThreadRegistrar, unregisterThread); - if (error) - CRASH(); -} - -void Heap::registerThread() -{ - ASSERT(!m_globalData->exclusiveThread || m_globalData->exclusiveThread == currentThread()); - - if (!m_currentThreadRegistrar || pthread_getspecific(m_currentThreadRegistrar)) - return; - - pthread_setspecific(m_currentThreadRegistrar, this); - Heap::Thread* thread = new Heap::Thread(pthread_self(), getCurrentPlatformThread(), m_globalData->stack().origin()); - - MutexLocker lock(m_registeredThreadsMutex); - - thread->next = m_registeredThreads; - m_registeredThreads = thread; -} - -void Heap::unregisterThread(void* p) -{ - if (p) - static_cast<Heap*>(p)->unregisterThread(); -} - -void Heap::unregisterThread() -{ - pthread_t currentPosixThread = pthread_self(); - - MutexLocker lock(m_registeredThreadsMutex); - - if (pthread_equal(currentPosixThread, m_registeredThreads->posixThread)) { - Thread* t = m_registeredThreads; - m_registeredThreads = m_registeredThreads->next; - delete t; - } else { - Heap::Thread* last = m_registeredThreads; - Heap::Thread* t; - for (t = m_registeredThreads->next; t; t = t->next) { - if (pthread_equal(t->posixThread, currentPosixThread)) { - last->next = t->next; - break; - } - last = t; - } - ASSERT(t); // If t is NULL, we never found ourselves in the list. - delete t; - } -} - -#else // ENABLE(JSC_MULTIPLE_THREADS) - -void Heap::registerThread() -{ -} - -#endif - -inline bool isPointerAligned(void* p) -{ - return (((intptr_t)(p) & (sizeof(char*) - 1)) == 0); -} - -// Cell size needs to be a power of two for isPossibleCell to be valid. -COMPILE_ASSERT(sizeof(CollectorCell) % 2 == 0, Collector_cell_size_is_power_of_two); - -static inline bool isCellAligned(void *p) -{ - return (((intptr_t)(p) & CELL_MASK) == 0); -} - -static inline bool isPossibleCell(void* p) -{ - return isCellAligned(p) && p; -} - -void Heap::markConservatively(MarkStack& markStack, void* start, void* end) -{ -#if OS(WINCE) - if (start > end) { - void* tmp = start; - start = end; - end = tmp; - } -#else - ASSERT(start <= end); -#endif - - ASSERT((static_cast<char*>(end) - static_cast<char*>(start)) < 0x1000000); - ASSERT(isPointerAligned(start)); - ASSERT(isPointerAligned(end)); - - char** p = static_cast<char**>(start); - char** e = static_cast<char**>(end); - - while (p != e) { - char* x = *p++; - if (isPossibleCell(x)) { - size_t usedBlocks; - uintptr_t xAsBits = reinterpret_cast<uintptr_t>(x); - xAsBits &= CELL_ALIGN_MASK; - - uintptr_t offset = xAsBits & BLOCK_OFFSET_MASK; - const size_t lastCellOffset = sizeof(CollectorCell) * (CELLS_PER_BLOCK - 1); - if (offset > lastCellOffset) - continue; - - CollectorBlock* blockAddr = reinterpret_cast<CollectorBlock*>(xAsBits - offset); - usedBlocks = m_heap.usedBlocks; - for (size_t block = 0; block < usedBlocks; block++) { - if (m_heap.collectorBlock(block) != blockAddr) - continue; - markStack.append(reinterpret_cast<JSCell*>(xAsBits)); - } - } - } -} - -void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal(MarkStack& markStack) -{ - markConservatively(markStack, m_globalData->stack().current(), m_globalData->stack().origin()); - markStack.drain(); -} - -#if COMPILER(GCC) -#define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*)))) -#else -#define REGISTER_BUFFER_ALIGNMENT -#endif - -void Heap::markCurrentThreadConservatively(MarkStack& markStack) -{ - // setjmp forces volatile registers onto the stack - jmp_buf registers REGISTER_BUFFER_ALIGNMENT; -#if COMPILER(MSVC) -#pragma warning(push) -#pragma warning(disable: 4611) -#endif - setjmp(registers); -#if COMPILER(MSVC) -#pragma warning(pop) -#endif - - markCurrentThreadConservativelyInternal(markStack); -} - -#if ENABLE(JSC_MULTIPLE_THREADS) - -static inline void suspendThread(const PlatformThread& platformThread) -{ -#if OS(DARWIN) - thread_suspend(platformThread); -#elif OS(WINDOWS) - SuspendThread(platformThread); -#else -#error Need a way to suspend threads on this platform -#endif -} - -static inline void resumeThread(const PlatformThread& platformThread) -{ -#if OS(DARWIN) - thread_resume(platformThread); -#elif OS(WINDOWS) - ResumeThread(platformThread); -#else -#error Need a way to resume threads on this platform -#endif -} - -typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit - -#if OS(DARWIN) - -#if CPU(X86) -typedef i386_thread_state_t PlatformThreadRegisters; -#elif CPU(X86_64) -typedef x86_thread_state64_t PlatformThreadRegisters; -#elif CPU(PPC) -typedef ppc_thread_state_t PlatformThreadRegisters; -#elif CPU(PPC64) -typedef ppc_thread_state64_t PlatformThreadRegisters; -#elif CPU(ARM) -typedef arm_thread_state_t PlatformThreadRegisters; -#else -#error Unknown Architecture -#endif - -#elif OS(WINDOWS) && CPU(X86) -typedef CONTEXT PlatformThreadRegisters; -#else -#error Need a thread register struct for this platform -#endif - -static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs) -{ -#if OS(DARWIN) - -#if CPU(X86) - unsigned user_count = sizeof(regs)/sizeof(int); - thread_state_flavor_t flavor = i386_THREAD_STATE; -#elif CPU(X86_64) - unsigned user_count = x86_THREAD_STATE64_COUNT; - thread_state_flavor_t flavor = x86_THREAD_STATE64; -#elif CPU(PPC) - unsigned user_count = PPC_THREAD_STATE_COUNT; - thread_state_flavor_t flavor = PPC_THREAD_STATE; -#elif CPU(PPC64) - unsigned user_count = PPC_THREAD_STATE64_COUNT; - thread_state_flavor_t flavor = PPC_THREAD_STATE64; -#elif CPU(ARM) - unsigned user_count = ARM_THREAD_STATE_COUNT; - thread_state_flavor_t flavor = ARM_THREAD_STATE; -#else -#error Unknown Architecture -#endif - - kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)®s, &user_count); - if (result != KERN_SUCCESS) { - WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, - "JavaScript garbage collection failed because thread_get_state returned an error (%d). This is probably the result of running inside Rosetta, which is not supported.", result); - CRASH(); - } - return user_count * sizeof(usword_t); -// end OS(DARWIN) - -#elif OS(WINDOWS) && CPU(X86) - regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS; - GetThreadContext(platformThread, ®s); - return sizeof(CONTEXT); -#else -#error Need a way to get thread registers on this platform -#endif -} - -static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) -{ -#if OS(DARWIN) - -#if __DARWIN_UNIX03 - -#if CPU(X86) - return reinterpret_cast<void*>(regs.__esp); -#elif CPU(X86_64) - return reinterpret_cast<void*>(regs.__rsp); -#elif CPU(PPC) || CPU(PPC64) - return reinterpret_cast<void*>(regs.__r1); -#elif CPU(ARM) - return reinterpret_cast<void*>(regs.__sp); -#else -#error Unknown Architecture -#endif - -#else // !__DARWIN_UNIX03 - -#if CPU(X86) - return reinterpret_cast<void*>(regs.esp); -#elif CPU(X86_64) - return reinterpret_cast<void*>(regs.rsp); -#elif CPU(PPC) || CPU(PPC64) - return reinterpret_cast<void*>(regs.r1); -#else -#error Unknown Architecture -#endif - -#endif // __DARWIN_UNIX03 - -// end OS(DARWIN) -#elif CPU(X86) && OS(WINDOWS) - return reinterpret_cast<void*>((uintptr_t) regs.Esp); -#else -#error Need a way to get the stack pointer for another thread on this platform -#endif -} - -void Heap::markOtherThreadConservatively(MarkStack& markStack, Thread* thread) -{ - suspendThread(thread->platformThread); - - PlatformThreadRegisters regs; - size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs); - - // mark the thread's registers - markConservatively(markStack, static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize)); - markStack.drain(); - - void* stackPointer = otherThreadStackPointer(regs); - markConservatively(markStack, stackPointer, thread->stackBase); - markStack.drain(); - - resumeThread(thread->platformThread); -} - -#endif - -void Heap::markStackObjectsConservatively(MarkStack& markStack) -{ - markCurrentThreadConservatively(markStack); - -#if ENABLE(JSC_MULTIPLE_THREADS) - - if (m_currentThreadRegistrar) { - - MutexLocker lock(m_registeredThreadsMutex); - -#ifndef NDEBUG - // Forbid malloc during the mark phase. Marking a thread suspends it, so - // a malloc inside markChildren() would risk a deadlock with a thread that had been - // suspended while holding the malloc lock. - fastMallocForbid(); -#endif - // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held, - // and since this is a shared heap, they are real locks. - for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { - if (!pthread_equal(thread->posixThread, pthread_self())) - markOtherThreadConservatively(markStack, thread); - } -#ifndef NDEBUG - fastMallocAllow(); -#endif - } -#endif -} - -void Heap::updateWeakGCHandles() -{ - for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) - weakGCHandlePool(i)->update(); -} - -void WeakGCHandlePool::update() -{ - for (unsigned i = 1; i < WeakGCHandlePool::numPoolEntries; ++i) { - if (m_entries[i].isValidPtr()) { - JSCell* cell = m_entries[i].get(); - if (!cell || !Heap::isCellMarked(cell)) - m_entries[i].invalidate(); - } - } -} - -WeakGCHandle* Heap::addWeakGCHandle(JSCell* ptr) -{ - for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) - if (!weakGCHandlePool(i)->isFull()) - return weakGCHandlePool(i)->allocate(ptr); - - PageAllocationAligned allocation = PageAllocationAligned::allocate(WeakGCHandlePool::poolSize, WeakGCHandlePool::poolSize, OSAllocator::JSGCHeapPages); - m_weakGCHandlePools.append(allocation); - - WeakGCHandlePool* pool = new (allocation.base()) WeakGCHandlePool(); - return pool->allocate(ptr); -} - -void Heap::protect(JSValue k) -{ - ASSERT(k); - ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); - - if (!k.isCell()) - return; - - m_protectedValues.add(k.asCell()); -} - -bool Heap::unprotect(JSValue k) -{ - ASSERT(k); - ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); - - if (!k.isCell()) - return false; - - return m_protectedValues.remove(k.asCell()); -} - -void Heap::markProtectedObjects(MarkStack& markStack) -{ - ProtectCountSet::iterator end = m_protectedValues.end(); - for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) { - markStack.append(it->first); - markStack.drain(); - } -} - -void Heap::pushTempSortVector(Vector<ValueStringPair>* tempVector) -{ - m_tempSortingVectors.append(tempVector); -} - -void Heap::popTempSortVector(Vector<ValueStringPair>* tempVector) -{ - ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last()); - m_tempSortingVectors.removeLast(); -} - -void Heap::markTempSortVectors(MarkStack& markStack) -{ - typedef Vector<Vector<ValueStringPair>* > VectorOfValueStringVectors; - - VectorOfValueStringVectors::iterator end = m_tempSortingVectors.end(); - for (VectorOfValueStringVectors::iterator it = m_tempSortingVectors.begin(); it != end; ++it) { - Vector<ValueStringPair>* tempSortingVector = *it; - - Vector<ValueStringPair>::iterator vectorEnd = tempSortingVector->end(); - for (Vector<ValueStringPair>::iterator vectorIt = tempSortingVector->begin(); vectorIt != vectorEnd; ++vectorIt) - if (vectorIt->first) - markStack.append(vectorIt->first); - markStack.drain(); - } -} - -void Heap::clearMarkBits() -{ - for (size_t i = 0; i < m_heap.usedBlocks; ++i) - clearMarkBits(m_heap.collectorBlock(i)); -} - -void Heap::clearMarkBits(CollectorBlock* block) -{ - // allocate assumes that the last cell in every block is marked. - block->marked.clearAll(); - block->marked.set(HeapConstants::cellsPerBlock - 1); -} - -size_t Heap::markedCells(size_t startBlock, size_t startCell) const -{ - ASSERT(startBlock <= m_heap.usedBlocks); - ASSERT(startCell < HeapConstants::cellsPerBlock); - - if (startBlock >= m_heap.usedBlocks) - return 0; - - size_t result = 0; - result += m_heap.collectorBlock(startBlock)->marked.count(startCell); - for (size_t i = startBlock + 1; i < m_heap.usedBlocks; ++i) - result += m_heap.collectorBlock(i)->marked.count(); - - return result; -} - -void Heap::sweep() -{ - ASSERT(m_heap.operationInProgress == NoOperation); - if (m_heap.operationInProgress != NoOperation) - CRASH(); - m_heap.operationInProgress = Collection; - -#if !ENABLE(JSC_ZOMBIES) - Structure* dummyMarkableCellStructure = m_globalData->dummyMarkableCellStructure.get(); -#endif - - DeadObjectIterator it(m_heap, m_heap.nextBlock, m_heap.nextCell); - DeadObjectIterator end(m_heap, m_heap.usedBlocks); - for ( ; it != end; ++it) { - JSCell* cell = *it; -#if ENABLE(JSC_ZOMBIES) - if (!cell->isZombie()) { - const ClassInfo* info = cell->classInfo(); - cell->~JSCell(); - new (cell) JSZombie(info, JSZombie::leakedZombieStructure()); - Heap::markCell(cell); - } -#else - cell->~JSCell(); - // Callers of sweep assume it's safe to mark any cell in the heap. - new (cell) JSCell(dummyMarkableCellStructure); -#endif - } - - m_heap.operationInProgress = NoOperation; -} - -void Heap::markRoots() -{ -#ifndef NDEBUG - if (m_globalData->isSharedInstance()) { - ASSERT(JSLock::lockCount() > 0); - ASSERT(JSLock::currentThreadIsHoldingLock()); - } -#endif - - ASSERT(m_heap.operationInProgress == NoOperation); - if (m_heap.operationInProgress != NoOperation) - CRASH(); - - m_heap.operationInProgress = Collection; - - MarkStack& markStack = m_globalData->markStack; - - // Reset mark bits. - clearMarkBits(); - - // Mark stack roots. - markStackObjectsConservatively(markStack); - m_globalData->interpreter->registerFile().markCallFrames(markStack, this); - - // Mark explicitly registered roots. - markProtectedObjects(markStack); - - // Mark temporary vector for Array sorting - markTempSortVectors(markStack); - - // Mark misc. other roots. - if (m_markListSet && m_markListSet->size()) - MarkedArgumentBuffer::markLists(markStack, *m_markListSet); - if (m_globalData->exception) - markStack.append(m_globalData->exception); - if (m_globalData->firstStringifierToMark) - JSONObject::markStringifiers(markStack, m_globalData->firstStringifierToMark); - - // Mark the small strings cache last, since it will clear itself if nothing - // else has marked it. - m_globalData->smallStrings.markChildren(markStack); - - markStack.drain(); - markStack.compact(); - - updateWeakGCHandles(); - - m_heap.operationInProgress = NoOperation; -} - -size_t Heap::objectCount() const -{ - return m_heap.nextBlock * HeapConstants::cellsPerBlock // allocated full blocks - + m_heap.nextCell // allocated cells in current block - + markedCells(m_heap.nextBlock, m_heap.nextCell) // marked cells in remainder of m_heap - - m_heap.usedBlocks; // 1 cell per block is a dummy sentinel -} - -void Heap::addToStatistics(Heap::Statistics& statistics) const -{ - statistics.size += m_heap.usedBlocks * BLOCK_SIZE; - statistics.free += m_heap.usedBlocks * BLOCK_SIZE - (objectCount() * HeapConstants::cellSize); -} - -Heap::Statistics Heap::statistics() const -{ - Statistics statistics = { 0, 0 }; - addToStatistics(statistics); - return statistics; -} - -size_t Heap::size() const -{ - return m_heap.usedBlocks * BLOCK_SIZE; -} - -size_t Heap::globalObjectCount() -{ - size_t count = 0; - if (JSGlobalObject* head = m_globalData->head) { - JSGlobalObject* o = head; - do { - ++count; - o = o->next(); - } while (o != head); - } - return count; -} - -size_t Heap::protectedGlobalObjectCount() -{ - size_t count = 0; - if (JSGlobalObject* head = m_globalData->head) { - JSGlobalObject* o = head; - do { - if (m_protectedValues.contains(o)) - ++count; - o = o->next(); - } while (o != head); - } - - return count; -} - -size_t Heap::protectedObjectCount() -{ - return m_protectedValues.size(); -} - -static const char* typeName(JSCell* cell) -{ - if (cell->isString()) - return "string"; - if (cell->isGetterSetter()) - return "Getter-Setter"; - if (cell->isAPIValueWrapper()) - return "API wrapper"; - if (cell->isPropertyNameIterator()) - return "For-in iterator"; - if (!cell->isObject()) - return "[empty cell]"; - const ClassInfo* info = cell->classInfo(); - return info ? info->className : "Object"; -} - -HashCountedSet<const char*>* Heap::protectedObjectTypeCounts() -{ - HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; - - ProtectCountSet::iterator end = m_protectedValues.end(); - for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) - counts->add(typeName(it->first)); - - return counts; -} - -HashCountedSet<const char*>* Heap::objectTypeCounts() -{ - HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; - - LiveObjectIterator it = primaryHeapBegin(); - LiveObjectIterator heapEnd = primaryHeapEnd(); - for ( ; it != heapEnd; ++it) - counts->add(typeName(*it)); - - return counts; -} - -bool Heap::isBusy() -{ - return m_heap.operationInProgress != NoOperation; -} - -void Heap::reset() -{ - ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); - JAVASCRIPTCORE_GC_BEGIN(); - - markRoots(); - - JAVASCRIPTCORE_GC_MARKED(); - - m_heap.nextCell = 0; - m_heap.nextBlock = 0; - m_heap.nextNumber = 0; - m_heap.extraCost = 0; -#if ENABLE(JSC_ZOMBIES) - sweep(); -#endif - resizeBlocks(); - - JAVASCRIPTCORE_GC_END(); - - (*m_activityCallback)(); -} - -void Heap::collectAllGarbage() -{ - ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); - JAVASCRIPTCORE_GC_BEGIN(); - - // If the last iteration through the heap deallocated blocks, we need - // to clean up remaining garbage before marking. Otherwise, the conservative - // marking mechanism might follow a pointer to unmapped memory. - if (m_heap.didShrink) - sweep(); - - markRoots(); - - JAVASCRIPTCORE_GC_MARKED(); - - m_heap.nextCell = 0; - m_heap.nextBlock = 0; - m_heap.nextNumber = 0; - m_heap.extraCost = 0; - sweep(); - resizeBlocks(); - - JAVASCRIPTCORE_GC_END(); -} - -LiveObjectIterator Heap::primaryHeapBegin() -{ - return LiveObjectIterator(m_heap, 0); -} - -LiveObjectIterator Heap::primaryHeapEnd() -{ - return LiveObjectIterator(m_heap, m_heap.usedBlocks); -} - -void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback) -{ - m_activityCallback = activityCallback; -} - -GCActivityCallback* Heap::activityCallback() -{ - return m_activityCallback.get(); -} - -} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/CollectorHeapIterator.h b/Source/JavaScriptCore/runtime/CollectorHeapIterator.h index 9d107b7..7229d77 100644 --- a/Source/JavaScriptCore/runtime/CollectorHeapIterator.h +++ b/Source/JavaScriptCore/runtime/CollectorHeapIterator.h @@ -24,7 +24,7 @@ */ #include "config.h" -#include "Collector.h" +#include "Heap.h" #ifndef CollectorHeapIterator_h #define CollectorHeapIterator_h diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp index 1ef264c..4a58800 100644 --- a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp +++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp @@ -130,11 +130,16 @@ JSObject* createNotAnObjectError(ExecState* exec, JSValue value) JSObject* createErrorForInvalidGlobalAssignment(ExecState* exec, const UString& propertyName) { return createReferenceError(exec, makeUString("Strict mode forbids implicit creation of global property '", propertyName, "'")); -} +} + +JSObject* createOutOfMemoryError(JSGlobalObject* globalObject) +{ + return createError(globalObject, "Out of memory"); +} JSObject* throwOutOfMemoryError(ExecState* exec) { - return throwError(exec, createError(exec, "Out of memory")); + return throwError(exec, createOutOfMemoryError(exec->lexicalGlobalObject())); } JSObject* throwStackOverflowError(ExecState* exec) diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.h b/Source/JavaScriptCore/runtime/ExceptionHelpers.h index 7edffad..5f1ec6f 100644 --- a/Source/JavaScriptCore/runtime/ExceptionHelpers.h +++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.h @@ -47,6 +47,7 @@ namespace JSC { JSObject* createTerminatedExecutionException(JSGlobalData*); JSObject* createStackOverflowError(ExecState*); JSObject* createStackOverflowError(JSGlobalObject*); + JSObject* createOutOfMemoryError(JSGlobalObject*); JSObject* createUndefinedVariableError(ExecState*, const Identifier&); JSObject* createNotAnObjectError(ExecState*, JSValue); JSObject* createInvalidParamError(ExecState*, const char* op, JSValue); diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp index c7262be..25c551b 100644 --- a/Source/JavaScriptCore/runtime/Executable.cpp +++ b/Source/JavaScriptCore/runtime/Executable.cpp @@ -109,8 +109,12 @@ JSObject* EvalExecutable::compileInternal(ExecState* exec, ScopeChainNode* scope ASSERT(!m_evalCodeBlock); m_evalCodeBlock = adoptPtr(new EvalCodeBlock(this, globalObject, source().provider(), scopeChain.localDepth())); OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(evalNode.get(), scopeChain, m_evalCodeBlock->symbolTable(), m_evalCodeBlock.get()))); - generator->generate(); - + if ((exception = generator->generate())) { + m_evalCodeBlock.clear(); + evalNode->destroyData(); + return exception; + } + evalNode->destroyData(); #if ENABLE(JIT) @@ -157,7 +161,11 @@ JSObject* ProgramExecutable::compileInternal(ExecState* exec, ScopeChainNode* sc m_programCodeBlock = adoptPtr(new ProgramCodeBlock(this, GlobalCode, globalObject, source().provider())); OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(programNode.get(), scopeChain, &globalObject->symbolTable(), m_programCodeBlock.get()))); - generator->generate(); + if ((exception = generator->generate())) { + m_programCodeBlock.clear(); + programNode->destroyData(); + return exception; + } programNode->destroyData(); @@ -194,7 +202,12 @@ JSObject* FunctionExecutable::compileForCallInternal(ExecState* exec, ScopeChain ASSERT(!m_codeBlockForCall); m_codeBlockForCall = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), false)); OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChain, m_codeBlockForCall->symbolTable(), m_codeBlockForCall.get()))); - generator->generate(); + if ((exception = generator->generate())) { + m_codeBlockForCall.clear(); + body->destroyData(); + return exception; + } + m_numParametersForCall = m_codeBlockForCall->m_numParameters; ASSERT(m_numParametersForCall); m_numCapturedVariables = m_codeBlockForCall->m_numCapturedVars; @@ -235,7 +248,12 @@ JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, Scope ASSERT(!m_codeBlockForConstruct); m_codeBlockForConstruct = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), true)); OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChain, m_codeBlockForConstruct->symbolTable(), m_codeBlockForConstruct.get()))); - generator->generate(); + if ((exception = generator->generate())) { + m_codeBlockForConstruct.clear(); + body->destroyData(); + return exception; + } + m_numParametersForConstruct = m_codeBlockForConstruct->m_numParameters; ASSERT(m_numParametersForConstruct); m_numCapturedVariables = m_codeBlockForConstruct->m_numCapturedVars; diff --git a/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp b/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp index 7168a05..414a0ad 100644 --- a/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp +++ b/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp @@ -30,7 +30,7 @@ #include "GCActivityCallback.h" #include "APIShims.h" -#include "Collector.h" +#include "Heap.h" #include "JSGlobalData.h" #include "JSLock.h" #include <wtf/RetainPtr.h> diff --git a/Source/JavaScriptCore/runtime/Heap.cpp b/Source/JavaScriptCore/runtime/Heap.cpp new file mode 100644 index 0000000..a224ee0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Heap.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "Heap.h" + +#include "CollectorHeapIterator.h" +#include "GCActivityCallback.h" +#include "GCHandle.h" +#include "Interpreter.h" +#include "JSGlobalData.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "JSONObject.h" +#include "Tracing.h" + +#define COLLECT_ON_EVERY_ALLOCATION 0 + +namespace JSC { + +Heap::Heap(JSGlobalData* globalData) + : m_markedSpace(globalData) + , m_operationInProgress(NoOperation) + , m_markListSet(0) + , m_activityCallback(DefaultGCActivityCallback::create(this)) + , m_globalData(globalData) + , m_machineStackMarker(&globalData->heap) + , m_extraCost(0) +{ + (*m_activityCallback)(); +} + +Heap::~Heap() +{ + // The destroy function must already have been called, so assert this. + ASSERT(!m_globalData); +} + +void Heap::destroy() +{ + JSLock lock(SilenceAssertionsOnly); + + if (!m_globalData) + return; + + ASSERT(!m_globalData->dynamicGlobalObject); + ASSERT(m_operationInProgress == NoOperation); + + // The global object is not GC protected at this point, so sweeping may delete it + // (and thus the global data) before other objects that may use the global data. + RefPtr<JSGlobalData> protect(m_globalData); + + delete m_markListSet; + m_markListSet = 0; + + ProtectCountSet protectedValuesCopy = m_protectedValues; + m_markedSpace.destroy(protectedValuesCopy); + ASSERT(!protectedObjectCount()); + + m_globalData = 0; +} + +void Heap::recordExtraCost(size_t cost) +{ + // Our frequency of garbage collection tries to balance memory use against speed + // by collecting based on the number of newly created values. However, for values + // that hold on to a great deal of memory that's not in the form of other JS values, + // that is not good enough - in some cases a lot of those objects can pile up and + // use crazy amounts of memory without a GC happening. So we track these extra + // memory costs. Only unusually large objects are noted, and we only keep track + // of this extra cost until the next GC. In garbage collected languages, most values + // are either very short lived temporaries, or have extremely long lifetimes. So + // if a large value survives one garbage collection, there is not much point to + // collecting more frequently as long as it stays alive. + + if (m_extraCost > maxExtraCost && m_extraCost > m_markedSpace.size() / 2) { + JAVASCRIPTCORE_GC_BEGIN(); + + // If the last iteration through the heap deallocated blocks, we need + // to clean up remaining garbage before marking. Otherwise, the conservative + // marking mechanism might follow a pointer to unmapped memory. + if (m_markedSpace.didShrink()) + m_markedSpace.sweep(); + + markRoots(); + + JAVASCRIPTCORE_GC_MARKED(); + + m_markedSpace.reset(); + m_extraCost = 0; + + JAVASCRIPTCORE_GC_END(); + + (*m_activityCallback)(); + } + m_extraCost += cost; +} + +void* Heap::allocate(size_t s) +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + ASSERT_UNUSED(s, s <= HeapConstants::cellSize); + ASSERT(m_operationInProgress == NoOperation); + +#if COLLECT_ON_EVERY_ALLOCATION + collectAllGarbage(); + ASSERT(m_operationInProgress == NoOperation); +#endif + + m_operationInProgress = Allocation; + void* result = m_markedSpace.allocate(s); + m_operationInProgress = NoOperation; + + if (!result) { + JAVASCRIPTCORE_GC_BEGIN(); + + markRoots(); + + JAVASCRIPTCORE_GC_MARKED(); + + m_markedSpace.reset(); + m_extraCost = 0; + + JAVASCRIPTCORE_GC_END(); + + (*m_activityCallback)(); + + m_operationInProgress = Allocation; + result = m_markedSpace.allocate(s); + m_operationInProgress = NoOperation; + } + ASSERT(result); + return result; +} + +void Heap::markConservatively(MarkStack& markStack, void* start, void* end) +{ + m_markedSpace.markConservatively(markStack, start, end); +} + +void Heap::updateWeakGCHandles() +{ + for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) + weakGCHandlePool(i)->update(); +} + +void WeakGCHandlePool::update() +{ + for (unsigned i = 1; i < WeakGCHandlePool::numPoolEntries; ++i) { + if (m_entries[i].isValidPtr()) { + JSCell* cell = m_entries[i].get(); + if (!cell || !Heap::isCellMarked(cell)) + m_entries[i].invalidate(); + } + } +} + +WeakGCHandle* Heap::addWeakGCHandle(JSCell* ptr) +{ + for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) + if (!weakGCHandlePool(i)->isFull()) + return weakGCHandlePool(i)->allocate(ptr); + + PageAllocationAligned allocation = PageAllocationAligned::allocate(WeakGCHandlePool::poolSize, WeakGCHandlePool::poolSize, OSAllocator::JSGCHeapPages); + m_weakGCHandlePools.append(allocation); + + WeakGCHandlePool* pool = new (allocation.base()) WeakGCHandlePool(); + return pool->allocate(ptr); +} + +void Heap::protect(JSValue k) +{ + ASSERT(k); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); + + if (!k.isCell()) + return; + + m_protectedValues.add(k.asCell()); +} + +bool Heap::unprotect(JSValue k) +{ + ASSERT(k); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); + + if (!k.isCell()) + return false; + + return m_protectedValues.remove(k.asCell()); +} + +void Heap::markProtectedObjects(MarkStack& markStack) +{ + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) { + markStack.append(it->first); + markStack.drain(); + } +} + +void Heap::pushTempSortVector(Vector<ValueStringPair>* tempVector) +{ + m_tempSortingVectors.append(tempVector); +} + +void Heap::popTempSortVector(Vector<ValueStringPair>* tempVector) +{ + ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last()); + m_tempSortingVectors.removeLast(); +} + +void Heap::markTempSortVectors(MarkStack& markStack) +{ + typedef Vector<Vector<ValueStringPair>* > VectorOfValueStringVectors; + + VectorOfValueStringVectors::iterator end = m_tempSortingVectors.end(); + for (VectorOfValueStringVectors::iterator it = m_tempSortingVectors.begin(); it != end; ++it) { + Vector<ValueStringPair>* tempSortingVector = *it; + + Vector<ValueStringPair>::iterator vectorEnd = tempSortingVector->end(); + for (Vector<ValueStringPair>::iterator vectorIt = tempSortingVector->begin(); vectorIt != vectorEnd; ++vectorIt) + if (vectorIt->first) + markStack.append(vectorIt->first); + markStack.drain(); + } +} + +void Heap::markRoots() +{ +#ifndef NDEBUG + if (m_globalData->isSharedInstance()) { + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + } +#endif + + ASSERT(m_operationInProgress == NoOperation); + if (m_operationInProgress != NoOperation) + CRASH(); + + m_operationInProgress = Collection; + + MarkStack& markStack = m_globalData->markStack; + + // Reset mark bits. + m_markedSpace.clearMarkBits(); + + // Mark stack roots. + m_machineStackMarker.markMachineStackConservatively(markStack); + m_globalData->interpreter->registerFile().markCallFrames(markStack, this); + + // Mark explicitly registered roots. + markProtectedObjects(markStack); + + // Mark temporary vector for Array sorting + markTempSortVectors(markStack); + + // Mark misc. other roots. + if (m_markListSet && m_markListSet->size()) + MarkedArgumentBuffer::markLists(markStack, *m_markListSet); + if (m_globalData->exception) + markStack.append(m_globalData->exception); + if (m_globalData->firstStringifierToMark) + JSONObject::markStringifiers(markStack, m_globalData->firstStringifierToMark); + + // Mark the small strings cache last, since it will clear itself if nothing + // else has marked it. + m_globalData->smallStrings.markChildren(markStack); + + markStack.drain(); + markStack.compact(); + + updateWeakGCHandles(); + + m_operationInProgress = NoOperation; +} + +size_t Heap::objectCount() const +{ + return m_markedSpace.objectCount(); +} + +MarkedSpace::Statistics Heap::statistics() const +{ + return m_markedSpace.statistics(); +} + +size_t Heap::size() const +{ + return m_markedSpace.size(); +} + +size_t Heap::globalObjectCount() +{ + size_t count = 0; + if (JSGlobalObject* head = m_globalData->head) { + JSGlobalObject* o = head; + do { + ++count; + o = o->next(); + } while (o != head); + } + return count; +} + +size_t Heap::protectedGlobalObjectCount() +{ + size_t count = 0; + if (JSGlobalObject* head = m_globalData->head) { + JSGlobalObject* o = head; + do { + if (m_protectedValues.contains(o)) + ++count; + o = o->next(); + } while (o != head); + } + + return count; +} + +size_t Heap::protectedObjectCount() +{ + return m_protectedValues.size(); +} + +static const char* typeName(JSCell* cell) +{ + if (cell->isString()) + return "string"; + if (cell->isGetterSetter()) + return "Getter-Setter"; + if (cell->isAPIValueWrapper()) + return "API wrapper"; + if (cell->isPropertyNameIterator()) + return "For-in iterator"; + if (!cell->isObject()) + return "[empty cell]"; + const ClassInfo* info = cell->classInfo(); + return info ? info->className : "Object"; +} + +HashCountedSet<const char*>* Heap::protectedObjectTypeCounts() +{ + HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; + + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) + counts->add(typeName(it->first)); + + return counts; +} + +HashCountedSet<const char*>* Heap::objectTypeCounts() +{ + HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; + + LiveObjectIterator it = primaryHeapBegin(); + LiveObjectIterator heapEnd = primaryHeapEnd(); + for ( ; it != heapEnd; ++it) + counts->add(typeName(*it)); + + return counts; +} + +bool Heap::isBusy() +{ + return m_operationInProgress != NoOperation; +} + +void Heap::collectAllGarbage() +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + JAVASCRIPTCORE_GC_BEGIN(); + + // If the last iteration through the heap deallocated blocks, we need + // to clean up remaining garbage before marking. Otherwise, the conservative + // marking mechanism might follow a pointer to unmapped memory. + if (m_markedSpace.didShrink()) + m_markedSpace.sweep(); + + markRoots(); + + JAVASCRIPTCORE_GC_MARKED(); + + m_markedSpace.reset(); + m_markedSpace.sweep(); + m_extraCost = 0; + + JAVASCRIPTCORE_GC_END(); + + (*m_activityCallback)(); +} + +LiveObjectIterator Heap::primaryHeapBegin() +{ + return m_markedSpace.primaryHeapBegin(); +} + +LiveObjectIterator Heap::primaryHeapEnd() +{ + return m_markedSpace.primaryHeapEnd(); +} + +void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback) +{ + m_activityCallback = activityCallback; +} + +GCActivityCallback* Heap::activityCallback() +{ + return m_activityCallback.get(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Heap.h b/Source/JavaScriptCore/runtime/Heap.h new file mode 100644 index 0000000..243bba3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Heap.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef Heap_h +#define Heap_h + +#include "MarkedSpace.h" +#include <wtf/Forward.h> +#include <wtf/HashSet.h> + +#define ASSERT_CLASS_FITS_IN_CELL(class) COMPILE_ASSERT(sizeof(class) <= CELL_SIZE, class_fits_in_cell) + +namespace JSC { + + class JSValue; + class UString; + class GCActivityCallback; + class JSCell; + class JSGlobalData; + class JSValue; + class LiveObjectIterator; + class MarkedArgumentBuffer; + class MarkStack; + class WeakGCHandlePool; + + typedef std::pair<JSValue, UString> ValueStringPair; + + enum OperationInProgress { NoOperation, Allocation, Collection }; + + class Heap : public Noncopyable { + public: + void destroy(); + + void* allocate(size_t); + + bool isBusy(); // true if an allocation or collection is in progress + void collectAllGarbage(); + + GCActivityCallback* activityCallback(); + void setActivityCallback(PassOwnPtr<GCActivityCallback>); + + static const size_t minExtraCost = 256; + static const size_t maxExtraCost = 1024 * 1024; + + void reportExtraMemoryCost(size_t cost); + + size_t objectCount() const; + MarkedSpace::Statistics statistics() const; + size_t size() const; + + void protect(JSValue); + // Returns true if the value is no longer protected by any protect pointers + // (though it may still be alive due to heap/stack references). + bool unprotect(JSValue); + + static Heap* heap(JSValue); // 0 for immediate values + static Heap* heap(JSCell*); + + size_t globalObjectCount(); + size_t protectedObjectCount(); + size_t protectedGlobalObjectCount(); + HashCountedSet<const char*>* protectedObjectTypeCounts(); + HashCountedSet<const char*>* objectTypeCounts(); + + static bool isCellMarked(const JSCell*); + static bool checkMarkCell(const JSCell*); + static void markCell(JSCell*); + + WeakGCHandle* addWeakGCHandle(JSCell*); + + void markConservatively(MarkStack&, void* start, void* end); + + void pushTempSortVector(WTF::Vector<ValueStringPair>*); + void popTempSortVector(WTF::Vector<ValueStringPair>*); + + HashSet<MarkedArgumentBuffer*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<MarkedArgumentBuffer*>; return *m_markListSet; } + + JSGlobalData* globalData() const { return m_globalData; } + + LiveObjectIterator primaryHeapBegin(); + LiveObjectIterator primaryHeapEnd(); + + MachineStackMarker& machineStackMarker() { return m_machineStackMarker; } + + MarkedSpace& markedSpace() { return m_markedSpace; } + + private: + friend class JSGlobalData; + Heap(JSGlobalData*); + ~Heap(); + + void recordExtraCost(size_t); + + void markRoots(); + void markProtectedObjects(MarkStack&); + void markTempSortVectors(MarkStack&); + + void updateWeakGCHandles(); + WeakGCHandlePool* weakGCHandlePool(size_t index); + + MarkedSpace m_markedSpace; + OperationInProgress m_operationInProgress; + + ProtectCountSet m_protectedValues; + WTF::Vector<PageAllocationAligned> m_weakGCHandlePools; + WTF::Vector<WTF::Vector<ValueStringPair>* > m_tempSortingVectors; + + HashSet<MarkedArgumentBuffer*>* m_markListSet; + + OwnPtr<GCActivityCallback> m_activityCallback; + + JSGlobalData* m_globalData; + + MachineStackMarker m_machineStackMarker; + + size_t m_extraCost; + }; + + inline bool Heap::isCellMarked(const JSCell* cell) + { + return MarkedSpace::isCellMarked(cell); + } + + inline bool Heap::checkMarkCell(const JSCell* cell) + { + return MarkedSpace::checkMarkCell(cell); + } + + inline void Heap::markCell(JSCell* cell) + { + MarkedSpace::markCell(cell); + } + + inline void Heap::reportExtraMemoryCost(size_t cost) + { + if (cost > minExtraCost) + recordExtraCost(cost); + } + + inline WeakGCHandlePool* Heap::weakGCHandlePool(size_t index) + { + return static_cast<WeakGCHandlePool*>(m_weakGCHandlePools[index].base()); + } + +} // namespace JSC + +#endif // Heap_h diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.cpp b/Source/JavaScriptCore/runtime/InitializeThreading.cpp index 08dddc1..27611b7 100644 --- a/Source/JavaScriptCore/runtime/InitializeThreading.cpp +++ b/Source/JavaScriptCore/runtime/InitializeThreading.cpp @@ -29,7 +29,7 @@ #include "config.h" #include "InitializeThreading.h" -#include "Collector.h" +#include "Heap.h" #include "dtoa.h" #include "Identifier.h" #include "JSGlobalObject.h" diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index 556a16e..556603b 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -952,8 +952,10 @@ void JSArray::sort(ExecState* exec) for (size_t i = 0; i < lengthNotIncludingUndefined; i++) values[i].second = values[i].first.toString(exec); - if (exec->hadException()) + if (exec->hadException()) { + Heap::heap(this)->popTempSortVector(&values); return; + } // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather // than O(N log N). diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h index 7d4929d..95f4efa 100644 --- a/Source/JavaScriptCore/runtime/JSCell.h +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -25,7 +25,7 @@ #include "CallData.h" #include "ConstructData.h" -#include "Collector.h" +#include "Heap.h" #include "JSImmediate.h" #include "JSValue.h" #include "MarkStack.h" @@ -63,6 +63,7 @@ namespace JSC { friend class JSAPIValueWrapper; friend class JSZombie; friend class JSGlobalData; + friend class MarkedSpace; private: explicit JSCell(Structure*); @@ -362,7 +363,7 @@ namespace JSC { inline Heap* Heap::heap(JSCell* c) { - return cellBlock(c)->heap; + return MarkedSpace::cellBlock(c)->heap; } #if ENABLE(JSC_ZOMBIES) diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp index aca995a..f20a9a4 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -30,7 +30,7 @@ #include "JSGlobalData.h" #include "ArgList.h" -#include "Collector.h" +#include "Heap.h" #include "CollectorHeapIterator.h" #include "CommonIdentifiers.h" #include "FunctionConstructor.h" diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h index 699f975..a24732a 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.h +++ b/Source/JavaScriptCore/runtime/JSGlobalData.h @@ -30,7 +30,7 @@ #define JSGlobalData_h #include "CachedTranscendentalFunction.h" -#include "Collector.h" +#include "Heap.h" #include "DateInstanceCache.h" #include "ExecutableAllocator.h" #include "JITStubs.h" @@ -128,7 +128,7 @@ namespace JSC { #if ENABLE(JSC_MULTIPLE_THREADS) // Will start tracking threads that use the heap, which is resource-heavy. - void makeUsableFromMultipleThreads() { heap.makeUsableFromMultipleThreads(); } + void makeUsableFromMultipleThreads() { heap.machineStackMarker().makeUsableFromMultipleThreads(); } #endif GlobalDataType globalDataType; @@ -229,8 +229,7 @@ namespace JSC { int maxReentryDepth; RegExpCache* m_regExpCache; - - BumpPointerAllocator m_regexAllocator; + BumpPointerAllocator m_regExpAllocator; #if ENABLE(REGEXP_TRACING) typedef ListHashSet<RefPtr<RegExp> > RTTraceList; diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp index 10f4f3f..918141f 100644 --- a/Source/JavaScriptCore/runtime/JSLock.cpp +++ b/Source/JavaScriptCore/runtime/JSLock.cpp @@ -21,7 +21,7 @@ #include "config.h" #include "JSLock.h" -#include "Collector.h" +#include "Heap.h" #include "CallFrame.h" #if ENABLE(JSC_MULTIPLE_THREADS) diff --git a/Source/JavaScriptCore/runtime/JSNumberCell.h b/Source/JavaScriptCore/runtime/JSNumberCell.h index 0040067..1ccdf50 100644 --- a/Source/JavaScriptCore/runtime/JSNumberCell.h +++ b/Source/JavaScriptCore/runtime/JSNumberCell.h @@ -26,7 +26,7 @@ #include "CallFrame.h" #include "JSCell.h" #include "JSImmediate.h" -#include "Collector.h" +#include "Heap.h" #include "UString.h" #include <stddef.h> // for size_t diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index 30e40e4..6ecc73f 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -588,10 +588,28 @@ bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& property } } -static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, JSValue oldValue) +static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor) { if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { - target->putWithAttributes(exec, propertyName, descriptor.value() ? descriptor.value() : oldValue, attributes & ~(Getter | Setter)); + if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) { + GetterSetter* accessor = new (exec) GetterSetter(exec); + if (oldDescriptor.getter()) { + attributes |= Getter; + accessor->setGetter(asObject(oldDescriptor.getter())); + } + if (oldDescriptor.setter()) { + attributes |= Setter; + accessor->setSetter(asObject(oldDescriptor.setter())); + } + target->putWithAttributes(exec, propertyName, accessor, attributes); + return true; + } + JSValue newValue = jsUndefined(); + if (descriptor.value()) + newValue = descriptor.value(); + else if (oldDescriptor.value()) + newValue = oldDescriptor.value(); + target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter)); return true; } attributes &= ~ReadOnly; @@ -608,8 +626,11 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName { // If we have a new property we can just put it on normally PropertyDescriptor current; - if (!getOwnPropertyDescriptor(exec, propertyName, current)) - return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), jsUndefined()); + if (!getOwnPropertyDescriptor(exec, propertyName, current)) { + PropertyDescriptor oldDescriptor; + oldDescriptor.setValue(jsUndefined()); + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor); + } if (descriptor.isEmpty()) return true; @@ -635,7 +656,7 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName if (descriptor.isGenericDescriptor()) { if (!current.attributesEqual(descriptor)) { deleteProperty(exec, propertyName); - putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value()); + putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current); } return true; } @@ -648,7 +669,7 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName return false; } deleteProperty(exec, propertyName); - return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value() ? current.value() : jsUndefined()); + return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current); } // Changing the value and attributes of an existing property @@ -676,7 +697,7 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName return true; } deleteProperty(exec, propertyName); - return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value()); + return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current); } // Changing the accessor functions of an existing accessor property diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h index fefffde..6696404 100644 --- a/Source/JavaScriptCore/runtime/JSString.h +++ b/Source/JavaScriptCore/runtime/JSString.h @@ -314,11 +314,15 @@ namespace JSC { ~JSString() { ASSERT(vptr() == JSGlobalData::jsStringVPtr); - for (unsigned i = 0; i < m_fiberCount; ++i) - RopeImpl::deref(m_other.m_fibers[i]); - - if (!m_fiberCount && m_other.m_finalizerCallback) - m_other.m_finalizerCallback(this, m_other.m_finalizerContext); + if (!m_fiberCount) { + if (m_other.m_finalizerCallback) + m_other.m_finalizerCallback(this, m_other.m_finalizerContext); + } else { + unsigned i = 0; + do + RopeImpl::deref(m_other.m_fibers[i]); + while (++i < m_fiberCount); + } } const UString& value(ExecState* exec) const @@ -624,8 +628,7 @@ namespace JSC { inline UString JSValue::toPrimitiveString(ExecState* exec) const { - if (isString()) - return static_cast<JSString*>(asCell())->value(exec); + ASSERT(!isString()); if (isInt32()) return exec->globalData().numericStrings.add(asInt32()); if (isDouble()) diff --git a/Source/JavaScriptCore/runtime/JSValue.h b/Source/JavaScriptCore/runtime/JSValue.h index dc54f40..cad9662 100644 --- a/Source/JavaScriptCore/runtime/JSValue.h +++ b/Source/JavaScriptCore/runtime/JSValue.h @@ -763,8 +763,7 @@ namespace JSC { return asValue() == jsNull(); } #endif // USE(JSVALUE32_64) - - typedef std::pair<JSValue, UString> ValueStringPair; + } // namespace JSC #endif // JSValue_h diff --git a/Source/JavaScriptCore/runtime/MachineStackMarker.cpp b/Source/JavaScriptCore/runtime/MachineStackMarker.cpp new file mode 100644 index 0000000..b4a1936 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MachineStackMarker.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "MachineStackMarker.h" + +#include "Heap.h" +#include "JSArray.h" +#include "JSGlobalData.h" +#include <setjmp.h> +#include <stdlib.h> + +#if OS(DARWIN) + +#include <mach/mach_init.h> +#include <mach/mach_port.h> +#include <mach/task.h> +#include <mach/thread_act.h> +#include <mach/vm_map.h> + +#elif OS(WINDOWS) + +#include <windows.h> +#include <malloc.h> + +#elif OS(HAIKU) + +#include <OS.h> + +#elif OS(UNIX) + +#include <stdlib.h> +#if !OS(HAIKU) +#include <sys/mman.h> +#endif +#include <unistd.h> + +#if OS(SOLARIS) +#include <thread.h> +#else +#include <pthread.h> +#endif + +#if HAVE(PTHREAD_NP_H) +#include <pthread_np.h> +#endif + +#if OS(QNX) +#include <fcntl.h> +#include <sys/procfs.h> +#include <stdio.h> +#include <errno.h> +#endif + +#endif + +namespace JSC { + +#if ENABLE(JSC_MULTIPLE_THREADS) + +#if OS(DARWIN) +typedef mach_port_t PlatformThread; +#elif OS(WINDOWS) +typedef HANDLE PlatformThread; +#endif + +class MachineStackMarker::Thread { +public: + Thread(pthread_t pthread, const PlatformThread& platThread, void* base) + : posixThread(pthread) + , platformThread(platThread) + , stackBase(base) + { + } + + Thread* next; + pthread_t posixThread; + PlatformThread platformThread; + void* stackBase; +}; + +#endif + +MachineStackMarker::MachineStackMarker(Heap* heap) + : m_heap(heap) +#if ENABLE(JSC_MULTIPLE_THREADS) + , m_registeredThreads(0) + , m_currentThreadRegistrar(0) +#endif +{ +} + +MachineStackMarker::~MachineStackMarker() +{ +#if ENABLE(JSC_MULTIPLE_THREADS) + if (m_currentThreadRegistrar) { + int error = pthread_key_delete(m_currentThreadRegistrar); + ASSERT_UNUSED(error, !error); + } + + MutexLocker registeredThreadsLock(m_registeredThreadsMutex); + for (Thread* t = m_registeredThreads; t;) { + Thread* next = t->next; + delete t; + t = next; + } +#endif +} + +#if ENABLE(JSC_MULTIPLE_THREADS) + +static inline PlatformThread getCurrentPlatformThread() +{ +#if OS(DARWIN) + return pthread_mach_thread_np(pthread_self()); +#elif OS(WINDOWS) + return pthread_getw32threadhandle_np(pthread_self()); +#endif +} + +void MachineStackMarker::makeUsableFromMultipleThreads() +{ + if (m_currentThreadRegistrar) + return; + + int error = pthread_key_create(&m_currentThreadRegistrar, unregisterThread); + if (error) + CRASH(); +} + +void MachineStackMarker::registerThread() +{ + ASSERT(!m_heap->globalData()->exclusiveThread || m_heap->globalData()->exclusiveThread == currentThread()); + + if (!m_currentThreadRegistrar || pthread_getspecific(m_currentThreadRegistrar)) + return; + + pthread_setspecific(m_currentThreadRegistrar, this); + Thread* thread = new Thread(pthread_self(), getCurrentPlatformThread(), m_heap->globalData()->stack().origin()); + + MutexLocker lock(m_registeredThreadsMutex); + + thread->next = m_registeredThreads; + m_registeredThreads = thread; +} + +void MachineStackMarker::unregisterThread(void* p) +{ + if (p) + static_cast<MachineStackMarker*>(p)->unregisterThread(); +} + +void MachineStackMarker::unregisterThread() +{ + pthread_t currentPosixThread = pthread_self(); + + MutexLocker lock(m_registeredThreadsMutex); + + if (pthread_equal(currentPosixThread, m_registeredThreads->posixThread)) { + Thread* t = m_registeredThreads; + m_registeredThreads = m_registeredThreads->next; + delete t; + } else { + Thread* last = m_registeredThreads; + Thread* t; + for (t = m_registeredThreads->next; t; t = t->next) { + if (pthread_equal(t->posixThread, currentPosixThread)) { + last->next = t->next; + break; + } + last = t; + } + ASSERT(t); // If t is NULL, we never found ourselves in the list. + delete t; + } +} + +#endif + +void NEVER_INLINE MachineStackMarker::markCurrentThreadConservativelyInternal(MarkStack& markStack) +{ + m_heap->markConservatively(markStack, m_heap->globalData()->stack().current(), m_heap->globalData()->stack().origin()); + markStack.drain(); +} + +#if COMPILER(GCC) +#define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*)))) +#else +#define REGISTER_BUFFER_ALIGNMENT +#endif + +void MachineStackMarker::markCurrentThreadConservatively(MarkStack& markStack) +{ + // setjmp forces volatile registers onto the stack + jmp_buf registers REGISTER_BUFFER_ALIGNMENT; +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4611) +#endif + setjmp(registers); +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + + markCurrentThreadConservativelyInternal(markStack); +} + +#if ENABLE(JSC_MULTIPLE_THREADS) + +static inline void suspendThread(const PlatformThread& platformThread) +{ +#if OS(DARWIN) + thread_suspend(platformThread); +#elif OS(WINDOWS) + SuspendThread(platformThread); +#else +#error Need a way to suspend threads on this platform +#endif +} + +static inline void resumeThread(const PlatformThread& platformThread) +{ +#if OS(DARWIN) + thread_resume(platformThread); +#elif OS(WINDOWS) + ResumeThread(platformThread); +#else +#error Need a way to resume threads on this platform +#endif +} + +typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit + +#if OS(DARWIN) + +#if CPU(X86) +typedef i386_thread_state_t PlatformThreadRegisters; +#elif CPU(X86_64) +typedef x86_thread_state64_t PlatformThreadRegisters; +#elif CPU(PPC) +typedef ppc_thread_state_t PlatformThreadRegisters; +#elif CPU(PPC64) +typedef ppc_thread_state64_t PlatformThreadRegisters; +#elif CPU(ARM) +typedef arm_thread_state_t PlatformThreadRegisters; +#else +#error Unknown Architecture +#endif + +#elif OS(WINDOWS) && CPU(X86) +typedef CONTEXT PlatformThreadRegisters; +#else +#error Need a thread register struct for this platform +#endif + +static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs) +{ +#if OS(DARWIN) + +#if CPU(X86) + unsigned user_count = sizeof(regs)/sizeof(int); + thread_state_flavor_t flavor = i386_THREAD_STATE; +#elif CPU(X86_64) + unsigned user_count = x86_THREAD_STATE64_COUNT; + thread_state_flavor_t flavor = x86_THREAD_STATE64; +#elif CPU(PPC) + unsigned user_count = PPC_THREAD_STATE_COUNT; + thread_state_flavor_t flavor = PPC_THREAD_STATE; +#elif CPU(PPC64) + unsigned user_count = PPC_THREAD_STATE64_COUNT; + thread_state_flavor_t flavor = PPC_THREAD_STATE64; +#elif CPU(ARM) + unsigned user_count = ARM_THREAD_STATE_COUNT; + thread_state_flavor_t flavor = ARM_THREAD_STATE; +#else +#error Unknown Architecture +#endif + + kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)®s, &user_count); + if (result != KERN_SUCCESS) { + WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, + "JavaScript garbage collection failed because thread_get_state returned an error (%d). This is probably the result of running inside Rosetta, which is not supported.", result); + CRASH(); + } + return user_count * sizeof(usword_t); +// end OS(DARWIN) + +#elif OS(WINDOWS) && CPU(X86) + regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS; + GetThreadContext(platformThread, ®s); + return sizeof(CONTEXT); +#else +#error Need a way to get thread registers on this platform +#endif +} + +static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) +{ +#if OS(DARWIN) + +#if __DARWIN_UNIX03 + +#if CPU(X86) + return reinterpret_cast<void*>(regs.__esp); +#elif CPU(X86_64) + return reinterpret_cast<void*>(regs.__rsp); +#elif CPU(PPC) || CPU(PPC64) + return reinterpret_cast<void*>(regs.__r1); +#elif CPU(ARM) + return reinterpret_cast<void*>(regs.__sp); +#else +#error Unknown Architecture +#endif + +#else // !__DARWIN_UNIX03 + +#if CPU(X86) + return reinterpret_cast<void*>(regs.esp); +#elif CPU(X86_64) + return reinterpret_cast<void*>(regs.rsp); +#elif CPU(PPC) || CPU(PPC64) + return reinterpret_cast<void*>(regs.r1); +#else +#error Unknown Architecture +#endif + +#endif // __DARWIN_UNIX03 + +// end OS(DARWIN) +#elif CPU(X86) && OS(WINDOWS) + return reinterpret_cast<void*>((uintptr_t) regs.Esp); +#else +#error Need a way to get the stack pointer for another thread on this platform +#endif +} + +void MachineStackMarker::markOtherThreadConservatively(MarkStack& markStack, Thread* thread) +{ + suspendThread(thread->platformThread); + + PlatformThreadRegisters regs; + size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs); + + // mark the thread's registers + m_heap->markConservatively(markStack, static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize)); + markStack.drain(); + + void* stackPointer = otherThreadStackPointer(regs); + m_heap->markConservatively(markStack, stackPointer, thread->stackBase); + markStack.drain(); + + resumeThread(thread->platformThread); +} + +#endif + +void MachineStackMarker::markMachineStackConservatively(MarkStack& markStack) +{ + markCurrentThreadConservatively(markStack); + +#if ENABLE(JSC_MULTIPLE_THREADS) + + if (m_currentThreadRegistrar) { + + MutexLocker lock(m_registeredThreadsMutex); + +#ifndef NDEBUG + // Forbid malloc during the mark phase. Marking a thread suspends it, so + // a malloc inside markChildren() would risk a deadlock with a thread that had been + // suspended while holding the malloc lock. + fastMallocForbid(); +#endif + // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held, + // and since this is a shared heap, they are real locks. + for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { + if (!pthread_equal(thread->posixThread, pthread_self())) + markOtherThreadConservatively(markStack, thread); + } +#ifndef NDEBUG + fastMallocAllow(); +#endif + } +#endif +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/MachineStackMarker.h b/Source/JavaScriptCore/runtime/MachineStackMarker.h new file mode 100644 index 0000000..e80fe05 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MachineStackMarker.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef MachineStackMarker_h +#define MachineStackMarker_h + +#include <wtf/Noncopyable.h> +#include <wtf/ThreadingPrimitives.h> + +#if ENABLE(JSC_MULTIPLE_THREADS) +#include <pthread.h> +#endif + +namespace JSC { + + class Heap; + class MarkStack; + + class MachineStackMarker : public Noncopyable { + public: + MachineStackMarker(Heap*); + ~MachineStackMarker(); + + void markMachineStackConservatively(MarkStack&); + +#if ENABLE(JSC_MULTIPLE_THREADS) + void makeUsableFromMultipleThreads(); + void registerThread(); // Only needs to be called by clients that can use the same heap from multiple threads. +#endif + + private: + void markCurrentThreadConservatively(MarkStack&); + void markCurrentThreadConservativelyInternal(MarkStack&); + +#if ENABLE(JSC_MULTIPLE_THREADS) + class Thread; + + static void unregisterThread(void*); + + void unregisterThread(); + void markOtherThreadConservatively(MarkStack&, Thread*); +#endif + + Heap* m_heap; + +#if ENABLE(JSC_MULTIPLE_THREADS) + Mutex m_registeredThreadsMutex; + Thread* m_registeredThreads; + pthread_key_t m_currentThreadRegistrar; +#endif + }; + +} // namespace JSC + +#endif // MachineStackMarker_h diff --git a/Source/JavaScriptCore/runtime/MarkedSpace.cpp b/Source/JavaScriptCore/runtime/MarkedSpace.cpp new file mode 100644 index 0000000..4bc3c18 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MarkedSpace.cpp @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "MarkedSpace.h" + +#include "CollectorHeapIterator.h" +#include "JSCell.h" +#include "JSGlobalData.h" +#include "JSLock.h" + +using std::max; + +namespace JSC { + +class Structure; + +// tunable parameters + +const size_t GROWTH_FACTOR = 2; +const size_t LOW_WATER_FACTOR = 4; +const size_t ALLOCATIONS_PER_COLLECTION = 3600; +// This value has to be a macro to be used in max() without introducing +// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. +#define MIN_ARRAY_SIZE (static_cast<size_t>(14)) + +MarkedSpace::MarkedSpace(JSGlobalData* globalData) + : m_globalData(globalData) +{ + memset(&m_heap, 0, sizeof(CollectorHeap)); + allocateBlock(); +} + +void MarkedSpace::destroy(ProtectCountSet& protectedValuesCopy) +{ + clearMarkBits(); + ProtectCountSet::iterator protectedValuesEnd = protectedValuesCopy.end(); + for (ProtectCountSet::iterator it = protectedValuesCopy.begin(); it != protectedValuesEnd; ++it) + markCell(it->first); + + m_heap.nextCell = 0; + m_heap.nextBlock = 0; + DeadObjectIterator it(m_heap, m_heap.nextBlock, m_heap.nextCell); + DeadObjectIterator end(m_heap, m_heap.usedBlocks); + for ( ; it != end; ++it) + (*it)->~JSCell(); + + protectedValuesEnd = protectedValuesCopy.end(); + for (ProtectCountSet::iterator it = protectedValuesCopy.begin(); it != protectedValuesEnd; ++it) + it->first->~JSCell(); + + for (size_t block = 0; block < m_heap.usedBlocks; ++block) + m_heap.blocks[block].deallocate(); + + fastFree(m_heap.blocks); + + memset(&m_heap, 0, sizeof(CollectorHeap)); +} + +NEVER_INLINE CollectorBlock* MarkedSpace::allocateBlock() +{ + PageAllocationAligned allocation = PageAllocationAligned::allocate(BLOCK_SIZE, BLOCK_SIZE, OSAllocator::JSGCHeapPages); + CollectorBlock* block = static_cast<CollectorBlock*>(allocation.base()); + if (!block) + CRASH(); + + // Initialize block. + + block->heap = &globalData()->heap; + clearMarkBits(block); + + Structure* dummyMarkableCellStructure = globalData()->dummyMarkableCellStructure.get(); + for (size_t i = 0; i < HeapConstants::cellsPerBlock; ++i) + new (&block->cells[i]) JSCell(dummyMarkableCellStructure); + + // Add block to blocks vector. + + size_t numBlocks = m_heap.numBlocks; + if (m_heap.usedBlocks == numBlocks) { + static const size_t maxNumBlocks = ULONG_MAX / sizeof(PageAllocationAligned) / GROWTH_FACTOR; + if (numBlocks > maxNumBlocks) + CRASH(); + numBlocks = max(MIN_ARRAY_SIZE, numBlocks * GROWTH_FACTOR); + m_heap.numBlocks = numBlocks; + m_heap.blocks = static_cast<PageAllocationAligned*>(fastRealloc(m_heap.blocks, numBlocks * sizeof(PageAllocationAligned))); + } + m_heap.blocks[m_heap.usedBlocks++] = allocation; + + return block; +} + +NEVER_INLINE void MarkedSpace::freeBlock(size_t block) +{ + m_heap.didShrink = true; + + ObjectIterator it(m_heap, block); + ObjectIterator end(m_heap, block + 1); + for ( ; it != end; ++it) + (*it)->~JSCell(); + m_heap.blocks[block].deallocate(); + + // swap with the last block so we compact as we go + m_heap.blocks[block] = m_heap.blocks[m_heap.usedBlocks - 1]; + m_heap.usedBlocks--; + + if (m_heap.numBlocks > MIN_ARRAY_SIZE && m_heap.usedBlocks < m_heap.numBlocks / LOW_WATER_FACTOR) { + m_heap.numBlocks = m_heap.numBlocks / GROWTH_FACTOR; + m_heap.blocks = static_cast<PageAllocationAligned*>(fastRealloc(m_heap.blocks, m_heap.numBlocks * sizeof(PageAllocationAligned))); + } +} + +void* MarkedSpace::allocate(size_t s) +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + typedef HeapConstants::Block Block; + typedef HeapConstants::Cell Cell; + + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + ASSERT_UNUSED(s, s <= HeapConstants::cellSize); + + // Fast case: find the next garbage cell and recycle it. + + do { + ASSERT(m_heap.nextBlock < m_heap.usedBlocks); + Block* block = m_heap.collectorBlock(m_heap.nextBlock); + do { + ASSERT(m_heap.nextCell < HeapConstants::cellsPerBlock); + if (!block->marked.get(m_heap.nextCell)) { // Always false for the last cell in the block + Cell* cell = &block->cells[m_heap.nextCell]; + + JSCell* imp = reinterpret_cast<JSCell*>(cell); + imp->~JSCell(); + + ++m_heap.nextCell; + return cell; + } + block->marked.advanceToNextPossibleFreeCell(m_heap.nextCell); + } while (m_heap.nextCell != HeapConstants::cellsPerBlock); + m_heap.nextCell = 0; + } while (++m_heap.nextBlock != m_heap.usedBlocks); + + return 0; +} + +void MarkedSpace::resizeBlocks() +{ + m_heap.didShrink = false; + + size_t usedCellCount = markedCells(); + size_t minCellCount = usedCellCount + max(ALLOCATIONS_PER_COLLECTION, usedCellCount); + size_t minBlockCount = (minCellCount + HeapConstants::cellsPerBlock - 1) / HeapConstants::cellsPerBlock; + + size_t maxCellCount = 1.25f * minCellCount; + size_t maxBlockCount = (maxCellCount + HeapConstants::cellsPerBlock - 1) / HeapConstants::cellsPerBlock; + + if (m_heap.usedBlocks < minBlockCount) + growBlocks(minBlockCount); + else if (m_heap.usedBlocks > maxBlockCount) + shrinkBlocks(maxBlockCount); +} + +void MarkedSpace::growBlocks(size_t neededBlocks) +{ + ASSERT(m_heap.usedBlocks < neededBlocks); + while (m_heap.usedBlocks < neededBlocks) + allocateBlock(); +} + +void MarkedSpace::shrinkBlocks(size_t neededBlocks) +{ + ASSERT(m_heap.usedBlocks > neededBlocks); + + // Clear the always-on last bit, so isEmpty() isn't fooled by it. + for (size_t i = 0; i < m_heap.usedBlocks; ++i) + m_heap.collectorBlock(i)->marked.clear(HeapConstants::cellsPerBlock - 1); + + for (size_t i = 0; i != m_heap.usedBlocks && m_heap.usedBlocks != neededBlocks; ) { + if (m_heap.collectorBlock(i)->marked.isEmpty()) { + freeBlock(i); + } else + ++i; + } + + // Reset the always-on last bit. + for (size_t i = 0; i < m_heap.usedBlocks; ++i) + m_heap.collectorBlock(i)->marked.set(HeapConstants::cellsPerBlock - 1); +} + +inline bool isPointerAligned(void* p) +{ + return (((intptr_t)(p) & (sizeof(char*) - 1)) == 0); +} + +// Cell size needs to be a power of two for isPossibleCell to be valid. +COMPILE_ASSERT(sizeof(CollectorCell) % 2 == 0, Collector_cell_size_is_power_of_two); + +static inline bool isCellAligned(void *p) +{ + return (((intptr_t)(p) & CELL_MASK) == 0); +} + +static inline bool isPossibleCell(void* p) +{ + return isCellAligned(p) && p; +} + +void MarkedSpace::markConservatively(MarkStack& markStack, void* start, void* end) +{ +#if OS(WINCE) + if (start > end) { + void* tmp = start; + start = end; + end = tmp; + } +#else + ASSERT(start <= end); +#endif + + ASSERT((static_cast<char*>(end) - static_cast<char*>(start)) < 0x1000000); + ASSERT(isPointerAligned(start)); + ASSERT(isPointerAligned(end)); + + char** p = static_cast<char**>(start); + char** e = static_cast<char**>(end); + + while (p != e) { + char* x = *p++; + if (isPossibleCell(x)) { + size_t usedBlocks; + uintptr_t xAsBits = reinterpret_cast<uintptr_t>(x); + xAsBits &= CELL_ALIGN_MASK; + + uintptr_t offset = xAsBits & BLOCK_OFFSET_MASK; + const size_t lastCellOffset = sizeof(CollectorCell) * (CELLS_PER_BLOCK - 1); + if (offset > lastCellOffset) + continue; + + CollectorBlock* blockAddr = reinterpret_cast<CollectorBlock*>(xAsBits - offset); + usedBlocks = m_heap.usedBlocks; + for (size_t block = 0; block < usedBlocks; block++) { + if (m_heap.collectorBlock(block) != blockAddr) + continue; + markStack.append(reinterpret_cast<JSCell*>(xAsBits)); + } + } + } +} + +void MarkedSpace::clearMarkBits() +{ + for (size_t i = 0; i < m_heap.usedBlocks; ++i) + clearMarkBits(m_heap.collectorBlock(i)); +} + +void MarkedSpace::clearMarkBits(CollectorBlock* block) +{ + // allocate assumes that the last cell in every block is marked. + block->marked.clearAll(); + block->marked.set(HeapConstants::cellsPerBlock - 1); +} + +size_t MarkedSpace::markedCells(size_t startBlock, size_t startCell) const +{ + ASSERT(startBlock <= m_heap.usedBlocks); + ASSERT(startCell < HeapConstants::cellsPerBlock); + + if (startBlock >= m_heap.usedBlocks) + return 0; + + size_t result = 0; + result += m_heap.collectorBlock(startBlock)->marked.count(startCell); + for (size_t i = startBlock + 1; i < m_heap.usedBlocks; ++i) + result += m_heap.collectorBlock(i)->marked.count(); + + return result; +} + +void MarkedSpace::sweep() +{ +#if !ENABLE(JSC_ZOMBIES) + Structure* dummyMarkableCellStructure = globalData()->dummyMarkableCellStructure.get(); +#endif + + DeadObjectIterator it(m_heap, m_heap.nextBlock, m_heap.nextCell); + DeadObjectIterator end(m_heap, m_heap.usedBlocks); + for ( ; it != end; ++it) { + JSCell* cell = *it; +#if ENABLE(JSC_ZOMBIES) + if (!cell->isZombie()) { + const ClassInfo* info = cell->classInfo(); + cell->~JSCell(); + new (cell) JSZombie(info, JSZombie::leakedZombieStructure()); + Heap::markCell(cell); + } +#else + cell->~JSCell(); + // Callers of sweep assume it's safe to mark any cell in the heap. + new (cell) JSCell(dummyMarkableCellStructure); +#endif + } +} + +size_t MarkedSpace::objectCount() const +{ + return m_heap.nextBlock * HeapConstants::cellsPerBlock // allocated full blocks + + m_heap.nextCell // allocated cells in current block + + markedCells(m_heap.nextBlock, m_heap.nextCell) // marked cells in remainder of m_heap + - m_heap.usedBlocks; // 1 cell per block is a dummy sentinel +} + +void MarkedSpace::addToStatistics(Statistics& statistics) const +{ + statistics.size += m_heap.usedBlocks * BLOCK_SIZE; + statistics.free += m_heap.usedBlocks * BLOCK_SIZE - (objectCount() * HeapConstants::cellSize); +} + +MarkedSpace::Statistics MarkedSpace::statistics() const +{ + Statistics statistics = { 0, 0 }; + addToStatistics(statistics); + return statistics; +} + +size_t MarkedSpace::size() const +{ + return m_heap.usedBlocks * BLOCK_SIZE; +} + +void MarkedSpace::reset() +{ + m_heap.nextCell = 0; + m_heap.nextBlock = 0; +#if ENABLE(JSC_ZOMBIES) + sweep(); +#endif + resizeBlocks(); +} + +LiveObjectIterator MarkedSpace::primaryHeapBegin() +{ + return LiveObjectIterator(m_heap, 0); +} + +LiveObjectIterator MarkedSpace::primaryHeapEnd() +{ + return LiveObjectIterator(m_heap, m_heap.usedBlocks); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Collector.h b/Source/JavaScriptCore/runtime/MarkedSpace.h index a4e2fe1..78f918c 100644 --- a/Source/JavaScriptCore/runtime/Collector.h +++ b/Source/JavaScriptCore/runtime/MarkedSpace.h @@ -19,44 +19,25 @@ * */ -#ifndef Collector_h -#define Collector_h - -#include "GCHandle.h" -#include "JSValue.h" -#include <stddef.h> -#include <string.h> -#include <wtf/Bitmap.h> +#ifndef MarkedSpace_h +#define MarkedSpace_h + +#include "MachineStackMarker.h" +#include "PageAllocationAligned.h" #include <wtf/FixedArray.h> #include <wtf/HashCountedSet.h> -#include <wtf/HashSet.h> #include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> -#include <wtf/PageAllocation.h> -#include <wtf/PageAllocationAligned.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/StdLibExtras.h> -#include <wtf/Threading.h> - -#if ENABLE(JSC_MULTIPLE_THREADS) -#include <pthread.h> -#endif - -#define ASSERT_CLASS_FITS_IN_CELL(class) COMPILE_ASSERT(sizeof(class) <= CELL_SIZE, class_fits_in_cell) +#include <wtf/Vector.h> namespace JSC { class CollectorBlock; - class GCActivityCallback; + class Heap; class JSCell; class JSGlobalData; - class JSValue; - class MarkedArgumentBuffer; - class MarkStack; - - enum OperationInProgress { NoOperation, Allocation, Collection }; - class LiveObjectIterator; + class MarkStack; + class WeakGCHandle; #if OS(WINCE) || OS(SYMBIAN) || PLATFORM(BREWMP) const size_t BLOCK_SIZE = 64 * 1024; // 64k @@ -64,47 +45,31 @@ namespace JSC { const size_t BLOCK_SIZE = 256 * 1024; // 256k #endif + typedef HashCountedSet<JSCell*> ProtectCountSet; + struct CollectorHeap { size_t nextBlock; size_t nextCell; PageAllocationAligned* blocks; - void* nextNumber; - size_t numBlocks; size_t usedBlocks; - size_t extraCost; bool didShrink; - OperationInProgress operationInProgress; - CollectorBlock* collectorBlock(size_t index) const { return static_cast<CollectorBlock*>(blocks[index].base()); } }; - class Heap : public Noncopyable { + class MarkedSpace : public Noncopyable { public: - class Thread; + MarkedSpace(JSGlobalData*); + void destroy(ProtectCountSet&); - void destroy(); - - void* allocateNumber(size_t); void* allocate(size_t); - bool isBusy(); // true if an allocation or collection is in progress - void collectAllGarbage(); - - GCActivityCallback* activityCallback(); - void setActivityCallback(PassOwnPtr<GCActivityCallback>); - - static const size_t minExtraCost = 256; - static const size_t maxExtraCost = 1024 * 1024; - - void reportExtraMemoryCost(size_t cost); - size_t objectCount() const; struct Statistics { size_t size; @@ -113,22 +78,8 @@ namespace JSC { Statistics statistics() const; size_t size() const; - void protect(JSValue); - // Returns true if the value is no longer protected by any protect pointers - // (though it may still be alive due to heap/stack references). - bool unprotect(JSValue); - - static Heap* heap(JSValue); // 0 for immediate values static Heap* heap(JSCell*); - size_t globalObjectCount(); - size_t protectedObjectCount(); - size_t protectedGlobalObjectCount(); - HashCountedSet<const char*>* protectedObjectTypeCounts(); - HashCountedSet<const char*>* objectTypeCounts(); - - void registerThread(); // Only needs to be called by clients that can use the same heap from multiple threads. - static bool isCellMarked(const JSCell*); static bool checkMarkCell(const JSCell*); static void markCell(JSCell*); @@ -137,30 +88,21 @@ namespace JSC { void markConservatively(MarkStack&, void* start, void* end); - void pushTempSortVector(WTF::Vector<ValueStringPair>*); - void popTempSortVector(WTF::Vector<ValueStringPair>*); - - HashSet<MarkedArgumentBuffer*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<MarkedArgumentBuffer*>; return *m_markListSet; } - - JSGlobalData* globalData() const { return m_globalData; } static bool isNumber(JSCell*); LiveObjectIterator primaryHeapBegin(); LiveObjectIterator primaryHeapEnd(); - private: - void reset(); - void sweep(); + JSGlobalData* globalData() { return m_globalData; } + static CollectorBlock* cellBlock(const JSCell*); static size_t cellOffset(const JSCell*); - friend class JSGlobalData; - Heap(JSGlobalData*); - ~Heap(); + void reset(); + void sweep(); NEVER_INLINE CollectorBlock* allocateBlock(); NEVER_INLINE void freeBlock(size_t); - void freeBlocks(); void resizeBlocks(); void growBlocks(size_t neededBlocks); void shrinkBlocks(size_t neededBlocks); @@ -168,44 +110,14 @@ namespace JSC { void clearMarkBits(CollectorBlock*); size_t markedCells(size_t startBlock = 0, size_t startCell = 0) const; - void recordExtraCost(size_t); - void addToStatistics(Statistics&) const; void markRoots(); - void markProtectedObjects(MarkStack&); - void markTempSortVectors(MarkStack&); - void markCurrentThreadConservatively(MarkStack&); - void markCurrentThreadConservativelyInternal(MarkStack&); - void markOtherThreadConservatively(MarkStack&, Thread*); - void markStackObjectsConservatively(MarkStack&); - - void updateWeakGCHandles(); - WeakGCHandlePool* weakGCHandlePool(size_t index); - - typedef HashCountedSet<JSCell*> ProtectCountSet; + bool didShrink() { return m_heap.didShrink; } + + private: CollectorHeap m_heap; - - ProtectCountSet m_protectedValues; - WTF::Vector<PageAllocationAligned> m_weakGCHandlePools; - WTF::Vector<WTF::Vector<ValueStringPair>* > m_tempSortingVectors; - - HashSet<MarkedArgumentBuffer*>* m_markListSet; - - OwnPtr<GCActivityCallback> m_activityCallback; - -#if ENABLE(JSC_MULTIPLE_THREADS) - void makeUsableFromMultipleThreads(); - - static void unregisterThread(void*); - void unregisterThread(); - - Mutex m_registeredThreadsMutex; - Thread* m_registeredThreads; - pthread_key_t m_currentThreadRegistrar; -#endif - JSGlobalData* m_globalData; }; @@ -219,7 +131,7 @@ namespace JSC { const size_t SMALL_CELL_SIZE = CELL_SIZE / 2; const size_t CELL_MASK = CELL_SIZE - 1; const size_t CELL_ALIGN_MASK = ~CELL_MASK; - const size_t CELLS_PER_BLOCK = (BLOCK_SIZE - sizeof(Heap*)) * 8 * CELL_SIZE / (8 * CELL_SIZE + 1) / CELL_SIZE; // one bitmap byte can represent 8 cells. + const size_t CELLS_PER_BLOCK = (BLOCK_SIZE - sizeof(MarkedSpace*)) * 8 * CELL_SIZE / (8 * CELL_SIZE + 1) / CELL_SIZE; // one bitmap byte can represent 8 cells. const size_t BITMAP_SIZE = (CELLS_PER_BLOCK + 7) / 8; const size_t BITMAP_WORDS = (BITMAP_SIZE + 3) / sizeof(uint32_t); @@ -283,54 +195,31 @@ namespace JSC { typedef CollectorBlock Block; }; - inline CollectorBlock* Heap::cellBlock(const JSCell* cell) + inline CollectorBlock* MarkedSpace::cellBlock(const JSCell* cell) { return reinterpret_cast<CollectorBlock*>(reinterpret_cast<uintptr_t>(cell) & BLOCK_MASK); } - inline size_t Heap::cellOffset(const JSCell* cell) + inline size_t MarkedSpace::cellOffset(const JSCell* cell) { return (reinterpret_cast<uintptr_t>(cell) & BLOCK_OFFSET_MASK) / CELL_SIZE; } - inline bool Heap::isCellMarked(const JSCell* cell) + inline bool MarkedSpace::isCellMarked(const JSCell* cell) { return cellBlock(cell)->marked.get(cellOffset(cell)); } - inline bool Heap::checkMarkCell(const JSCell* cell) + inline bool MarkedSpace::checkMarkCell(const JSCell* cell) { return cellBlock(cell)->marked.getset(cellOffset(cell)); } - inline void Heap::markCell(JSCell* cell) + inline void MarkedSpace::markCell(JSCell* cell) { cellBlock(cell)->marked.set(cellOffset(cell)); } - inline void Heap::reportExtraMemoryCost(size_t cost) - { - if (cost > minExtraCost) - recordExtraCost(cost); - } - - inline void* Heap::allocateNumber(size_t s) - { - if (void* result = m_heap.nextNumber) { - m_heap.nextNumber = 0; - return result; - } - - void* result = allocate(s); - m_heap.nextNumber = static_cast<char*>(result) + (CELL_SIZE / 2); - return result; - } - - - inline WeakGCHandlePool* Heap::weakGCHandlePool(size_t index) - { - return static_cast<WeakGCHandlePool*>(m_weakGCHandlePools[index].base()); - } } // namespace JSC -#endif /* Collector_h */ +#endif // MarkedSpace_h diff --git a/Source/JavaScriptCore/runtime/MemoryStatistics.cpp b/Source/JavaScriptCore/runtime/MemoryStatistics.cpp index 7fafa9c..06a319b 100644 --- a/Source/JavaScriptCore/runtime/MemoryStatistics.cpp +++ b/Source/JavaScriptCore/runtime/MemoryStatistics.cpp @@ -32,7 +32,7 @@ namespace JSC { -Heap::Statistics heapStatistics(JSGlobalData* commonGlobalData) +MarkedSpace::Statistics heapStatistics(JSGlobalData* commonGlobalData) { return commonGlobalData->heap.statistics(); } diff --git a/Source/JavaScriptCore/runtime/MemoryStatistics.h b/Source/JavaScriptCore/runtime/MemoryStatistics.h index 1b92eb9..2659702 100644 --- a/Source/JavaScriptCore/runtime/MemoryStatistics.h +++ b/Source/JavaScriptCore/runtime/MemoryStatistics.h @@ -26,7 +26,7 @@ #ifndef MemoryStatistics_h #define MemoryStatistics_h -#include "Collector.h" +#include "Heap.h" class JSGlobalData; @@ -37,7 +37,7 @@ struct GlobalMemoryStatistics { size_t JITBytes; }; -Heap::Statistics heapStatistics(JSGlobalData* commonGlobalData); +MarkedSpace::Statistics heapStatistics(JSGlobalData* commonGlobalData); GlobalMemoryStatistics globalMemoryStatistics(); } diff --git a/Source/JavaScriptCore/runtime/Protect.h b/Source/JavaScriptCore/runtime/Protect.h index 06cf97f..0c1b5e8 100644 --- a/Source/JavaScriptCore/runtime/Protect.h +++ b/Source/JavaScriptCore/runtime/Protect.h @@ -22,7 +22,7 @@ #ifndef Protect_h #define Protect_h -#include "Collector.h" +#include "Heap.h" #include "JSValue.h" namespace JSC { diff --git a/Source/JavaScriptCore/runtime/RegExp.cpp b/Source/JavaScriptCore/runtime/RegExp.cpp index 664a7fb..31a1abe 100644 --- a/Source/JavaScriptCore/runtime/RegExp.cpp +++ b/Source/JavaScriptCore/runtime/RegExp.cpp @@ -22,22 +22,20 @@ #include "config.h" #include "RegExp.h" + #include "Lexer.h" +#include "yarr/Yarr.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wtf/Assertions.h> #include <wtf/OwnArrayPtr.h> -#include "yarr/RegexJIT.h" -#include "yarr/RegexInterpreter.h" -#include "yarr/RegexPattern.h" - namespace JSC { struct RegExpRepresentation { #if ENABLE(YARR_JIT) - Yarr::RegexCodeBlock m_regExpJITCode; + Yarr::YarrCodeBlock m_regExpJITCode; #endif OwnPtr<Yarr::BytecodePattern> m_regExpBytecode; }; @@ -82,7 +80,7 @@ PassRefPtr<RegExp> RegExp::create(JSGlobalData* globalData, const UString& patte RegExp::RegExpState RegExp::compile(JSGlobalData* globalData) { - Yarr::RegexPattern pattern(m_patternString, ignoreCase(), multiline(), &m_constructionError); + Yarr::YarrPattern pattern(m_patternString, ignoreCase(), multiline(), &m_constructionError); if (m_constructionError) return ParseError; @@ -92,7 +90,7 @@ RegExp::RegExpState RegExp::compile(JSGlobalData* globalData) #if ENABLE(YARR_JIT) if (!pattern.m_containsBackreferences && globalData->canUseJIT()) { - Yarr::jitCompileRegex(pattern, globalData, m_representation->m_regExpJITCode); + Yarr::jitCompile(pattern, globalData, m_representation->m_regExpJITCode); #if ENABLE(YARR_JIT_DEBUG) if (!m_representation->m_regExpJITCode.isFallBack()) res = JITCode; @@ -105,7 +103,7 @@ RegExp::RegExpState RegExp::compile(JSGlobalData* globalData) } #endif - m_representation->m_regExpBytecode = Yarr::byteCompileRegex(pattern, &globalData->m_regexAllocator); + m_representation->m_regExpBytecode = Yarr::byteCompile(pattern, &globalData->m_regExpAllocator); return res; } @@ -144,13 +142,13 @@ int RegExp::match(const UString& s, int startOffset, Vector<int, 32>* ovector) int result; #if ENABLE(YARR_JIT) if (m_state == JITCode) { - result = Yarr::executeRegex(m_representation->m_regExpJITCode, s.characters(), startOffset, s.length(), offsetVector); + result = Yarr::execute(m_representation->m_regExpJITCode, s.characters(), startOffset, s.length(), offsetVector); #if ENABLE(YARR_JIT_DEBUG) matchCompareWithInterpreter(s, startOffset, offsetVector, result); #endif } else #endif - result = Yarr::interpretRegex(m_representation->m_regExpBytecode.get(), s.characters(), startOffset, s.length(), offsetVector); + result = Yarr::interpret(m_representation->m_regExpBytecode.get(), s.characters(), startOffset, s.length(), offsetVector); ASSERT(result >= -1); #if ENABLE(REGEXP_TRACING) @@ -181,7 +179,7 @@ void RegExp::matchCompareWithInterpreter(const UString& s, int startOffset, int* for (unsigned j = 0, i = 0; i < m_numSubpatterns + 1; j += 2, i++) interpreterOffsetVector[j] = -1; - interpreterResult = Yarr::interpretRegex(m_representation->m_regExpBytecode.get(), s.characters(), startOffset, s.length(), interpreterOffsetVector); + interpreterResult = Yarr::interpret(m_representation->m_regExpBytecode.get(), s.characters(), startOffset, s.length(), interpreterOffsetVector); if (jitResult != interpreterResult) differences++; @@ -230,7 +228,7 @@ void RegExp::matchCompareWithInterpreter(const UString& s, int startOffset, int* snprintf(formattedPattern, 41, (pattLen <= 38) ? "/%.38s/" : "/%.36s...", rawPattern); #if ENABLE(YARR_JIT) - Yarr::RegexCodeBlock& codeBlock = m_representation->m_regExpJITCode; + Yarr::YarrCodeBlock& codeBlock = m_representation->m_regExpJITCode; char jitAddr[20]; if (m_state == JITCode) diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp index 21ca170..30d3eab 100644 --- a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -307,7 +307,7 @@ JSObject* constructRegExp(ExecState* exec, const ArgList& args) RefPtr<RegExp> regExp = exec->globalData().regExpCache()->lookupOrCreate(pattern, flags); if (!regExp->isValid()) - return throwError(exec, createSyntaxError(exec, makeUString("Invalid regular expression: ", regExp->errorMessage()))); + return throwError(exec, createSyntaxError(exec, regExp->errorMessage())); return new (exec) RegExpObject(exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->regExpStructure(), regExp.release()); } diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp index 0a4c8bf..04bcc3b 100644 --- a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp @@ -95,7 +95,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec) } if (!regExp->isValid()) - return throwVMError(exec, createSyntaxError(exec, makeUString("Invalid regular expression: ", regExp->errorMessage()))); + return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage())); asRegExpObject(thisValue)->setRegExp(regExp.release()); asRegExpObject(thisValue)->setLastIndex(0); diff --git a/Source/JavaScriptCore/runtime/UString.cpp b/Source/JavaScriptCore/runtime/UString.cpp index b3cd40c..b70d505 100644 --- a/Source/JavaScriptCore/runtime/UString.cpp +++ b/Source/JavaScriptCore/runtime/UString.cpp @@ -25,7 +25,7 @@ #include "UString.h" #include "JSGlobalObjectFunctions.h" -#include "Collector.h" +#include "Heap.h" #include "Identifier.h" #include "Operations.h" #include <ctype.h> diff --git a/Source/JavaScriptCore/runtime/WeakGCMap.h b/Source/JavaScriptCore/runtime/WeakGCMap.h index 6b96a74..2d4e59d 100644 --- a/Source/JavaScriptCore/runtime/WeakGCMap.h +++ b/Source/JavaScriptCore/runtime/WeakGCMap.h @@ -26,7 +26,7 @@ #ifndef WeakGCMap_h #define WeakGCMap_h -#include "Collector.h" +#include "Heap.h" #include <wtf/HashMap.h> namespace JSC { diff --git a/Source/JavaScriptCore/runtime/WeakGCPtr.h b/Source/JavaScriptCore/runtime/WeakGCPtr.h index ac77cf3..6cc75a5 100644 --- a/Source/JavaScriptCore/runtime/WeakGCPtr.h +++ b/Source/JavaScriptCore/runtime/WeakGCPtr.h @@ -26,7 +26,7 @@ #ifndef WeakGCPtr_h #define WeakGCPtr_h -#include "Collector.h" +#include "Heap.h" #include "GCHandle.h" #include <wtf/Noncopyable.h> |