1b238d9a8a09cf2ca0fbe55f284cb8d67934e412
[WebKit-https.git] / JavaScriptCore / runtime / Collector.cpp
1 /*
2  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3  *  Copyright (C) 2007 Eric Seidel <eric@webkit.org>
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #include "config.h"
22 #include "Collector.h"
23
24 #include "ArgList.h"
25 #include "CallFrame.h"
26 #include "CodeBlock.h"
27 #include "CollectorHeapIterator.h"
28 #include "Interpreter.h"
29 #include "JSArray.h"
30 #include "JSGlobalObject.h"
31 #include "JSLock.h"
32 #include "JSONObject.h"
33 #include "JSString.h"
34 #include "JSValue.h"
35 #include "MarkStack.h"
36 #include "Nodes.h"
37 #include "Tracing.h"
38 #include <algorithm>
39 #include <limits.h>
40 #include <setjmp.h>
41 #include <stdlib.h>
42 #include <wtf/FastMalloc.h>
43 #include <wtf/HashCountedSet.h>
44 #include <wtf/UnusedParam.h>
45 #include <wtf/VMTags.h>
46
47 #if PLATFORM(DARWIN)
48
49 #include <mach/mach_init.h>
50 #include <mach/mach_port.h>
51 #include <mach/task.h>
52 #include <mach/thread_act.h>
53 #include <mach/vm_map.h>
54
55 #elif PLATFORM(SYMBIAN)
56 #include <e32std.h>
57 #include <e32cmn.h>
58 #include <unistd.h>
59
60 #elif PLATFORM(WIN_OS)
61
62 #include <windows.h>
63 #include <malloc.h>
64
65 #elif PLATFORM(HAIKU)
66
67 #include <OS.h>
68
69 #elif PLATFORM(UNIX)
70
71 #include <stdlib.h>
72 #if !PLATFORM(HAIKU)
73 #include <sys/mman.h>
74 #endif
75 #include <unistd.h>
76
77 #if PLATFORM(SOLARIS)
78 #include <thread.h>
79 #else
80 #include <pthread.h>
81 #endif
82
83 #if HAVE(PTHREAD_NP_H)
84 #include <pthread_np.h>
85 #endif
86
87 #if PLATFORM(QNX)
88 #include <fcntl.h>
89 #include <sys/procfs.h>
90 #include <stdio.h>
91 #include <errno.h>
92 #endif
93
94 #endif
95
96 #define COLLECT_ON_EVERY_ALLOCATION 0
97
98 using std::max;
99
100 namespace JSC {
101
102 // tunable parameters
103
104 const size_t GROWTH_FACTOR = 2;
105 const size_t LOW_WATER_FACTOR = 4;
106 const size_t ALLOCATIONS_PER_COLLECTION = 4000;
107 // This value has to be a macro to be used in max() without introducing
108 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
109 #define MIN_ARRAY_SIZE (static_cast<size_t>(14))
110
111 #if PLATFORM(SYMBIAN)
112 const size_t MAX_NUM_BLOCKS = 256; // Max size of collector heap set to 16 MB
113 static RHeap* userChunk = 0;
114 #endif
115
116 #if ENABLE(JSC_MULTIPLE_THREADS)
117
118 #if PLATFORM(DARWIN)
119 typedef mach_port_t PlatformThread;
120 #elif PLATFORM(WIN_OS)
121 struct PlatformThread {
122     PlatformThread(DWORD _id, HANDLE _handle) : id(_id), handle(_handle) {}
123     DWORD id;
124     HANDLE handle;
125 };
126 #endif
127
128 class Heap::Thread {
129 public:
130     Thread(pthread_t pthread, const PlatformThread& platThread, void* base) 
131         : posixThread(pthread)
132         , platformThread(platThread)
133         , stackBase(base)
134     {
135     }
136
137     Thread* next;
138     pthread_t posixThread;
139     PlatformThread platformThread;
140     void* stackBase;
141 };
142
143 #endif
144
145 Heap::Heap(JSGlobalData* globalData)
146     : m_markListSet(0)
147 #if ENABLE(JSC_MULTIPLE_THREADS)
148     , m_registeredThreads(0)
149     , m_currentThreadRegistrar(0)
150 #endif
151     , m_globalData(globalData)
152 {
153     ASSERT(globalData);
154
155 #if PLATFORM(SYMBIAN)
156     // Symbian OpenC supports mmap but currently not the MAP_ANON flag.
157     // Using fastMalloc() does not properly align blocks on 64k boundaries
158     // and previous implementation was flawed/incomplete.
159     // UserHeap::ChunkHeap allows allocation of continuous memory and specification
160     // of alignment value for (symbian) cells within that heap.
161     //
162     // Clarification and mapping of terminology:
163     // RHeap (created by UserHeap::ChunkHeap below) is continuos memory chunk,
164     // which can dynamically grow up to 8 MB,
165     // that holds all CollectorBlocks of this session (static).
166     // Each symbian cell within RHeap maps to a 64kb aligned CollectorBlock.
167     // JSCell objects are maintained as usual within CollectorBlocks.
168     if (!userChunk) {
169         userChunk = UserHeap::ChunkHeap(0, 0, MAX_NUM_BLOCKS * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
170         if (!userChunk)
171             CRASH();
172     }
173 #endif // PLATFORM(SYMBIAN)
174     
175     memset(&primaryHeap, 0, sizeof(CollectorHeap));
176     memset(&numberHeap, 0, sizeof(CollectorHeap));
177 }
178
179 Heap::~Heap()
180 {
181     // The destroy function must already have been called, so assert this.
182     ASSERT(!m_globalData);
183 }
184
185 void Heap::destroy()
186 {
187     JSLock lock(SilenceAssertionsOnly);
188
189     if (!m_globalData)
190         return;
191
192     // The global object is not GC protected at this point, so sweeping may delete it
193     // (and thus the global data) before other objects that may use the global data.
194     RefPtr<JSGlobalData> protect(m_globalData);
195
196     delete m_markListSet;
197     m_markListSet = 0;
198
199     sweep<PrimaryHeap>();
200     // No need to sweep number heap, because the JSNumber destructor doesn't do anything.
201
202     ASSERT(!primaryHeap.numLiveObjects);
203
204     freeBlocks(&primaryHeap);
205     freeBlocks(&numberHeap);
206
207 #if ENABLE(JSC_MULTIPLE_THREADS)
208     if (m_currentThreadRegistrar) {
209         int error = pthread_key_delete(m_currentThreadRegistrar);
210         ASSERT_UNUSED(error, !error);
211     }
212
213     MutexLocker registeredThreadsLock(m_registeredThreadsMutex);
214     for (Heap::Thread* t = m_registeredThreads; t;) {
215         Heap::Thread* next = t->next;
216         delete t;
217         t = next;
218     }
219 #endif
220
221     m_globalData = 0;
222 }
223
224 template <HeapType heapType>
225 NEVER_INLINE CollectorBlock* Heap::allocateBlock()
226 {
227 #if PLATFORM(DARWIN)
228     vm_address_t address = 0;
229     // FIXME: tag the region as a JavaScriptCore heap when we get a registered VM tag: <rdar://problem/6054788>.
230     vm_map(current_task(), &address, BLOCK_SIZE, BLOCK_OFFSET_MASK, VM_FLAGS_ANYWHERE | VM_TAG_FOR_COLLECTOR_MEMORY, MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_DEFAULT);
231 #elif PLATFORM(SYMBIAN)
232     // Allocate a 64 kb aligned CollectorBlock
233     unsigned char* mask = reinterpret_cast<unsigned char*>(userChunk->Alloc(BLOCK_SIZE));
234     if (!mask)
235         CRASH();
236     uintptr_t address = reinterpret_cast<uintptr_t>(mask);
237
238     memset(reinterpret_cast<void*>(address), 0, BLOCK_SIZE);
239 #elif PLATFORM(WINCE)
240     void* address = VirtualAlloc(NULL, BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
241 #elif PLATFORM(WIN_OS)
242 #if COMPILER(MINGW)
243     void* address = __mingw_aligned_malloc(BLOCK_SIZE, BLOCK_SIZE);
244 #else
245     void* address = _aligned_malloc(BLOCK_SIZE, BLOCK_SIZE);
246 #endif
247     memset(address, 0, BLOCK_SIZE);
248 #elif HAVE(POSIX_MEMALIGN)
249     void* address;
250     posix_memalign(&address, BLOCK_SIZE, BLOCK_SIZE);
251     memset(address, 0, BLOCK_SIZE);
252 #else
253
254 #if ENABLE(JSC_MULTIPLE_THREADS)
255 #error Need to initialize pagesize safely.
256 #endif
257     static size_t pagesize = getpagesize();
258
259     size_t extra = 0;
260     if (BLOCK_SIZE > pagesize)
261         extra = BLOCK_SIZE - pagesize;
262
263     void* mmapResult = mmap(NULL, BLOCK_SIZE + extra, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
264     uintptr_t address = reinterpret_cast<uintptr_t>(mmapResult);
265
266     size_t adjust = 0;
267     if ((address & BLOCK_OFFSET_MASK) != 0)
268         adjust = BLOCK_SIZE - (address & BLOCK_OFFSET_MASK);
269
270     if (adjust > 0)
271         munmap(reinterpret_cast<char*>(address), adjust);
272
273     if (adjust < extra)
274         munmap(reinterpret_cast<char*>(address + adjust + BLOCK_SIZE), extra - adjust);
275
276     address += adjust;
277     memset(reinterpret_cast<void*>(address), 0, BLOCK_SIZE);
278 #endif
279
280     CollectorBlock* block = reinterpret_cast<CollectorBlock*>(address);
281     block->freeList = block->cells;
282     block->heap = this;
283     block->type = heapType;
284
285     CollectorHeap& heap = heapType == PrimaryHeap ? primaryHeap : numberHeap;
286     size_t numBlocks = heap.numBlocks;
287     if (heap.usedBlocks == numBlocks) {
288         static const size_t maxNumBlocks = ULONG_MAX / sizeof(CollectorBlock*) / GROWTH_FACTOR;
289         if (numBlocks > maxNumBlocks)
290             CRASH();
291         numBlocks = max(MIN_ARRAY_SIZE, numBlocks * GROWTH_FACTOR);
292         heap.numBlocks = numBlocks;
293         heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, numBlocks * sizeof(CollectorBlock*)));
294     }
295     heap.blocks[heap.usedBlocks++] = block;
296
297     return block;
298 }
299
300 template <HeapType heapType>
301 NEVER_INLINE void Heap::freeBlock(size_t block)
302 {
303     CollectorHeap& heap = heapType == PrimaryHeap ? primaryHeap : numberHeap;
304
305     freeBlock(heap.blocks[block]);
306
307     // swap with the last block so we compact as we go
308     heap.blocks[block] = heap.blocks[heap.usedBlocks - 1];
309     heap.usedBlocks--;
310
311     if (heap.numBlocks > MIN_ARRAY_SIZE && heap.usedBlocks < heap.numBlocks / LOW_WATER_FACTOR) {
312         heap.numBlocks = heap.numBlocks / GROWTH_FACTOR; 
313         heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock*)));
314     }
315 }
316
317 NEVER_INLINE void Heap::freeBlock(CollectorBlock* block)
318 {
319 #if PLATFORM(DARWIN)    
320     vm_deallocate(current_task(), reinterpret_cast<vm_address_t>(block), BLOCK_SIZE);
321 #elif PLATFORM(SYMBIAN)
322     userChunk->Free(reinterpret_cast<TAny*>(block));
323 #elif PLATFORM(WINCE)
324     VirtualFree(block, 0, MEM_RELEASE);
325 #elif PLATFORM(WIN_OS)
326 #if COMPILER(MINGW)
327     __mingw_aligned_free(block);
328 #else
329     _aligned_free(block);
330 #endif
331 #elif HAVE(POSIX_MEMALIGN)
332     free(block);
333 #else
334     munmap(reinterpret_cast<char*>(block), BLOCK_SIZE);
335 #endif
336 }
337
338 void Heap::freeBlocks(CollectorHeap* heap)
339 {
340     for (size_t i = 0; i < heap->usedBlocks; ++i)
341         if (heap->blocks[i])
342             freeBlock(heap->blocks[i]);
343     fastFree(heap->blocks);
344     memset(heap, 0, sizeof(CollectorHeap));
345 }
346
347 void Heap::recordExtraCost(size_t cost)
348 {
349     // Our frequency of garbage collection tries to balance memory use against speed
350     // by collecting based on the number of newly created values. However, for values
351     // that hold on to a great deal of memory that's not in the form of other JS values,
352     // that is not good enough - in some cases a lot of those objects can pile up and
353     // use crazy amounts of memory without a GC happening. So we track these extra
354     // memory costs. Only unusually large objects are noted, and we only keep track
355     // of this extra cost until the next GC. In garbage collected languages, most values
356     // are either very short lived temporaries, or have extremely long lifetimes. So
357     // if a large value survives one garbage collection, there is not much point to
358     // collecting more frequently as long as it stays alive.
359     // NOTE: we target the primaryHeap unconditionally as JSNumber doesn't modify cost 
360
361     primaryHeap.extraCost += cost;
362 }
363
364 template <HeapType heapType> ALWAYS_INLINE void* Heap::heapAllocate(size_t s)
365 {
366     typedef typename HeapConstants<heapType>::Block Block;
367     typedef typename HeapConstants<heapType>::Cell Cell;
368
369     CollectorHeap& heap = heapType == PrimaryHeap ? primaryHeap : numberHeap;
370     ASSERT(JSLock::lockCount() > 0);
371     ASSERT(JSLock::currentThreadIsHoldingLock());
372     ASSERT_UNUSED(s, s <= HeapConstants<heapType>::cellSize);
373
374     ASSERT(heap.operationInProgress == NoOperation);
375     ASSERT(heapType == PrimaryHeap || heap.extraCost == 0);
376     // FIXME: If another global variable access here doesn't hurt performance
377     // too much, we could CRASH() in NDEBUG builds, which could help ensure we
378     // don't spend any time debugging cases where we allocate inside an object's
379     // deallocation code.
380
381 #if COLLECT_ON_EVERY_ALLOCATION
382     collect();
383 #endif
384
385     size_t numLiveObjects = heap.numLiveObjects;
386     size_t usedBlocks = heap.usedBlocks;
387     size_t i = heap.firstBlockWithPossibleSpace;
388
389     // if we have a huge amount of extra cost, we'll try to collect even if we still have
390     // free cells left.
391     if (heapType == PrimaryHeap && heap.extraCost > ALLOCATIONS_PER_COLLECTION) {
392         size_t numLiveObjectsAtLastCollect = heap.numLiveObjectsAtLastCollect;
393         size_t numNewObjects = numLiveObjects - numLiveObjectsAtLastCollect;
394         const size_t newCost = numNewObjects + heap.extraCost;
395         if (newCost >= ALLOCATIONS_PER_COLLECTION && newCost >= numLiveObjectsAtLastCollect)
396             goto collect;
397     }
398
399     ASSERT(heap.operationInProgress == NoOperation);
400 #ifndef NDEBUG
401     // FIXME: Consider doing this in NDEBUG builds too (see comment above).
402     heap.operationInProgress = Allocation;
403 #endif
404
405 scan:
406     Block* targetBlock;
407     size_t targetBlockUsedCells;
408     if (i != usedBlocks) {
409         targetBlock = reinterpret_cast<Block*>(heap.blocks[i]);
410         targetBlockUsedCells = targetBlock->usedCells;
411         ASSERT(targetBlockUsedCells <= HeapConstants<heapType>::cellsPerBlock);
412         while (targetBlockUsedCells == HeapConstants<heapType>::cellsPerBlock) {
413             if (++i == usedBlocks)
414                 goto collect;
415             targetBlock = reinterpret_cast<Block*>(heap.blocks[i]);
416             targetBlockUsedCells = targetBlock->usedCells;
417             ASSERT(targetBlockUsedCells <= HeapConstants<heapType>::cellsPerBlock);
418         }
419         heap.firstBlockWithPossibleSpace = i;
420     } else {
421
422 collect:
423         size_t numLiveObjectsAtLastCollect = heap.numLiveObjectsAtLastCollect;
424         size_t numNewObjects = numLiveObjects - numLiveObjectsAtLastCollect;
425         const size_t newCost = numNewObjects + heap.extraCost;
426
427         if (newCost >= ALLOCATIONS_PER_COLLECTION && newCost >= numLiveObjectsAtLastCollect) {
428 #ifndef NDEBUG
429             heap.operationInProgress = NoOperation;
430 #endif
431             bool foundGarbage = collect();
432             numLiveObjects = heap.numLiveObjects;
433             usedBlocks = heap.usedBlocks;
434             i = heap.firstBlockWithPossibleSpace;
435 #ifndef NDEBUG
436             heap.operationInProgress = Allocation;
437 #endif
438             if (foundGarbage)
439                 goto scan;
440         }
441
442         // didn't find a block, and GC didn't reclaim anything, need to allocate a new block
443         targetBlock = reinterpret_cast<Block*>(allocateBlock<heapType>());
444         heap.firstBlockWithPossibleSpace = heap.usedBlocks - 1;
445         targetBlockUsedCells = 0;
446     }
447
448     // find a free spot in the block and detach it from the free list
449     Cell* newCell = targetBlock->freeList;
450
451     // "next" field is a cell offset -- 0 means next cell, so a zeroed block is already initialized
452     targetBlock->freeList = (newCell + 1) + newCell->u.freeCell.next;
453
454     targetBlock->usedCells = static_cast<uint32_t>(targetBlockUsedCells + 1);
455     heap.numLiveObjects = numLiveObjects + 1;
456
457 #ifndef NDEBUG
458     // FIXME: Consider doing this in NDEBUG builds too (see comment above).
459     heap.operationInProgress = NoOperation;
460 #endif
461
462     return newCell;
463 }
464
465 void* Heap::allocate(size_t s)
466 {
467     return heapAllocate<PrimaryHeap>(s);
468 }
469
470 void* Heap::allocateNumber(size_t s)
471 {
472     return heapAllocate<NumberHeap>(s);
473 }
474
475 #if PLATFORM(WINCE)
476 void* g_stackBase = 0;
477
478 inline bool isPageWritable(void* page)
479 {
480     MEMORY_BASIC_INFORMATION memoryInformation;
481     DWORD result = VirtualQuery(page, &memoryInformation, sizeof(memoryInformation));
482
483     // return false on error, including ptr outside memory
484     if (result != sizeof(memoryInformation))
485         return false;
486
487     DWORD protect = memoryInformation.Protect & ~(PAGE_GUARD | PAGE_NOCACHE);
488     return protect == PAGE_READWRITE
489         || protect == PAGE_WRITECOPY
490         || protect == PAGE_EXECUTE_READWRITE
491         || protect == PAGE_EXECUTE_WRITECOPY;
492 }
493
494 static void* getStackBase(void* previousFrame)
495 {
496     // find the address of this stack frame by taking the address of a local variable
497     bool isGrowingDownward;
498     void* thisFrame = (void*)(&isGrowingDownward);
499
500     isGrowingDownward = previousFrame < &thisFrame;
501     static DWORD pageSize = 0;
502     if (!pageSize) {
503         SYSTEM_INFO systemInfo;
504         GetSystemInfo(&systemInfo);
505         pageSize = systemInfo.dwPageSize;
506     }
507
508     // scan all of memory starting from this frame, and return the last writeable page found
509     register char* currentPage = (char*)((DWORD)thisFrame & ~(pageSize - 1));
510     if (isGrowingDownward) {
511         while (currentPage > 0) {
512             // check for underflow
513             if (currentPage >= (char*)pageSize)
514                 currentPage -= pageSize;
515             else
516                 currentPage = 0;
517             if (!isPageWritable(currentPage))
518                 return currentPage + pageSize;
519         }
520         return 0;
521     } else {
522         while (true) {
523             // guaranteed to complete because isPageWritable returns false at end of memory
524             currentPage += pageSize;
525             if (!isPageWritable(currentPage))
526                 return currentPage;
527         }
528     }
529 }
530 #endif
531
532 #if PLATFORM(QNX)
533 static inline void *currentThreadStackBaseQNX()
534 {
535     static void* stackBase = 0;
536     static size_t stackSize = 0;
537     static pthread_t stackThread;
538     pthread_t thread = pthread_self();
539     if (stackBase == 0 || thread != stackThread) {
540         struct _debug_thread_info threadInfo;
541         memset(&threadInfo, 0, sizeof(threadInfo));
542         threadInfo.tid = pthread_self();
543         int fd = open("/proc/self", O_RDONLY);
544         if (fd == -1) {
545             LOG_ERROR("Unable to open /proc/self (errno: %d)", errno);
546             return 0;
547         }
548         devctl(fd, DCMD_PROC_TIDSTATUS, &threadInfo, sizeof(threadInfo), 0);
549         close(fd);
550         stackBase = reinterpret_cast<void*>(threadInfo.stkbase);
551         stackSize = threadInfo.stksize;
552         ASSERT(stackBase);
553         stackThread = thread;
554     }
555     return static_cast<char*>(stackBase) + stackSize;
556 }
557 #endif
558
559 static inline void* currentThreadStackBase()
560 {
561 #if PLATFORM(DARWIN)
562     pthread_t thread = pthread_self();
563     return pthread_get_stackaddr_np(thread);
564 #elif PLATFORM(WIN_OS) && PLATFORM(X86) && COMPILER(MSVC)
565     // offset 0x18 from the FS segment register gives a pointer to
566     // the thread information block for the current thread
567     NT_TIB* pTib;
568     __asm {
569         MOV EAX, FS:[18h]
570         MOV pTib, EAX
571     }
572     return static_cast<void*>(pTib->StackBase);
573 #elif PLATFORM(WIN_OS) && PLATFORM(X86_64) && COMPILER(MSVC)
574     PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
575     return reinterpret_cast<void*>(pTib->StackBase);
576 #elif PLATFORM(WIN_OS) && PLATFORM(X86) && COMPILER(GCC)
577     // offset 0x18 from the FS segment register gives a pointer to
578     // the thread information block for the current thread
579     NT_TIB* pTib;
580     asm ( "movl %%fs:0x18, %0\n"
581           : "=r" (pTib)
582         );
583     return static_cast<void*>(pTib->StackBase);
584 #elif PLATFORM(QNX)
585     return currentThreadStackBaseQNX();
586 #elif PLATFORM(SOLARIS)
587     stack_t s;
588     thr_stksegment(&s);
589     return s.ss_sp;
590 #elif PLATFORM(OPENBSD)
591     pthread_t thread = pthread_self();
592     stack_t stack;
593     pthread_stackseg_np(thread, &stack);
594     return stack.ss_sp;
595 #elif PLATFORM(SYMBIAN)
596     static void* stackBase = 0;
597     if (stackBase == 0) {
598         TThreadStackInfo info;
599         RThread thread;
600         thread.StackInfo(info);
601         stackBase = (void*)info.iBase;
602     }
603     return (void*)stackBase;
604 #elif PLATFORM(HAIKU)
605     thread_info threadInfo;
606     get_thread_info(find_thread(NULL), &threadInfo);
607     return threadInfo.stack_end;
608 #elif PLATFORM(UNIX)
609     static void* stackBase = 0;
610     static size_t stackSize = 0;
611     static pthread_t stackThread;
612     pthread_t thread = pthread_self();
613     if (stackBase == 0 || thread != stackThread) {
614         pthread_attr_t sattr;
615         pthread_attr_init(&sattr);
616 #if HAVE(PTHREAD_NP_H) || PLATFORM(NETBSD)
617         // e.g. on FreeBSD 5.4, neundorf@kde.org
618         pthread_attr_get_np(thread, &sattr);
619 #else
620         // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
621         pthread_getattr_np(thread, &sattr);
622 #endif
623         int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
624         (void)rc; // FIXME: Deal with error code somehow? Seems fatal.
625         ASSERT(stackBase);
626         pthread_attr_destroy(&sattr);
627         stackThread = thread;
628     }
629     return static_cast<char*>(stackBase) + stackSize;
630 #elif PLATFORM(WINCE)
631     if (g_stackBase)
632         return g_stackBase;
633     else {
634         int dummy;
635         return getStackBase(&dummy);
636     }
637 #else
638 #error Need a way to get the stack base on this platform
639 #endif
640 }
641
642 #if ENABLE(JSC_MULTIPLE_THREADS)
643
644 static inline PlatformThread getCurrentPlatformThread()
645 {
646 #if PLATFORM(DARWIN)
647     return pthread_mach_thread_np(pthread_self());
648 #elif PLATFORM(WIN_OS)
649     HANDLE threadHandle = pthread_getw32threadhandle_np(pthread_self());
650     return PlatformThread(GetCurrentThreadId(), threadHandle);
651 #endif
652 }
653
654 void Heap::makeUsableFromMultipleThreads()
655 {
656     if (m_currentThreadRegistrar)
657         return;
658
659     int error = pthread_key_create(&m_currentThreadRegistrar, unregisterThread);
660     if (error)
661         CRASH();
662 }
663
664 void Heap::registerThread()
665 {
666     ASSERT(!m_globalData->mainThreadOnly || isMainThread());
667
668     if (!m_currentThreadRegistrar || pthread_getspecific(m_currentThreadRegistrar))
669         return;
670
671     pthread_setspecific(m_currentThreadRegistrar, this);
672     Heap::Thread* thread = new Heap::Thread(pthread_self(), getCurrentPlatformThread(), currentThreadStackBase());
673
674     MutexLocker lock(m_registeredThreadsMutex);
675
676     thread->next = m_registeredThreads;
677     m_registeredThreads = thread;
678 }
679
680 void Heap::unregisterThread(void* p)
681 {
682     if (p)
683         static_cast<Heap*>(p)->unregisterThread();
684 }
685
686 void Heap::unregisterThread()
687 {
688     pthread_t currentPosixThread = pthread_self();
689
690     MutexLocker lock(m_registeredThreadsMutex);
691
692     if (pthread_equal(currentPosixThread, m_registeredThreads->posixThread)) {
693         Thread* t = m_registeredThreads;
694         m_registeredThreads = m_registeredThreads->next;
695         delete t;
696     } else {
697         Heap::Thread* last = m_registeredThreads;
698         Heap::Thread* t;
699         for (t = m_registeredThreads->next; t; t = t->next) {
700             if (pthread_equal(t->posixThread, currentPosixThread)) {
701                 last->next = t->next;
702                 break;
703             }
704             last = t;
705         }
706         ASSERT(t); // If t is NULL, we never found ourselves in the list.
707         delete t;
708     }
709 }
710
711 #else // ENABLE(JSC_MULTIPLE_THREADS)
712
713 void Heap::registerThread()
714 {
715 }
716
717 #endif
718
719 #define IS_POINTER_ALIGNED(p) (((intptr_t)(p) & (sizeof(char*) - 1)) == 0)
720
721 // cell size needs to be a power of two for this to be valid
722 #define IS_HALF_CELL_ALIGNED(p) (((intptr_t)(p) & (CELL_MASK >> 1)) == 0)
723
724 void Heap::markConservatively(MarkStack& markStack, void* start, void* end)
725 {
726     if (start > end) {
727         void* tmp = start;
728         start = end;
729         end = tmp;
730     }
731
732     ASSERT((static_cast<char*>(end) - static_cast<char*>(start)) < 0x1000000);
733     ASSERT(IS_POINTER_ALIGNED(start));
734     ASSERT(IS_POINTER_ALIGNED(end));
735
736     char** p = static_cast<char**>(start);
737     char** e = static_cast<char**>(end);
738
739     size_t usedPrimaryBlocks = primaryHeap.usedBlocks;
740     size_t usedNumberBlocks = numberHeap.usedBlocks;
741     CollectorBlock** primaryBlocks = primaryHeap.blocks;
742     CollectorBlock** numberBlocks = numberHeap.blocks;
743
744     const size_t lastCellOffset = sizeof(CollectorCell) * (CELLS_PER_BLOCK - 1);
745
746     while (p != e) {
747         char* x = *p++;
748         if (IS_HALF_CELL_ALIGNED(x) && x) {
749             uintptr_t xAsBits = reinterpret_cast<uintptr_t>(x);
750             xAsBits &= CELL_ALIGN_MASK;
751             uintptr_t offset = xAsBits & BLOCK_OFFSET_MASK;
752             CollectorBlock* blockAddr = reinterpret_cast<CollectorBlock*>(xAsBits - offset);
753             // Mark the the number heap, we can mark these Cells directly to avoid the virtual call cost
754             for (size_t block = 0; block < usedNumberBlocks; block++) {
755                 if ((numberBlocks[block] == blockAddr) & (offset <= lastCellOffset)) {
756                     Heap::markCell(reinterpret_cast<JSCell*>(xAsBits));
757                     goto endMarkLoop;
758                 }
759             }
760           
761             // Mark the primary heap
762             for (size_t block = 0; block < usedPrimaryBlocks; block++) {
763                 if ((primaryBlocks[block] == blockAddr) & (offset <= lastCellOffset)) {
764                     if (reinterpret_cast<CollectorCell*>(xAsBits)->u.freeCell.zeroIfFree) {
765                         markStack.append(reinterpret_cast<JSCell*>(xAsBits));
766                         markStack.drain();
767                     }
768                     break;
769                 }
770             }
771         endMarkLoop:
772             ;
773         }
774     }
775 }
776
777 void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal(MarkStack& markStack)
778 {
779     void* dummy;
780     void* stackPointer = &dummy;
781     void* stackBase = currentThreadStackBase();
782     markConservatively(markStack, stackPointer, stackBase);
783 }
784
785 #if COMPILER(GCC)
786 #define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*))))
787 #else
788 #define REGISTER_BUFFER_ALIGNMENT
789 #endif
790
791 void Heap::markCurrentThreadConservatively(MarkStack& markStack)
792 {
793     // setjmp forces volatile registers onto the stack
794     jmp_buf registers REGISTER_BUFFER_ALIGNMENT;
795 #if COMPILER(MSVC)
796 #pragma warning(push)
797 #pragma warning(disable: 4611)
798 #endif
799     setjmp(registers);
800 #if COMPILER(MSVC)
801 #pragma warning(pop)
802 #endif
803
804     markCurrentThreadConservativelyInternal(markStack);
805 }
806
807 #if ENABLE(JSC_MULTIPLE_THREADS)
808
809 static inline void suspendThread(const PlatformThread& platformThread)
810 {
811 #if PLATFORM(DARWIN)
812     thread_suspend(platformThread);
813 #elif PLATFORM(WIN_OS)
814     SuspendThread(platformThread.handle);
815 #else
816 #error Need a way to suspend threads on this platform
817 #endif
818 }
819
820 static inline void resumeThread(const PlatformThread& platformThread)
821 {
822 #if PLATFORM(DARWIN)
823     thread_resume(platformThread);
824 #elif PLATFORM(WIN_OS)
825     ResumeThread(platformThread.handle);
826 #else
827 #error Need a way to resume threads on this platform
828 #endif
829 }
830
831 typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
832
833 #if PLATFORM(DARWIN)
834
835 #if PLATFORM(X86)
836 typedef i386_thread_state_t PlatformThreadRegisters;
837 #elif PLATFORM(X86_64)
838 typedef x86_thread_state64_t PlatformThreadRegisters;
839 #elif PLATFORM(PPC)
840 typedef ppc_thread_state_t PlatformThreadRegisters;
841 #elif PLATFORM(PPC64)
842 typedef ppc_thread_state64_t PlatformThreadRegisters;
843 #elif PLATFORM(ARM)
844 typedef arm_thread_state_t PlatformThreadRegisters;
845 #else
846 #error Unknown Architecture
847 #endif
848
849 #elif PLATFORM(WIN_OS)&& PLATFORM(X86)
850 typedef CONTEXT PlatformThreadRegisters;
851 #else
852 #error Need a thread register struct for this platform
853 #endif
854
855 static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs)
856 {
857 #if PLATFORM(DARWIN)
858
859 #if PLATFORM(X86)
860     unsigned user_count = sizeof(regs)/sizeof(int);
861     thread_state_flavor_t flavor = i386_THREAD_STATE;
862 #elif PLATFORM(X86_64)
863     unsigned user_count = x86_THREAD_STATE64_COUNT;
864     thread_state_flavor_t flavor = x86_THREAD_STATE64;
865 #elif PLATFORM(PPC) 
866     unsigned user_count = PPC_THREAD_STATE_COUNT;
867     thread_state_flavor_t flavor = PPC_THREAD_STATE;
868 #elif PLATFORM(PPC64)
869     unsigned user_count = PPC_THREAD_STATE64_COUNT;
870     thread_state_flavor_t flavor = PPC_THREAD_STATE64;
871 #elif PLATFORM(ARM)
872     unsigned user_count = ARM_THREAD_STATE_COUNT;
873     thread_state_flavor_t flavor = ARM_THREAD_STATE;
874 #else
875 #error Unknown Architecture
876 #endif
877
878     kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)&regs, &user_count);
879     if (result != KERN_SUCCESS) {
880         WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 
881                             "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);
882         CRASH();
883     }
884     return user_count * sizeof(usword_t);
885 // end PLATFORM(DARWIN)
886
887 #elif PLATFORM(WIN_OS) && PLATFORM(X86)
888     regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS;
889     GetThreadContext(platformThread.handle, &regs);
890     return sizeof(CONTEXT);
891 #else
892 #error Need a way to get thread registers on this platform
893 #endif
894 }
895
896 static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
897 {
898 #if PLATFORM(DARWIN)
899
900 #if __DARWIN_UNIX03
901
902 #if PLATFORM(X86)
903     return reinterpret_cast<void*>(regs.__esp);
904 #elif PLATFORM(X86_64)
905     return reinterpret_cast<void*>(regs.__rsp);
906 #elif PLATFORM(PPC) || PLATFORM(PPC64)
907     return reinterpret_cast<void*>(regs.__r1);
908 #elif PLATFORM(ARM)
909     return reinterpret_cast<void*>(regs.__sp);
910 #else
911 #error Unknown Architecture
912 #endif
913
914 #else // !__DARWIN_UNIX03
915
916 #if PLATFORM(X86)
917     return reinterpret_cast<void*>(regs.esp);
918 #elif PLATFORM(X86_64)
919     return reinterpret_cast<void*>(regs.rsp);
920 #elif (PLATFORM(PPC) || PLATFORM(PPC64))
921     return reinterpret_cast<void*>(regs.r1);
922 #else
923 #error Unknown Architecture
924 #endif
925
926 #endif // __DARWIN_UNIX03
927
928 // end PLATFORM(DARWIN)
929 #elif PLATFORM(X86) && PLATFORM(WIN_OS)
930     return reinterpret_cast<void*>((uintptr_t) regs.Esp);
931 #else
932 #error Need a way to get the stack pointer for another thread on this platform
933 #endif
934 }
935
936 void Heap::markOtherThreadConservatively(MarkStack& markStack, Thread* thread)
937 {
938     suspendThread(thread->platformThread);
939
940     PlatformThreadRegisters regs;
941     size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs);
942
943     // mark the thread's registers
944     markConservatively(markStack, static_cast<void*>(&regs), static_cast<void*>(reinterpret_cast<char*>(&regs) + regSize));
945
946     void* stackPointer = otherThreadStackPointer(regs);
947     markConservatively(markStack, stackPointer, thread->stackBase);
948
949     resumeThread(thread->platformThread);
950 }
951
952 #endif
953
954 void Heap::markStackObjectsConservatively(MarkStack& markStack)
955 {
956     markCurrentThreadConservatively(markStack);
957
958 #if ENABLE(JSC_MULTIPLE_THREADS)
959
960     if (m_currentThreadRegistrar) {
961
962         MutexLocker lock(m_registeredThreadsMutex);
963
964 #ifndef NDEBUG
965         // Forbid malloc during the mark phase. Marking a thread suspends it, so 
966         // a malloc inside markChildren() would risk a deadlock with a thread that had been 
967         // suspended while holding the malloc lock.
968         fastMallocForbid();
969 #endif
970         // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
971         // and since this is a shared heap, they are real locks.
972         for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
973             if (!pthread_equal(thread->posixThread, pthread_self()))
974                 markOtherThreadConservatively(markStack, thread);
975         }
976 #ifndef NDEBUG
977         fastMallocAllow();
978 #endif
979     }
980 #endif
981 }
982
983 void Heap::protect(JSValue k)
984 {
985     ASSERT(k);
986     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance);
987
988     if (!k.isCell())
989         return;
990
991     m_protectedValues.add(k.asCell());
992 }
993
994 void Heap::unprotect(JSValue k)
995 {
996     ASSERT(k);
997     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance);
998
999     if (!k.isCell())
1000         return;
1001
1002     m_protectedValues.remove(k.asCell());
1003 }
1004
1005 void Heap::markProtectedObjects(MarkStack& markStack)
1006 {
1007     ProtectCountSet::iterator end = m_protectedValues.end();
1008     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) {
1009         markStack.append(it->first);
1010         markStack.drain();
1011     }
1012 }
1013
1014 template <HeapType heapType> size_t Heap::sweep()
1015 {
1016     typedef typename HeapConstants<heapType>::Block Block;
1017     typedef typename HeapConstants<heapType>::Cell Cell;
1018
1019     // SWEEP: delete everything with a zero refcount (garbage) and unmark everything else
1020     CollectorHeap& heap = heapType == PrimaryHeap ? primaryHeap : numberHeap;
1021     
1022     size_t emptyBlocks = 0;
1023     size_t numLiveObjects = heap.numLiveObjects;
1024     
1025     for (size_t block = 0; block < heap.usedBlocks; block++) {
1026         Block* curBlock = reinterpret_cast<Block*>(heap.blocks[block]);
1027         
1028         size_t usedCells = curBlock->usedCells;
1029         Cell* freeList = curBlock->freeList;
1030         
1031         if (usedCells == HeapConstants<heapType>::cellsPerBlock) {
1032             // special case with a block where all cells are used -- testing indicates this happens often
1033             for (size_t i = 0; i < HeapConstants<heapType>::cellsPerBlock; i++) {
1034                 if (!curBlock->marked.get(i >> HeapConstants<heapType>::bitmapShift)) {
1035                     Cell* cell = curBlock->cells + i;
1036                     
1037                     if (heapType != NumberHeap) {
1038                         JSCell* imp = reinterpret_cast<JSCell*>(cell);
1039                         // special case for allocated but uninitialized object
1040                         // (We don't need this check earlier because nothing prior this point 
1041                         // assumes the object has a valid vptr.)
1042                         if (cell->u.freeCell.zeroIfFree == 0)
1043                             continue;
1044                         
1045                         imp->~JSCell();
1046                     }
1047                     
1048                     --usedCells;
1049                     --numLiveObjects;
1050                     
1051                     // put cell on the free list
1052                     cell->u.freeCell.zeroIfFree = 0;
1053                     cell->u.freeCell.next = freeList - (cell + 1);
1054                     freeList = cell;
1055                 }
1056             }
1057         } else {
1058             size_t minimumCellsToProcess = usedCells;
1059             for (size_t i = 0; (i < minimumCellsToProcess) & (i < HeapConstants<heapType>::cellsPerBlock); i++) {
1060                 Cell* cell = curBlock->cells + i;
1061                 if (cell->u.freeCell.zeroIfFree == 0) {
1062                     ++minimumCellsToProcess;
1063                 } else {
1064                     if (!curBlock->marked.get(i >> HeapConstants<heapType>::bitmapShift)) {
1065                         if (heapType != NumberHeap) {
1066                             JSCell* imp = reinterpret_cast<JSCell*>(cell);
1067                             imp->~JSCell();
1068                         }
1069                         --usedCells;
1070                         --numLiveObjects;
1071                         
1072                         // put cell on the free list
1073                         cell->u.freeCell.zeroIfFree = 0;
1074                         cell->u.freeCell.next = freeList - (cell + 1); 
1075                         freeList = cell;
1076                     }
1077                 }
1078             }
1079         }
1080         
1081         curBlock->usedCells = static_cast<uint32_t>(usedCells);
1082         curBlock->freeList = freeList;
1083         curBlock->marked.clearAll();
1084         
1085         if (!usedCells)
1086             ++emptyBlocks;
1087     }
1088     
1089     if (heap.numLiveObjects != numLiveObjects)
1090         heap.firstBlockWithPossibleSpace = 0;
1091     
1092     heap.numLiveObjects = numLiveObjects;
1093     heap.numLiveObjectsAtLastCollect = numLiveObjects;
1094     heap.extraCost = 0;
1095     
1096     if (!emptyBlocks)
1097         return numLiveObjects;
1098
1099     size_t neededCells = 1.25f * (numLiveObjects + max(ALLOCATIONS_PER_COLLECTION, numLiveObjects));
1100     size_t neededBlocks = (neededCells + HeapConstants<heapType>::cellsPerBlock - 1) / HeapConstants<heapType>::cellsPerBlock;
1101     for (size_t block = 0; block < heap.usedBlocks; block++) {
1102         if (heap.usedBlocks <= neededBlocks)
1103             break;
1104
1105         Block* curBlock = reinterpret_cast<Block*>(heap.blocks[block]);
1106         if (curBlock->usedCells)
1107             continue;
1108
1109         freeBlock<heapType>(block);
1110         block--; // Don't move forward a step in this case
1111     }
1112
1113     return numLiveObjects;
1114 }
1115
1116 bool Heap::collect()
1117 {
1118 #ifndef NDEBUG
1119     if (m_globalData->isSharedInstance) {
1120         ASSERT(JSLock::lockCount() > 0);
1121         ASSERT(JSLock::currentThreadIsHoldingLock());
1122     }
1123 #endif
1124
1125     ASSERT((primaryHeap.operationInProgress == NoOperation) | (numberHeap.operationInProgress == NoOperation));
1126     if ((primaryHeap.operationInProgress != NoOperation) | (numberHeap.operationInProgress != NoOperation))
1127         CRASH();
1128
1129     JAVASCRIPTCORE_GC_BEGIN();
1130     primaryHeap.operationInProgress = Collection;
1131     numberHeap.operationInProgress = Collection;
1132
1133     // MARK: first mark all referenced objects recursively starting out from the set of root objects
1134     MarkStack& markStack = m_globalData->markStack;
1135     markStackObjectsConservatively(markStack);
1136     markProtectedObjects(markStack);
1137     if (m_markListSet && m_markListSet->size())
1138         MarkedArgumentBuffer::markLists(markStack, *m_markListSet);
1139     if (m_globalData->exception)
1140         markStack.append(m_globalData->exception);
1141     m_globalData->interpreter->registerFile().markCallFrames(markStack, this);
1142     m_globalData->smallStrings.markChildren(markStack);
1143     if (m_globalData->functionCodeBlockBeingReparsed)
1144         m_globalData->functionCodeBlockBeingReparsed->markAggregate(markStack);
1145     if (m_globalData->firstStringifierToMark)
1146         JSONObject::markStringifiers(markStack, m_globalData->firstStringifierToMark);
1147
1148     markStack.drain();
1149     markStack.compact();
1150     JAVASCRIPTCORE_GC_MARKED();
1151
1152     size_t originalLiveObjects = primaryHeap.numLiveObjects + numberHeap.numLiveObjects;
1153     size_t numLiveObjects = sweep<PrimaryHeap>();
1154     numLiveObjects += sweep<NumberHeap>();
1155
1156     primaryHeap.operationInProgress = NoOperation;
1157     numberHeap.operationInProgress = NoOperation;
1158     JAVASCRIPTCORE_GC_END(originalLiveObjects, numLiveObjects);
1159
1160     return numLiveObjects < originalLiveObjects;
1161 }
1162
1163 size_t Heap::objectCount() 
1164 {
1165     return primaryHeap.numLiveObjects + numberHeap.numLiveObjects - m_globalData->smallStrings.count(); 
1166 }
1167
1168 template <HeapType heapType> 
1169 static void addToStatistics(Heap::Statistics& statistics, const CollectorHeap& heap)
1170 {
1171     typedef HeapConstants<heapType> HC;
1172     for (size_t i = 0; i < heap.usedBlocks; ++i) {
1173         if (heap.blocks[i]) {
1174             statistics.size += BLOCK_SIZE;
1175             statistics.free += (HC::cellsPerBlock - heap.blocks[i]->usedCells) * HC::cellSize;
1176         }
1177     }
1178 }
1179
1180 Heap::Statistics Heap::statistics() const
1181 {
1182     Statistics statistics = { 0, 0 };
1183     JSC::addToStatistics<PrimaryHeap>(statistics, primaryHeap);
1184     JSC::addToStatistics<NumberHeap>(statistics, numberHeap);
1185     return statistics;
1186 }
1187
1188 size_t Heap::globalObjectCount()
1189 {
1190     size_t count = 0;
1191     if (JSGlobalObject* head = m_globalData->head) {
1192         JSGlobalObject* o = head;
1193         do {
1194             ++count;
1195             o = o->next();
1196         } while (o != head);
1197     }
1198     return count;
1199 }
1200
1201 size_t Heap::protectedGlobalObjectCount()
1202 {
1203     size_t count = 0;
1204     if (JSGlobalObject* head = m_globalData->head) {
1205         JSGlobalObject* o = head;
1206         do {
1207             if (m_protectedValues.contains(o))
1208                 ++count;
1209             o = o->next();
1210         } while (o != head);
1211     }
1212
1213     return count;
1214 }
1215
1216 size_t Heap::protectedObjectCount()
1217 {
1218     return m_protectedValues.size();
1219 }
1220
1221 static const char* typeName(JSCell* cell)
1222 {
1223     if (cell->isString())
1224         return "string";
1225 #if USE(JSVALUE32)
1226     if (cell->isNumber())
1227         return "number";
1228 #endif
1229     if (cell->isGetterSetter())
1230         return "gettersetter";
1231     if (cell->isAPIValueWrapper())
1232         return "value wrapper";
1233     if (cell->isPropertyNameIterator())
1234         return "for-in iterator";
1235     ASSERT(cell->isObject());
1236     const ClassInfo* info = cell->classInfo();
1237     return info ? info->className : "Object";
1238 }
1239
1240 HashCountedSet<const char*>* Heap::protectedObjectTypeCounts()
1241 {
1242     HashCountedSet<const char*>* counts = new HashCountedSet<const char*>;
1243
1244     ProtectCountSet::iterator end = m_protectedValues.end();
1245     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
1246         counts->add(typeName(it->first));
1247
1248     return counts;
1249 }
1250
1251 bool Heap::isBusy()
1252 {
1253     return (primaryHeap.operationInProgress != NoOperation) | (numberHeap.operationInProgress != NoOperation);
1254 }
1255
1256 Heap::iterator Heap::primaryHeapBegin()
1257 {
1258     return iterator(primaryHeap.blocks, primaryHeap.blocks + primaryHeap.usedBlocks);
1259 }
1260
1261 Heap::iterator Heap::primaryHeapEnd()
1262 {
1263     return iterator(primaryHeap.blocks + primaryHeap.usedBlocks, primaryHeap.blocks + primaryHeap.usedBlocks);
1264 }
1265
1266 } // namespace JSC