26e4ef47fbd5e09ae9a54ac913e698f566d4c0a7
[WebKit-https.git] / Source / JavaScriptCore / heap / Heap.cpp
1 /*
2  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011 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 "Heap.h"
23
24 #include "CodeBlock.h"
25 #include "ConservativeRoots.h"
26 #include "GCActivityCallback.h"
27 #include "HeapRootVisitor.h"
28 #include "Interpreter.h"
29 #include "JSGlobalData.h"
30 #include "JSGlobalObject.h"
31 #include "JSLock.h"
32 #include "JSONObject.h"
33 #include "Tracing.h"
34 #include <algorithm>
35
36 #define COLLECT_ON_EVERY_ALLOCATION 0
37
38 using namespace std;
39 using namespace JSC;
40
41 namespace JSC {
42
43 namespace { 
44
45 static const size_t largeHeapSize = 16 * 1024 * 1024;
46 static const size_t smallHeapSize = 512 * 1024;
47
48 static size_t heapSizeForHint(HeapSize heapSize)
49 {
50 #if ENABLE(LARGE_HEAP)
51     if (heapSize == LargeHeap)
52         return largeHeapSize;
53     ASSERT(heapSize == SmallHeap);
54     return smallHeapSize;
55 #else
56     ASSERT_UNUSED(heapSize, heapSize == LargeHeap || heapSize == SmallHeap);
57     return smallHeapSize;
58 #endif
59 }
60
61 static inline bool isValidSharedInstanceThreadState()
62 {
63     if (!JSLock::lockCount())
64         return false;
65
66     if (!JSLock::currentThreadIsHoldingLock())
67         return false;
68
69     return true;
70 }
71
72 static inline bool isValidThreadState(JSGlobalData* globalData)
73 {
74     if (globalData->identifierTable != wtfThreadData().currentIdentifierTable())
75         return false;
76
77     if (globalData->isSharedInstance() && !isValidSharedInstanceThreadState())
78         return false;
79
80     return true;
81 }
82
83 class CountFunctor {
84 public:
85     typedef size_t ReturnType;
86
87     CountFunctor();
88     void count(size_t);
89     ReturnType returnValue();
90
91 private:
92     ReturnType m_count;
93 };
94
95 inline CountFunctor::CountFunctor()
96     : m_count(0)
97 {
98 }
99
100 inline void CountFunctor::count(size_t count)
101 {
102     m_count += count;
103 }
104
105 inline CountFunctor::ReturnType CountFunctor::returnValue()
106 {
107     return m_count;
108 }
109
110 struct ClearMarks : MarkedBlock::VoidFunctor {
111     void operator()(MarkedBlock*);
112 };
113
114 inline void ClearMarks::operator()(MarkedBlock* block)
115 {
116     block->clearMarks();
117     block->notifyMayHaveFreshFreeCells();
118 }
119
120 struct Sweep : MarkedBlock::VoidFunctor {
121     void operator()(MarkedBlock*);
122 };
123
124 inline void Sweep::operator()(MarkedBlock* block)
125 {
126     block->sweep();
127 }
128
129 struct MarkCount : CountFunctor {
130     void operator()(MarkedBlock*);
131 };
132
133 inline void MarkCount::operator()(MarkedBlock* block)
134 {
135     count(block->markCount());
136 }
137
138 struct Size : CountFunctor {
139     void operator()(MarkedBlock*);
140 };
141
142 inline void Size::operator()(MarkedBlock* block)
143 {
144     count(block->markCount() * block->cellSize());
145 }
146
147 struct Capacity : CountFunctor {
148     void operator()(MarkedBlock*);
149 };
150
151 inline void Capacity::operator()(MarkedBlock* block)
152 {
153     count(block->capacity());
154 }
155
156 struct Count : public CountFunctor {
157     void operator()(JSCell*);
158 };
159
160 inline void Count::operator()(JSCell*)
161 {
162     count(1);
163 }
164
165 struct CountIfGlobalObject : CountFunctor {
166     void operator()(JSCell*);
167 };
168
169 inline void CountIfGlobalObject::operator()(JSCell* cell)
170 {
171     if (!cell->isObject())
172         return;
173     if (!asObject(cell)->isGlobalObject())
174         return;
175     count(1);
176 }
177
178 class TakeIfEmpty {
179 public:
180     typedef MarkedBlock* ReturnType;
181
182     TakeIfEmpty(MarkedSpace*);
183     void operator()(MarkedBlock*);
184     ReturnType returnValue();
185
186 private:
187     MarkedSpace* m_markedSpace;
188     DoublyLinkedList<MarkedBlock> m_empties;
189 };
190
191 inline TakeIfEmpty::TakeIfEmpty(MarkedSpace* newSpace)
192     : m_markedSpace(newSpace)
193 {
194 }
195
196 inline void TakeIfEmpty::operator()(MarkedBlock* block)
197 {
198     if (!block->isEmpty())
199         return;
200
201     m_markedSpace->removeBlock(block);
202     m_empties.append(block);
203 }
204
205 inline TakeIfEmpty::ReturnType TakeIfEmpty::returnValue()
206 {
207     return m_empties.head();
208 }
209
210 class RecordType {
211 public:
212     typedef PassOwnPtr<TypeCountSet> ReturnType;
213
214     RecordType();
215     void operator()(JSCell*);
216     ReturnType returnValue();
217
218 private:
219     const char* typeName(JSCell*);
220     OwnPtr<TypeCountSet> m_typeCountSet;
221 };
222
223 inline RecordType::RecordType()
224     : m_typeCountSet(adoptPtr(new TypeCountSet))
225 {
226 }
227
228 inline const char* RecordType::typeName(JSCell* cell)
229 {
230     const ClassInfo* info = cell->classInfo();
231     if (!info || !info->className)
232         return "[unknown]";
233     return info->className;
234 }
235
236 inline void RecordType::operator()(JSCell* cell)
237 {
238     m_typeCountSet->add(typeName(cell));
239 }
240
241 inline PassOwnPtr<TypeCountSet> RecordType::returnValue()
242 {
243     return m_typeCountSet.release();
244 }
245
246 } // anonymous namespace
247
248 Heap::Heap(JSGlobalData* globalData, HeapSize heapSize)
249     : m_heapSize(heapSize)
250     , m_minBytesPerCycle(heapSizeForHint(heapSize))
251     , m_operationInProgress(NoOperation)
252     , m_markedSpace(this)
253     , m_extraCost(0)
254     , m_markListSet(0)
255     , m_activityCallback(DefaultGCActivityCallback::create(this))
256     , m_machineThreads(this)
257     , m_slotVisitor(globalData->jsArrayVPtr)
258     , m_handleHeap(globalData)
259     , m_isSafeToCollect(false)
260     , m_globalData(globalData)
261 {
262     m_markedSpace.setHighWaterMark(m_minBytesPerCycle);
263     (*m_activityCallback)();
264     m_numberOfFreeBlocks = 0;
265     m_blockFreeingThread = createThread(blockFreeingThreadStartFunc, this, "JavaScriptCore::BlockFree");
266     ASSERT(m_blockFreeingThread);
267 }
268
269 Heap::~Heap()
270 {
271     // destroy our thread
272     {
273         MutexLocker locker(m_freeBlockLock);
274         m_blockFreeingThreadShouldQuit = true;
275         m_freeBlockCondition.broadcast();
276     }
277     waitForThreadCompletion(m_blockFreeingThread, 0);
278     
279     // The destroy function must already have been called, so assert this.
280     ASSERT(!m_globalData);
281 }
282
283 void Heap::destroy()
284 {
285     JSLock lock(SilenceAssertionsOnly);
286
287     if (!m_globalData)
288         return;
289
290     ASSERT(!m_globalData->dynamicGlobalObject);
291     ASSERT(m_operationInProgress == NoOperation);
292     
293     // The global object is not GC protected at this point, so sweeping may delete it
294     // (and thus the global data) before other objects that may use the global data.
295     RefPtr<JSGlobalData> protect(m_globalData);
296
297 #if ENABLE(JIT)
298     m_globalData->jitStubs->clearHostFunctionStubs();
299 #endif
300
301     delete m_markListSet;
302     m_markListSet = 0;
303
304     clearMarks();
305     m_handleHeap.finalizeWeakHandles();
306     m_globalData->smallStrings.finalizeSmallStrings();
307
308     shrink();
309     ASSERT(!size());
310     
311 #if ENABLE(SIMPLE_HEAP_PROFILING)
312     m_slotVisitor.m_visitedTypeCounts.dump(stderr, "Visited Type Counts");
313     m_destroyedTypeCounts.dump(stderr, "Destroyed Type Counts");
314 #endif
315     
316     releaseFreeBlocks();
317
318     m_globalData = 0;
319 }
320
321 void Heap::waitForRelativeTimeWhileHoldingLock(double relative)
322 {
323     if (m_blockFreeingThreadShouldQuit)
324         return;
325     m_freeBlockCondition.timedWait(m_freeBlockLock, currentTime() + relative);
326 }
327
328 void Heap::waitForRelativeTime(double relative)
329 {
330     // If this returns early, that's fine, so long as it doesn't do it too
331     // frequently. It would only be a bug if this function failed to return
332     // when it was asked to do so.
333     
334     MutexLocker locker(m_freeBlockLock);
335     waitForRelativeTimeWhileHoldingLock(relative);
336 }
337
338 void* Heap::blockFreeingThreadStartFunc(void* heap)
339 {
340     static_cast<Heap*>(heap)->blockFreeingThreadMain();
341     return 0;
342 }
343
344 void Heap::blockFreeingThreadMain()
345 {
346     while (!m_blockFreeingThreadShouldQuit) {
347         // Generally wait for one second before scavenging free blocks. This
348         // may return early, particularly when we're being asked to quit.
349         waitForRelativeTime(1.0);
350         if (m_blockFreeingThreadShouldQuit)
351             break;
352         
353         // Now process the list of free blocks. Keep freeing until half of the
354         // blocks that are currently on the list are gone. Assume that a size_t
355         // field can be accessed atomically.
356         size_t currentNumberOfFreeBlocks = m_numberOfFreeBlocks;
357         if (!currentNumberOfFreeBlocks)
358             continue;
359         
360         size_t desiredNumberOfFreeBlocks = currentNumberOfFreeBlocks / 2;
361         
362         while (!m_blockFreeingThreadShouldQuit) {
363             MarkedBlock* block;
364             {
365                 MutexLocker locker(m_freeBlockLock);
366                 if (m_numberOfFreeBlocks <= desiredNumberOfFreeBlocks)
367                     block = 0;
368                 else {
369                     block = m_freeBlocks.removeHead();
370                     ASSERT(block);
371                     m_numberOfFreeBlocks--;
372                 }
373             }
374             
375             if (!block)
376                 break;
377             
378             MarkedBlock::destroy(block);
379         }
380     }
381 }
382
383 void Heap::reportExtraMemoryCostSlowCase(size_t cost)
384 {
385     // Our frequency of garbage collection tries to balance memory use against speed
386     // by collecting based on the number of newly created values. However, for values
387     // that hold on to a great deal of memory that's not in the form of other JS values,
388     // that is not good enough - in some cases a lot of those objects can pile up and
389     // use crazy amounts of memory without a GC happening. So we track these extra
390     // memory costs. Only unusually large objects are noted, and we only keep track
391     // of this extra cost until the next GC. In garbage collected languages, most values
392     // are either very short lived temporaries, or have extremely long lifetimes. So
393     // if a large value survives one garbage collection, there is not much point to
394     // collecting more frequently as long as it stays alive.
395
396     if (m_extraCost > maxExtraCost && m_extraCost > m_markedSpace.highWaterMark() / 2)
397         collectAllGarbage();
398     m_extraCost += cost;
399 }
400
401 inline void* Heap::tryAllocate(MarkedSpace::SizeClass& sizeClass)
402 {
403     m_operationInProgress = Allocation;
404     void* result = m_markedSpace.allocate(sizeClass);
405     m_operationInProgress = NoOperation;
406     return result;
407 }
408
409 void* Heap::allocateSlowCase(MarkedSpace::SizeClass& sizeClass)
410 {
411 #if COLLECT_ON_EVERY_ALLOCATION
412     collectAllGarbage();
413     ASSERT(m_operationInProgress == NoOperation);
414 #endif
415
416     void* result = tryAllocate(sizeClass);
417
418     if (LIKELY(result != 0))
419         return result;
420
421     AllocationEffort allocationEffort;
422     
423     if (m_markedSpace.waterMark() < m_markedSpace.highWaterMark() || !m_isSafeToCollect)
424         allocationEffort = AllocationMustSucceed;
425     else
426         allocationEffort = AllocationCanFail;
427     
428     MarkedBlock* block = allocateBlock(sizeClass.cellSize, allocationEffort);
429     if (block) {
430         m_markedSpace.addBlock(sizeClass, block);
431         void* result = tryAllocate(sizeClass);
432         ASSERT(result);
433         return result;
434     }
435
436     collect(DoNotSweep);
437     
438     result = tryAllocate(sizeClass);
439     
440     if (result)
441         return result;
442     
443     ASSERT(m_markedSpace.waterMark() < m_markedSpace.highWaterMark());
444     
445     m_markedSpace.addBlock(sizeClass, allocateBlock(sizeClass.cellSize, AllocationMustSucceed));
446     
447     result = tryAllocate(sizeClass);
448     ASSERT(result);
449     return result;
450 }
451
452 void Heap::protect(JSValue k)
453 {
454     ASSERT(k);
455     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
456
457     if (!k.isCell())
458         return;
459
460     m_protectedValues.add(k.asCell());
461 }
462
463 bool Heap::unprotect(JSValue k)
464 {
465     ASSERT(k);
466     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
467
468     if (!k.isCell())
469         return false;
470
471     return m_protectedValues.remove(k.asCell());
472 }
473
474 void Heap::markProtectedObjects(HeapRootVisitor& heapRootVisitor)
475 {
476     ProtectCountSet::iterator end = m_protectedValues.end();
477     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
478         heapRootVisitor.visit(&it->first);
479 }
480
481 void Heap::pushTempSortVector(Vector<ValueStringPair>* tempVector)
482 {
483     m_tempSortingVectors.append(tempVector);
484 }
485
486 void Heap::popTempSortVector(Vector<ValueStringPair>* tempVector)
487 {
488     ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last());
489     m_tempSortingVectors.removeLast();
490 }
491     
492 void Heap::markTempSortVectors(HeapRootVisitor& heapRootVisitor)
493 {
494     typedef Vector<Vector<ValueStringPair>* > VectorOfValueStringVectors;
495
496     VectorOfValueStringVectors::iterator end = m_tempSortingVectors.end();
497     for (VectorOfValueStringVectors::iterator it = m_tempSortingVectors.begin(); it != end; ++it) {
498         Vector<ValueStringPair>* tempSortingVector = *it;
499
500         Vector<ValueStringPair>::iterator vectorEnd = tempSortingVector->end();
501         for (Vector<ValueStringPair>::iterator vectorIt = tempSortingVector->begin(); vectorIt != vectorEnd; ++vectorIt) {
502             if (vectorIt->first)
503                 heapRootVisitor.visit(&vectorIt->first);
504         }
505     }
506 }
507
508 void Heap::harvestWeakReferences()
509 {
510     m_slotVisitor.harvestWeakReferences();
511 }
512
513 inline RegisterFile& Heap::registerFile()
514 {
515     return m_globalData->interpreter->registerFile();
516 }
517
518 void Heap::getConservativeRegisterRoots(HashSet<JSCell*>& roots)
519 {
520     ASSERT(isValidThreadState(m_globalData));
521     if (m_operationInProgress != NoOperation)
522         CRASH();
523     m_operationInProgress = Collection;
524     ConservativeRoots registerFileRoots(&m_blocks);
525     registerFile().gatherConservativeRoots(registerFileRoots);
526     size_t registerFileRootCount = registerFileRoots.size();
527     JSCell** registerRoots = registerFileRoots.roots();
528     for (size_t i = 0; i < registerFileRootCount; i++) {
529         setMarked(registerRoots[i]);
530         roots.add(registerRoots[i]);
531     }
532     m_operationInProgress = NoOperation;
533 }
534
535 void Heap::markRoots()
536 {
537     ASSERT(isValidThreadState(m_globalData));
538     if (m_operationInProgress != NoOperation)
539         CRASH();
540     m_operationInProgress = Collection;
541
542     void* dummy;
543
544     // We gather conservative roots before clearing mark bits because conservative
545     // gathering uses the mark bits to determine whether a reference is valid.
546     ConservativeRoots machineThreadRoots(&m_blocks);
547     m_machineThreads.gatherConservativeRoots(machineThreadRoots, &dummy);
548
549     ConservativeRoots registerFileRoots(&m_blocks);
550     registerFile().gatherConservativeRoots(registerFileRoots);
551
552     clearMarks();
553
554     SlotVisitor& visitor = m_slotVisitor;
555     HeapRootVisitor heapRootVisitor(visitor);
556
557     visitor.append(machineThreadRoots);
558     visitor.drain();
559
560     visitor.append(registerFileRoots);
561     visitor.drain();
562
563     markProtectedObjects(heapRootVisitor);
564     visitor.drain();
565     
566     markTempSortVectors(heapRootVisitor);
567     visitor.drain();
568
569     if (m_markListSet && m_markListSet->size())
570         MarkedArgumentBuffer::markLists(heapRootVisitor, *m_markListSet);
571     if (m_globalData->exception)
572         heapRootVisitor.visit(&m_globalData->exception);
573     visitor.drain();
574
575     m_handleHeap.visitStrongHandles(heapRootVisitor);
576     visitor.drain();
577
578     m_handleStack.visit(heapRootVisitor);
579     visitor.drain();
580
581     // Weak handles must be marked last, because their owners use the set of
582     // opaque roots to determine reachability.
583     int lastOpaqueRootCount;
584     do {
585         lastOpaqueRootCount = visitor.opaqueRootCount();
586         m_handleHeap.visitWeakHandles(heapRootVisitor);
587         visitor.drain();
588     // If the set of opaque roots has grown, more weak handles may have become reachable.
589     } while (lastOpaqueRootCount != visitor.opaqueRootCount());
590
591     // Need to call this here because weak handle processing could add weak
592     // reference harvesters.
593     harvestWeakReferences();
594
595     visitor.reset();
596
597     m_operationInProgress = NoOperation;
598 }
599
600 void Heap::clearMarks()
601 {
602     forEachBlock<ClearMarks>();
603 }
604
605 void Heap::sweep()
606 {
607     forEachBlock<Sweep>();
608 }
609
610 size_t Heap::objectCount()
611 {
612     return forEachBlock<MarkCount>();
613 }
614
615 size_t Heap::size()
616 {
617     return forEachBlock<Size>();
618 }
619
620 size_t Heap::capacity()
621 {
622     return forEachBlock<Capacity>();
623 }
624
625 size_t Heap::protectedGlobalObjectCount()
626 {
627     return forEachProtectedCell<CountIfGlobalObject>();
628 }
629
630 size_t Heap::globalObjectCount()
631 {
632     return forEachCell<CountIfGlobalObject>();
633 }
634
635 size_t Heap::protectedObjectCount()
636 {
637     return forEachProtectedCell<Count>();
638 }
639
640 PassOwnPtr<TypeCountSet> Heap::protectedObjectTypeCounts()
641 {
642     return forEachProtectedCell<RecordType>();
643 }
644
645 PassOwnPtr<TypeCountSet> Heap::objectTypeCounts()
646 {
647     return forEachCell<RecordType>();
648 }
649
650 void Heap::collectAllGarbage()
651 {
652     if (!m_isSafeToCollect)
653         return;
654     if (!m_globalData->dynamicGlobalObject)
655         m_globalData->recompileAllJSFunctions();
656
657     collect(DoSweep);
658 }
659
660 void Heap::collect(SweepToggle sweepToggle)
661 {
662     ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable());
663     ASSERT(m_isSafeToCollect);
664     JAVASCRIPTCORE_GC_BEGIN();
665     
666     canonicalizeBlocks();
667     
668     markRoots();
669     m_handleHeap.finalizeWeakHandles();
670     m_globalData->smallStrings.finalizeSmallStrings();
671
672     JAVASCRIPTCORE_GC_MARKED();
673     
674     resetAllocator();
675
676     if (sweepToggle == DoSweep) {
677         sweep();
678         shrink();
679     }
680
681     // To avoid pathological GC churn in large heaps, we set the allocation high
682     // water mark to be proportional to the current size of the heap. The exact
683     // proportion is a bit arbitrary. A 2X multiplier gives a 1:1 (heap size :
684     // new bytes allocated) proportion, and seems to work well in benchmarks.
685     size_t proportionalBytes = 2 * size();
686     m_markedSpace.setHighWaterMark(max(proportionalBytes, m_minBytesPerCycle));
687     JAVASCRIPTCORE_GC_END();
688
689     (*m_activityCallback)();
690 }
691
692 void Heap::canonicalizeBlocks()
693 {
694     m_markedSpace.canonicalizeBlocks();
695 }
696
697 void Heap::resetAllocator()
698 {
699     m_extraCost = 0;
700     m_markedSpace.resetAllocator();
701 }
702
703 void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback)
704 {
705     m_activityCallback = activityCallback;
706 }
707
708 GCActivityCallback* Heap::activityCallback()
709 {
710     return m_activityCallback.get();
711 }
712
713 bool Heap::isValidAllocation(size_t bytes)
714 {
715     if (!isValidThreadState(m_globalData))
716         return false;
717
718     if (bytes > MarkedSpace::maxCellSize)
719         return false;
720
721     if (m_operationInProgress != NoOperation)
722         return false;
723     
724     return true;
725 }
726
727 MarkedBlock* Heap::allocateBlock(size_t cellSize, Heap::AllocationEffort allocationEffort)
728 {
729     MarkedBlock* block;
730     
731     {
732         MutexLocker locker(m_freeBlockLock);
733         if (m_numberOfFreeBlocks) {
734             block = m_freeBlocks.removeHead();
735             ASSERT(block);
736             m_numberOfFreeBlocks--;
737         } else
738             block = 0;
739     }
740     if (block)
741         block->initForCellSize(cellSize);
742     else if (allocationEffort == AllocationCanFail)
743         return 0;
744     else
745         block = MarkedBlock::create(this, cellSize);
746     
747     m_blocks.add(block);
748
749     return block;
750 }
751
752 void Heap::freeBlocks(MarkedBlock* head)
753 {
754     MarkedBlock* next;
755     for (MarkedBlock* block = head; block; block = next) {
756         next = block->next();
757
758         m_blocks.remove(block);
759         block->reset();
760         MutexLocker locker(m_freeBlockLock);
761         m_freeBlocks.append(block);
762         m_numberOfFreeBlocks++;
763     }
764 }
765
766 void Heap::shrink()
767 {
768     // We record a temporary list of empties to avoid modifying m_blocks while iterating it.
769     TakeIfEmpty takeIfEmpty(&m_markedSpace);
770     freeBlocks(forEachBlock(takeIfEmpty));
771 }
772
773 void Heap::releaseFreeBlocks()
774 {
775     while (true) {
776         MarkedBlock* block;
777         {
778             MutexLocker locker(m_freeBlockLock);
779             if (!m_numberOfFreeBlocks)
780                 block = 0;
781             else {
782                 block = m_freeBlocks.removeHead();
783                 ASSERT(block);
784                 m_numberOfFreeBlocks--;
785             }
786         }
787         
788         if (!block)
789             break;
790         
791         MarkedBlock::destroy(block);
792     }
793 }
794
795 #if ENABLE(GGC)
796 void Heap::writeBarrierSlowCase(const JSCell* owner, JSCell* cell)
797 {
798 }
799
800 #else
801
802 void Heap::writeBarrierSlowCase(const JSCell*, JSCell*)
803 {
804 }
805 #endif
806
807 } // namespace JSC