1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
* vim: set ts=8 sw=4 et tw=78: |
* vim: set ts=8 sw=4 et tw=78: |
3 |
* |
* |
4 |
* ***** BEGIN LICENSE BLOCK ***** |
* ***** BEGIN LICENSE BLOCK ***** |
48 |
* |
* |
49 |
* XXX swizzle page to freelist for better locality of reference |
* XXX swizzle page to freelist for better locality of reference |
50 |
*/ |
*/ |
|
#include "jsstddef.h" |
|
51 |
#include <stdlib.h> /* for free */ |
#include <stdlib.h> /* for free */ |
52 |
#include <math.h> |
#include <math.h> |
53 |
#include <string.h> /* for memset used when DEBUG */ |
#include <string.h> /* for memset used when DEBUG */ |
54 |
#include "jstypes.h" |
#include "jstypes.h" |
55 |
|
#include "jsstdint.h" |
56 |
#include "jsutil.h" /* Added by JSIFY */ |
#include "jsutil.h" /* Added by JSIFY */ |
57 |
#include "jshash.h" /* Added by JSIFY */ |
#include "jshash.h" /* Added by JSIFY */ |
58 |
#include "jsbit.h" |
#include "jsbit.h" |
74 |
#include "jsparse.h" |
#include "jsparse.h" |
75 |
#include "jsscope.h" |
#include "jsscope.h" |
76 |
#include "jsscript.h" |
#include "jsscript.h" |
77 |
|
#include "jsstaticcheck.h" |
78 |
#include "jsstr.h" |
#include "jsstr.h" |
79 |
|
#include "jstask.h" |
80 |
#include "jstracer.h" |
#include "jstracer.h" |
81 |
|
|
82 |
#if JS_HAS_XML_SUPPORT |
#if JS_HAS_XML_SUPPORT |
83 |
#include "jsxml.h" |
#include "jsxml.h" |
84 |
#endif |
#endif |
85 |
|
|
86 |
|
#ifdef INCLUDE_MOZILLA_DTRACE |
87 |
|
#include "jsdtracef.h" |
88 |
|
#endif |
89 |
|
|
90 |
/* |
/* |
91 |
* Check if posix_memalign is available. |
* Check if posix_memalign is available. |
92 |
*/ |
*/ |
115 |
# define JS_GC_USE_MMAP 1 |
# define JS_GC_USE_MMAP 1 |
116 |
# endif |
# endif |
117 |
# include <windows.h> |
# include <windows.h> |
118 |
|
# elif defined(__SYMBIAN32__) |
119 |
|
// Symbian's OpenC has mmap (and #defines _POSIX_MAPPED_FILES), but |
120 |
|
// doesn't implement MAP_ANON. If we have MOZ_MEMORY, then we can use |
121 |
|
// posix_memalign; we've defined HAS_POSIX_MEMALIGN above. Otherwise, |
122 |
|
// we overallocate. |
123 |
# else |
# else |
124 |
# if defined(XP_UNIX) || defined(XP_BEOS) |
# if defined(XP_UNIX) || defined(XP_BEOS) |
125 |
# include <unistd.h> |
# include <unistd.h> |
134 |
# if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) |
# if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) |
135 |
# define MAP_ANONYMOUS MAP_ANON |
# define MAP_ANONYMOUS MAP_ANON |
136 |
# endif |
# endif |
137 |
|
# if !defined(MAP_ANONYMOUS) |
138 |
|
# define MAP_ANONYMOUS 0 |
139 |
|
# endif |
140 |
# else |
# else |
141 |
# if JS_GC_USE_MMAP |
# if JS_GC_USE_MMAP |
142 |
# error "JS_GC_USE_MMAP is set when mmap is not available" |
# error "JS_GC_USE_MMAP is set when mmap is not available" |
146 |
#endif |
#endif |
147 |
|
|
148 |
/* |
/* |
149 |
|
* Check JSTempValueUnion has the size of jsval and void * so we can |
150 |
|
* reinterpret jsval as void* GC-thing pointer and use JSTVU_SINGLE for |
151 |
|
* different GC-things. |
152 |
|
*/ |
153 |
|
JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(jsval)); |
154 |
|
JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(void *)); |
155 |
|
|
156 |
|
/* |
157 |
|
* Check that JSTRACE_XML follows JSTRACE_OBJECT, JSTRACE_DOUBLE and |
158 |
|
* JSTRACE_STRING. |
159 |
|
*/ |
160 |
|
JS_STATIC_ASSERT(JSTRACE_OBJECT == 0); |
161 |
|
JS_STATIC_ASSERT(JSTRACE_DOUBLE == 1); |
162 |
|
JS_STATIC_ASSERT(JSTRACE_STRING == 2); |
163 |
|
JS_STATIC_ASSERT(JSTRACE_XML == 3); |
164 |
|
|
165 |
|
/* |
166 |
|
* JS_IS_VALID_TRACE_KIND assumes that JSTRACE_STRING is the last non-xml |
167 |
|
* trace kind when JS_HAS_XML_SUPPORT is false. |
168 |
|
*/ |
169 |
|
JS_STATIC_ASSERT(JSTRACE_STRING + 1 == JSTRACE_XML); |
170 |
|
|
171 |
|
/* |
172 |
|
* The number of used GCX-types must stay within GCX_LIMIT. |
173 |
|
*/ |
174 |
|
JS_STATIC_ASSERT(GCX_NTYPES <= GCX_LIMIT); |
175 |
|
|
176 |
|
|
177 |
|
/* |
178 |
|
* Check that we can reinterpret double as JSGCDoubleCell. |
179 |
|
*/ |
180 |
|
JS_STATIC_ASSERT(sizeof(JSGCDoubleCell) == sizeof(double)); |
181 |
|
|
182 |
|
/* |
183 |
|
* Check that we can use memset(p, 0, ...) to implement JS_CLEAR_WEAK_ROOTS. |
184 |
|
*/ |
185 |
|
JS_STATIC_ASSERT(JSVAL_NULL == 0); |
186 |
|
|
187 |
|
|
188 |
|
/* |
189 |
* A GC arena contains a fixed number of flag bits for each thing in its heap, |
* A GC arena contains a fixed number of flag bits for each thing in its heap, |
190 |
* and supports O(1) lookup of a flag given its thing's address. |
* and supports O(1) lookup of a flag given its thing's address. |
191 |
* |
* |
441 |
((GC_ARENA_SIZE - (uint32) sizeof(JSGCArenaInfo)) / ((thingSize) + 1U)) |
((GC_ARENA_SIZE - (uint32) sizeof(JSGCArenaInfo)) / ((thingSize) + 1U)) |
442 |
|
|
443 |
#define THING_TO_ARENA(thing) \ |
#define THING_TO_ARENA(thing) \ |
444 |
((JSGCArenaInfo *)(((jsuword) (thing) | GC_ARENA_MASK) + \ |
(JS_ASSERT(!JSString::isStatic(thing)), \ |
445 |
1 - sizeof(JSGCArenaInfo))) |
(JSGCArenaInfo *)(((jsuword) (thing) | GC_ARENA_MASK) \ |
446 |
|
+ 1 - sizeof(JSGCArenaInfo))) |
447 |
|
|
448 |
#define THING_TO_INDEX(thing, thingSize) \ |
#define THING_TO_INDEX(thing, thingSize) \ |
449 |
((uint32) ((jsuword) (thing) & GC_ARENA_MASK) / (uint32) (thingSize)) |
((uint32) ((jsuword) (thing) & GC_ARENA_MASK) / (uint32) (thingSize)) |
652 |
* The maximum number of things to put on the local free list by taking |
* The maximum number of things to put on the local free list by taking |
653 |
* several things from the global free list or from the tail of the last |
* several things from the global free list or from the tail of the last |
654 |
* allocated arena to amortize the cost of rt->gcLock. |
* allocated arena to amortize the cost of rt->gcLock. |
|
* |
|
|
* We use number 8 based on benchmarks from bug 312238. |
|
655 |
*/ |
*/ |
656 |
#define MAX_THREAD_LOCAL_THINGS 8 |
#define MAX_THREAD_LOCAL_THINGS 64 |
657 |
|
|
658 |
#endif |
#endif |
659 |
|
|
723 |
{ |
{ |
724 |
if (table->array) { |
if (table->array) { |
725 |
JS_ASSERT(table->count > 0); |
JS_ASSERT(table->count > 0); |
726 |
free(table->array); |
js_free(table->array); |
727 |
table->array = NULL; |
table->array = NULL; |
728 |
table->count = 0; |
table->count = 0; |
729 |
} |
} |
757 |
if (capacity > (size_t)-1 / sizeof table->array[0]) |
if (capacity > (size_t)-1 / sizeof table->array[0]) |
758 |
goto bad; |
goto bad; |
759 |
} |
} |
760 |
array = (void **) realloc(table->array, |
array = (void **) js_realloc(table->array, |
761 |
capacity * sizeof table->array[0]); |
capacity * sizeof table->array[0]); |
762 |
if (!array) |
if (!array) |
763 |
goto bad; |
goto bad; |
764 |
#ifdef DEBUG |
#ifdef DEBUG |
797 |
array = table->array; |
array = table->array; |
798 |
JS_ASSERT(array); |
JS_ASSERT(array); |
799 |
if (capacity == 0) { |
if (capacity == 0) { |
800 |
free(array); |
js_free(array); |
801 |
table->array = NULL; |
table->array = NULL; |
802 |
return; |
return; |
803 |
} |
} |
804 |
array = (void **) realloc(array, capacity * sizeof array[0]); |
array = (void **) js_realloc(array, capacity * sizeof array[0]); |
805 |
if (array) |
if (array) |
806 |
table->array = array; |
table->array = array; |
807 |
} |
} |
882 |
* |
* |
883 |
* bytes to ensure that we always have room to store the gap. |
* bytes to ensure that we always have room to store the gap. |
884 |
*/ |
*/ |
885 |
p = malloc((js_gcArenasPerChunk + 1) << GC_ARENA_SHIFT); |
p = js_malloc((js_gcArenasPerChunk + 1) << GC_ARENA_SHIFT); |
886 |
if (!p) |
if (!p) |
887 |
return 0; |
return 0; |
888 |
|
|
914 |
#endif |
#endif |
915 |
|
|
916 |
#if HAS_POSIX_MEMALIGN |
#if HAS_POSIX_MEMALIGN |
917 |
free((void *) chunk); |
js_free((void *) chunk); |
918 |
#else |
#else |
919 |
/* See comments in NewGCChunk. */ |
/* See comments in NewGCChunk. */ |
920 |
JS_ASSERT(*GetMallocedChunkGapPtr(chunk) < GC_ARENA_SIZE); |
JS_ASSERT(*GetMallocedChunkGapPtr(chunk) < GC_ARENA_SIZE); |
921 |
free((void *) (chunk - *GetMallocedChunkGapPtr(chunk))); |
js_free((void *) (chunk - *GetMallocedChunkGapPtr(chunk))); |
922 |
#endif |
#endif |
923 |
} |
} |
924 |
|
|
1093 |
for (i = 0; i < GC_NUM_FREELISTS; i++) { |
for (i = 0; i < GC_NUM_FREELISTS; i++) { |
1094 |
arenaList = &rt->gcArenaList[i]; |
arenaList = &rt->gcArenaList[i]; |
1095 |
thingSize = GC_FREELIST_NBYTES(i); |
thingSize = GC_FREELIST_NBYTES(i); |
|
JS_ASSERT((size_t)(uint16)thingSize == thingSize); |
|
1096 |
arenaList->last = NULL; |
arenaList->last = NULL; |
1097 |
arenaList->lastCount = (uint16) THINGS_PER_ARENA(thingSize); |
arenaList->lastCount = THINGS_PER_ARENA(thingSize); |
1098 |
arenaList->thingSize = (uint16) thingSize; |
arenaList->thingSize = thingSize; |
1099 |
arenaList->freeList = NULL; |
arenaList->freeList = NULL; |
1100 |
} |
} |
1101 |
rt->gcDoubleArenaList.first = NULL; |
rt->gcDoubleArenaList.first = NULL; |
1146 |
JSGCArenaInfo *a; |
JSGCArenaInfo *a; |
1147 |
uint32 index; |
uint32 index; |
1148 |
|
|
1149 |
|
if (JSString::isStatic(thing)) |
1150 |
|
return NULL; |
1151 |
a = THING_TO_ARENA(thing); |
a = THING_TO_ARENA(thing); |
1152 |
if (!a->list) |
if (!a->list) |
1153 |
return NULL; |
return NULL; |
1158 |
intN |
intN |
1159 |
js_GetExternalStringGCType(JSString *str) |
js_GetExternalStringGCType(JSString *str) |
1160 |
{ |
{ |
1161 |
uintN type; |
JS_ASSERT(!JSString::isStatic(str)); |
1162 |
|
|
1163 |
type = (uintN) *GetGCThingFlags(str) & GCF_TYPEMASK; |
uintN type = (uintN) *GetGCThingFlags(str) & GCF_TYPEMASK; |
1164 |
JS_ASSERT(type == GCX_STRING || type >= GCX_EXTERNAL_STRING); |
JS_ASSERT(type == GCX_STRING || type >= GCX_EXTERNAL_STRING); |
1165 |
return (type == GCX_STRING) ? -1 : (intN) (type - GCX_EXTERNAL_STRING); |
return (type == GCX_STRING) ? -1 : (intN) (type - GCX_EXTERNAL_STRING); |
1166 |
} |
} |
1182 |
JSGCArenaInfo *a; |
JSGCArenaInfo *a; |
1183 |
uint32 index; |
uint32 index; |
1184 |
|
|
1185 |
|
if (JSString::isStatic(thing)) |
1186 |
|
return JSTRACE_STRING; |
1187 |
|
|
1188 |
a = THING_TO_ARENA(thing); |
a = THING_TO_ARENA(thing); |
1189 |
if (!a->list) |
if (!a->list) |
1190 |
return JSTRACE_DOUBLE; |
return JSTRACE_DOUBLE; |
1212 |
JSGCArenaInfo *a; |
JSGCArenaInfo *a; |
1213 |
uint32 index, flags; |
uint32 index, flags; |
1214 |
|
|
1215 |
|
if (JSString::isStatic(thing)) |
1216 |
|
return false; |
1217 |
|
|
1218 |
a = THING_TO_ARENA(thing); |
a = THING_TO_ARENA(thing); |
1219 |
if (!a->list) { |
if (!a->list) { |
1220 |
/* |
/* |
1313 |
rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; |
rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; |
1314 |
rt->gcEmptyArenaPoolLifespan = 30000; |
rt->gcEmptyArenaPoolLifespan = 30000; |
1315 |
|
|
1316 |
|
/* |
1317 |
|
* By default the trigger factor gets maximum possible value. This |
1318 |
|
* means that GC will not be triggered by growth of GC memory (gcBytes). |
1319 |
|
*/ |
1320 |
|
rt->setGCTriggerFactor((uint32) -1); |
1321 |
|
|
1322 |
|
/* |
1323 |
|
* The assigned value prevents GC from running when GC memory is too low |
1324 |
|
* (during JS engine start). |
1325 |
|
*/ |
1326 |
|
rt->setGCLastBytes(8192); |
1327 |
|
|
1328 |
METER(memset(&rt->gcStats, 0, sizeof rt->gcStats)); |
METER(memset(&rt->gcStats, 0, sizeof rt->gcStats)); |
1329 |
return JS_TRUE; |
return JS_TRUE; |
1330 |
} |
} |
1482 |
CheckLeakedRoots(JSRuntime *rt); |
CheckLeakedRoots(JSRuntime *rt); |
1483 |
#endif |
#endif |
1484 |
|
|
|
#ifdef JS_THREADSAFE |
|
|
static void |
|
|
TrimGCFreeListsPool(JSRuntime *rt, uintN keepCount); |
|
|
#endif |
|
|
|
|
1485 |
void |
void |
1486 |
js_FinishGC(JSRuntime *rt) |
js_FinishGC(JSRuntime *rt) |
1487 |
{ |
{ |
1493 |
#endif |
#endif |
1494 |
|
|
1495 |
FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo); |
FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo); |
|
#ifdef JS_THREADSAFE |
|
|
TrimGCFreeListsPool(rt, 0); |
|
|
JS_ASSERT(!rt->gcFreeListsPool); |
|
|
#endif |
|
1496 |
FinishGCArenaLists(rt); |
FinishGCArenaLists(rt); |
1497 |
|
|
1498 |
if (rt->gcRootsHash.ops) { |
if (rt->gcRootsHash.ops) { |
1529 |
* properly with a racing GC, without calling JS_AddRoot from a request. |
* properly with a racing GC, without calling JS_AddRoot from a request. |
1530 |
* We have to preserve API compatibility here, now that we avoid holding |
* We have to preserve API compatibility here, now that we avoid holding |
1531 |
* rt->gcLock across the mark phase (including the root hashtable mark). |
* rt->gcLock across the mark phase (including the root hashtable mark). |
|
* |
|
|
* If the GC is running and we're called on another thread, wait for this |
|
|
* GC activation to finish. We can safely wait here (in the case where we |
|
|
* are called within a request on another thread's context) without fear |
|
|
* of deadlock because the GC doesn't set rt->gcRunning until after it has |
|
|
* waited for all active requests to end. |
|
1532 |
*/ |
*/ |
1533 |
JS_LOCK_GC(rt); |
JS_LOCK_GC(rt); |
1534 |
#ifdef JS_THREADSAFE |
js_WaitForGC(rt); |
|
JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); |
|
|
if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { |
|
|
do { |
|
|
JS_AWAIT_GC_DONE(rt); |
|
|
} while (rt->gcLevel > 0); |
|
|
} |
|
|
#endif |
|
1535 |
rhe = (JSGCRootHashEntry *) |
rhe = (JSGCRootHashEntry *) |
1536 |
JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_ADD); |
JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_ADD); |
1537 |
if (rhe) { |
if (rhe) { |
1553 |
* Same synchronization drill as above in js_AddRoot. |
* Same synchronization drill as above in js_AddRoot. |
1554 |
*/ |
*/ |
1555 |
JS_LOCK_GC(rt); |
JS_LOCK_GC(rt); |
1556 |
#ifdef JS_THREADSAFE |
js_WaitForGC(rt); |
|
JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); |
|
|
if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { |
|
|
do { |
|
|
JS_AWAIT_GC_DONE(rt); |
|
|
} while (rt->gcLevel > 0); |
|
|
} |
|
|
#endif |
|
1557 |
(void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE); |
(void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE); |
1558 |
rt->gcPoke = JS_TRUE; |
rt->gcPoke = JS_TRUE; |
1559 |
JS_UNLOCK_GC(rt); |
JS_UNLOCK_GC(rt); |
1725 |
#define NGCHIST 64 |
#define NGCHIST 64 |
1726 |
|
|
1727 |
static struct GCHist { |
static struct GCHist { |
1728 |
JSBool lastDitch; |
bool lastDitch; |
1729 |
JSGCThing *freeList; |
JSGCThing *freeList; |
1730 |
} gchist[NGCHIST]; |
} gchist[NGCHIST]; |
1731 |
|
|
1732 |
unsigned gchpos = 0; |
unsigned gchpos = 0; |
1733 |
#endif |
#endif |
1734 |
|
|
1735 |
#ifdef JS_THREADSAFE |
void |
1736 |
|
JSRuntime::setGCTriggerFactor(uint32 factor) |
|
const JSGCFreeListSet js_GCEmptyFreeListSet = { |
|
|
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, NULL |
|
|
}; |
|
|
|
|
|
static void |
|
|
TrimGCFreeListsPool(JSRuntime *rt, uintN keepCount) |
|
1737 |
{ |
{ |
1738 |
JSGCFreeListSet **cursor, *freeLists, *link; |
JS_ASSERT(factor >= 100); |
1739 |
|
|
1740 |
cursor = &rt->gcFreeListsPool; |
gcTriggerFactor = factor; |
1741 |
while (keepCount != 0) { |
setGCLastBytes(gcLastBytes); |
|
--keepCount; |
|
|
freeLists = *cursor; |
|
|
if (!freeLists) |
|
|
return; |
|
|
memset(freeLists->array, 0, sizeof freeLists->array); |
|
|
cursor = &freeLists->link; |
|
|
} |
|
|
freeLists = *cursor; |
|
|
if (freeLists) { |
|
|
*cursor = NULL; |
|
|
do { |
|
|
link = freeLists->link; |
|
|
free(freeLists); |
|
|
} while ((freeLists = link) != NULL); |
|
|
} |
|
1742 |
} |
} |
1743 |
|
|
1744 |
void |
void |
1745 |
js_RevokeGCLocalFreeLists(JSContext *cx) |
JSRuntime::setGCLastBytes(size_t lastBytes) |
1746 |
{ |
{ |
1747 |
JS_ASSERT(!cx->gcLocalFreeLists->link); |
gcLastBytes = lastBytes; |
1748 |
if (cx->gcLocalFreeLists != &js_GCEmptyFreeListSet) { |
uint64 triggerBytes = uint64(lastBytes) * uint64(gcTriggerFactor / 100); |
1749 |
cx->gcLocalFreeLists->link = cx->runtime->gcFreeListsPool; |
if (triggerBytes != size_t(triggerBytes)) |
1750 |
cx->runtime->gcFreeListsPool = cx->gcLocalFreeLists; |
triggerBytes = size_t(-1); |
1751 |
cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet; |
gcTriggerBytes = size_t(triggerBytes); |
|
} |
|
1752 |
} |
} |
1753 |
|
|
1754 |
static JSGCFreeListSet * |
static JS_INLINE bool |
1755 |
EnsureLocalFreeList(JSContext *cx) |
IsGCThresholdReached(JSRuntime *rt) |
1756 |
{ |
{ |
1757 |
JSGCFreeListSet *freeLists; |
#ifdef JS_GC_ZEAL |
1758 |
|
if (rt->gcZeal >= 1) |
1759 |
freeLists = cx->gcLocalFreeLists; |
return true; |
1760 |
if (freeLists != &js_GCEmptyFreeListSet) { |
#endif |
|
JS_ASSERT(freeLists); |
|
|
return freeLists; |
|
|
} |
|
1761 |
|
|
1762 |
freeLists = cx->runtime->gcFreeListsPool; |
/* |
1763 |
if (freeLists) { |
* Since the initial value of the gcLastBytes parameter is not equal to |
1764 |
cx->runtime->gcFreeListsPool = freeLists->link; |
* zero (see the js_InitGC function) the return value is false when |
1765 |
freeLists->link = NULL; |
* the gcBytes value is close to zero at the JS engine start. |
1766 |
} else { |
*/ |
1767 |
/* JS_malloc is not used as the caller reports out-of-memory itself. */ |
return rt->gcMallocBytes >= rt->gcMaxMallocBytes || |
1768 |
freeLists = (JSGCFreeListSet *) calloc(1, sizeof *freeLists); |
rt->gcBytes >= rt->gcTriggerBytes; |
|
if (!freeLists) |
|
|
return NULL; |
|
|
} |
|
|
cx->gcLocalFreeLists = freeLists; |
|
|
return freeLists; |
|
1769 |
} |
} |
1770 |
|
|
1771 |
#endif |
template <class T> static JS_INLINE T* |
1772 |
|
NewGCThing(JSContext *cx, uintN flags) |
|
void * |
|
|
js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) |
|
1773 |
{ |
{ |
1774 |
JSRuntime *rt; |
JSRuntime *rt; |
1775 |
uintN flindex; |
bool doGC; |
|
JSBool doGC; |
|
1776 |
JSGCThing *thing; |
JSGCThing *thing; |
1777 |
uint8 *flagp; |
uint8 *flagp; |
1778 |
JSGCArenaList *arenaList; |
JSGCArenaList *arenaList; |
1785 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
1786 |
JSBool gcLocked; |
JSBool gcLocked; |
1787 |
uintN localMallocBytes; |
uintN localMallocBytes; |
|
JSGCFreeListSet *freeLists; |
|
1788 |
JSGCThing **lastptr; |
JSGCThing **lastptr; |
1789 |
JSGCThing *tmpthing; |
JSGCThing *tmpthing; |
1790 |
uint8 *tmpflagp; |
uint8 *tmpflagp; |
1793 |
|
|
1794 |
JS_ASSERT((flags & GCF_TYPEMASK) != GCX_DOUBLE); |
JS_ASSERT((flags & GCF_TYPEMASK) != GCX_DOUBLE); |
1795 |
rt = cx->runtime; |
rt = cx->runtime; |
1796 |
nbytes = JS_ROUNDUP(nbytes, sizeof(JSGCThing)); |
size_t nbytes = sizeof(T); |
1797 |
flindex = GC_FREELIST_INDEX(nbytes); |
JS_ASSERT(JS_ROUNDUP(nbytes, sizeof(JSGCThing)) == nbytes); |
1798 |
|
uintN flindex = GC_FREELIST_INDEX(nbytes); |
1799 |
|
|
1800 |
/* Updates of metering counters here may not be thread-safe. */ |
/* Updates of metering counters here may not be thread-safe. */ |
1801 |
METER(astats = &cx->runtime->gcStats.arenaStats[flindex]); |
METER(astats = &cx->runtime->gcStats.arenaStats[flindex]); |
1804 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
1805 |
gcLocked = JS_FALSE; |
gcLocked = JS_FALSE; |
1806 |
JS_ASSERT(cx->thread); |
JS_ASSERT(cx->thread); |
1807 |
freeLists = cx->gcLocalFreeLists; |
|
1808 |
thing = freeLists->array[flindex]; |
JSGCThing *&freeList = cx->thread->gcFreeLists[flindex]; |
1809 |
localMallocBytes = cx->thread->gcMallocBytes; |
thing = freeList; |
1810 |
|
localMallocBytes = JS_THREAD_DATA(cx)->gcMallocBytes; |
1811 |
if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) { |
if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) { |
1812 |
flagp = thing->flagp; |
flagp = thing->flagp; |
1813 |
freeLists->array[flindex] = thing->next; |
freeList = thing->next; |
1814 |
METER(astats->localalloc++); |
METER(astats->localalloc++); |
1815 |
goto success; |
goto success; |
1816 |
} |
} |
1820 |
|
|
1821 |
/* Transfer thread-local counter to global one. */ |
/* Transfer thread-local counter to global one. */ |
1822 |
if (localMallocBytes != 0) { |
if (localMallocBytes != 0) { |
1823 |
cx->thread->gcMallocBytes = 0; |
JS_THREAD_DATA(cx)->gcMallocBytes = 0; |
1824 |
if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes) |
if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes) |
1825 |
rt->gcMallocBytes = rt->gcMaxMallocBytes; |
rt->gcMallocBytes = rt->gcMaxMallocBytes; |
1826 |
else |
else |
1834 |
return NULL; |
return NULL; |
1835 |
} |
} |
1836 |
|
|
1837 |
doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke); |
#if defined JS_GC_ZEAL && defined JS_TRACER |
1838 |
#ifdef JS_GC_ZEAL |
if (rt->gcZeal >= 1 && JS_TRACE_MONITOR(cx).useReservedObjects) |
1839 |
doGC = doGC || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke); |
goto testReservedObjects; |
1840 |
#endif |
#endif |
1841 |
|
|
1842 |
arenaList = &rt->gcArenaList[flindex]; |
arenaList = &rt->gcArenaList[flindex]; |
1843 |
|
doGC = IsGCThresholdReached(rt); |
1844 |
for (;;) { |
for (;;) { |
1845 |
if (doGC && !JS_ON_TRACE(cx)) { |
if (doGC |
1846 |
|
#ifdef JS_TRACER |
1847 |
|
&& !JS_ON_TRACE(cx) && !JS_TRACE_MONITOR(cx).useReservedObjects |
1848 |
|
#endif |
1849 |
|
) { |
1850 |
/* |
/* |
1851 |
* Keep rt->gcLock across the call into js_GC so we don't starve |
* Keep rt->gcLock across the call into js_GC so we don't starve |
1852 |
* and lose to racing threads who deplete the heap just after |
* and lose to racing threads who deplete the heap just after |
1869 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
1870 |
/* |
/* |
1871 |
* Refill the local free list by taking several things from the |
* Refill the local free list by taking several things from the |
1872 |
* global free list unless we are still at rt->gcMaxMallocBytes |
* global free list unless the free list is already populated or |
1873 |
* barrier or the free list is already populated. The former |
* we are still at rt->gcMaxMallocBytes barrier. The former is |
1874 |
* happens when GC is canceled due to !gcCallback(cx, JSGC_BEGIN) |
* caused via allocating new things in gcCallback(cx, JSGC_END). |
1875 |
* or no gcPoke. The latter is caused via allocating new things |
* The latter happens when GC is canceled due to |
1876 |
* in gcCallback(cx, JSGC_END). |
* gcCallback(cx, JSGC_BEGIN) returning false. |
1877 |
*/ |
*/ |
1878 |
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes) |
if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes) |
|
break; |
|
|
|
|
|
freeLists = EnsureLocalFreeList(cx); |
|
|
if (!freeLists) |
|
|
goto fail; |
|
|
if (freeLists->array[flindex]) |
|
1879 |
break; |
break; |
1880 |
|
|
1881 |
tmpthing = arenaList->freeList; |
tmpthing = arenaList->freeList; |
1887 |
tmpthing = tmpthing->next; |
tmpthing = tmpthing->next; |
1888 |
} while (--maxFreeThings != 0); |
} while (--maxFreeThings != 0); |
1889 |
|
|
1890 |
freeLists->array[flindex] = arenaList->freeList; |
freeList = arenaList->freeList; |
1891 |
arenaList->freeList = tmpthing->next; |
arenaList->freeList = tmpthing->next; |
1892 |
tmpthing->next = NULL; |
tmpthing->next = NULL; |
1893 |
} |
} |
1905 |
JS_ASSERT(arenaList->lastCount < thingsLimit); |
JS_ASSERT(arenaList->lastCount < thingsLimit); |
1906 |
a = arenaList->last; |
a = arenaList->last; |
1907 |
} else { |
} else { |
1908 |
|
#ifdef JS_TRACER |
1909 |
|
if (JS_TRACE_MONITOR(cx).useReservedObjects) { |
1910 |
|
#ifdef JS_GC_ZEAL |
1911 |
|
testReservedObjects: |
1912 |
|
#endif |
1913 |
|
JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx); |
1914 |
|
|
1915 |
|
thing = (JSGCThing *) tm->reservedObjects; |
1916 |
|
flagp = GetGCThingFlags(thing); |
1917 |
|
JS_ASSERT(thing); |
1918 |
|
tm->reservedObjects = JSVAL_TO_OBJECT(tm->reservedObjects->fslots[0]); |
1919 |
|
break; |
1920 |
|
} |
1921 |
|
#endif |
1922 |
|
|
1923 |
a = NewGCArena(rt); |
a = NewGCArena(rt); |
1924 |
if (!a) { |
if (!a) { |
1925 |
if (doGC || JS_ON_TRACE(cx)) |
if (doGC || JS_ON_TRACE(cx)) |
1926 |
goto fail; |
goto fail; |
1927 |
doGC = JS_TRUE; |
doGC = true; |
1928 |
continue; |
continue; |
1929 |
} |
} |
1930 |
a->list = arenaList; |
a->list = arenaList; |
1945 |
* arena. Prefer to order free things by ascending address in the |
* arena. Prefer to order free things by ascending address in the |
1946 |
* (unscientific) hope of better cache locality. |
* (unscientific) hope of better cache locality. |
1947 |
*/ |
*/ |
1948 |
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes) |
if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes) |
|
break; |
|
|
|
|
|
freeLists = EnsureLocalFreeList(cx); |
|
|
if (!freeLists) |
|
|
goto fail; |
|
|
if (freeLists->array[flindex]) |
|
1949 |
break; |
break; |
1950 |
lastptr = &freeLists->array[flindex]; |
lastptr = &freeList; |
1951 |
maxFreeThings = thingsLimit - arenaList->lastCount; |
maxFreeThings = thingsLimit - arenaList->lastCount; |
1952 |
if (maxFreeThings > MAX_THREAD_LOCAL_THINGS) |
if (maxFreeThings > MAX_THREAD_LOCAL_THINGS) |
1953 |
maxFreeThings = MAX_THREAD_LOCAL_THINGS; |
maxFreeThings = MAX_THREAD_LOCAL_THINGS; |
1954 |
|
uint32 lastCount = arenaList->lastCount; |
1955 |
while (maxFreeThings != 0) { |
while (maxFreeThings != 0) { |
1956 |
--maxFreeThings; |
--maxFreeThings; |
1957 |
|
|
1958 |
tmpflagp = THING_FLAGP(a, arenaList->lastCount); |
tmpflagp = THING_FLAGP(a, lastCount); |
1959 |
tmpthing = FLAGP_TO_THING(tmpflagp, nbytes); |
tmpthing = FLAGP_TO_THING(tmpflagp, nbytes); |
1960 |
arenaList->lastCount++; |
lastCount++; |
1961 |
tmpthing->flagp = tmpflagp; |
tmpthing->flagp = tmpflagp; |
1962 |
*tmpflagp = GCF_FINAL; /* signifying that thing is free */ |
*tmpflagp = GCF_FINAL; /* signifying that thing is free */ |
1963 |
|
|
1965 |
lastptr = &tmpthing->next; |
lastptr = &tmpthing->next; |
1966 |
} |
} |
1967 |
*lastptr = NULL; |
*lastptr = NULL; |
1968 |
|
arenaList->lastCount = lastCount; |
1969 |
#endif |
#endif |
1970 |
break; |
break; |
1971 |
} |
} |
2018 |
if (gcLocked) |
if (gcLocked) |
2019 |
JS_UNLOCK_GC(rt); |
JS_UNLOCK_GC(rt); |
2020 |
#endif |
#endif |
2021 |
JS_COUNT_OPERATION(cx, JSOW_ALLOCATION); |
return (T*)thing; |
|
return thing; |
|
2022 |
|
|
2023 |
fail: |
fail: |
2024 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
2026 |
JS_UNLOCK_GC(rt); |
JS_UNLOCK_GC(rt); |
2027 |
#endif |
#endif |
2028 |
METER(astats->fail++); |
METER(astats->fail++); |
2029 |
if (!JS_ON_TRACE(cx)) |
js_ReportOutOfMemory(cx); |
|
JS_ReportOutOfMemory(cx); |
|
2030 |
return NULL; |
return NULL; |
2031 |
} |
} |
2032 |
|
|
2033 |
|
extern JSObject* js_NewGCObject(JSContext *cx, uintN flags) |
2034 |
|
{ |
2035 |
|
return NewGCThing<JSObject>(cx, flags); |
2036 |
|
} |
2037 |
|
|
2038 |
|
extern JSString* js_NewGCString(JSContext *cx, uintN flags) |
2039 |
|
{ |
2040 |
|
return NewGCThing<JSString>(cx, flags); |
2041 |
|
} |
2042 |
|
|
2043 |
|
extern JSFunction* js_NewGCFunction(JSContext *cx, uintN flags) |
2044 |
|
{ |
2045 |
|
return NewGCThing<JSFunction>(cx, flags); |
2046 |
|
} |
2047 |
|
|
2048 |
|
extern JSXML* js_NewGCXML(JSContext *cx, uintN flags) |
2049 |
|
{ |
2050 |
|
return NewGCThing<JSXML>(cx, flags); |
2051 |
|
} |
2052 |
|
|
2053 |
static JSGCDoubleCell * |
static JSGCDoubleCell * |
2054 |
RefillDoubleFreeList(JSContext *cx) |
RefillDoubleFreeList(JSContext *cx) |
2055 |
{ |
{ |
2072 |
return NULL; |
return NULL; |
2073 |
} |
} |
2074 |
|
|
2075 |
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke |
if (IsGCThresholdReached(rt)) |
|
#ifdef JS_GC_ZEAL |
|
|
&& (rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke)) |
|
|
#endif |
|
|
) { |
|
2076 |
goto do_gc; |
goto do_gc; |
|
} |
|
2077 |
|
|
2078 |
/* |
/* |
2079 |
* Loop until we find a flag bitmap byte with unset bits indicating free |
* Loop until we find a flag bitmap byte with unset bits indicating free |
2092 |
if (didGC || JS_ON_TRACE(cx)) { |
if (didGC || JS_ON_TRACE(cx)) { |
2093 |
METER(rt->gcStats.doubleArenaStats.fail++); |
METER(rt->gcStats.doubleArenaStats.fail++); |
2094 |
JS_UNLOCK_GC(rt); |
JS_UNLOCK_GC(rt); |
2095 |
if (!JS_ON_TRACE(cx)) |
js_ReportOutOfMemory(cx); |
|
JS_ReportOutOfMemory(cx); |
|
2096 |
return NULL; |
return NULL; |
2097 |
} |
} |
2098 |
js_GC(cx, GC_LAST_DITCH); |
js_GC(cx, GC_LAST_DITCH); |
2177 |
} while (bit != 0); |
} while (bit != 0); |
2178 |
} |
} |
2179 |
JS_ASSERT(list); |
JS_ASSERT(list); |
|
JS_COUNT_OPERATION(cx, JSOW_ALLOCATION * JS_BITS_PER_WORD); |
|
2180 |
|
|
2181 |
/* |
/* |
2182 |
* We delegate assigning cx->doubleFreeList to js_NewDoubleInRootedValue as |
* We delegate assigning cx->doubleFreeList to js_NewDoubleInRootedValue as |
2232 |
return dp; |
return dp; |
2233 |
} |
} |
2234 |
|
|
2235 |
|
#ifdef JS_TRACER |
2236 |
JSBool |
JSBool |
2237 |
js_AddAsGCBytes(JSContext *cx, size_t sz) |
js_ReserveObjects(JSContext *cx, size_t nobjects) |
2238 |
{ |
{ |
2239 |
JSRuntime *rt; |
/* |
2240 |
|
* Ensure at least nobjects objects are in the list. fslots[1] of each |
2241 |
rt = cx->runtime; |
* object on the reservedObjects list is the length of the list from there. |
2242 |
if (rt->gcBytes >= rt->gcMaxBytes || |
*/ |
2243 |
sz > (size_t) (rt->gcMaxBytes - rt->gcBytes) |
JSObject *&head = JS_TRACE_MONITOR(cx).reservedObjects; |
2244 |
#ifdef JS_GC_ZEAL |
size_t i = head ? JSVAL_TO_INT(head->fslots[1]) : 0; |
2245 |
|| rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke) |
while (i < nobjects) { |
2246 |
#endif |
JSObject *obj = js_NewGCObject(cx, GCX_OBJECT); |
2247 |
) { |
if (!obj) |
|
if (JS_ON_TRACE(cx)) { |
|
|
JS_UNLOCK_GC(rt); |
|
|
return JS_FALSE; |
|
|
} |
|
|
js_GC(cx, GC_LAST_DITCH); |
|
|
if (rt->gcBytes >= rt->gcMaxBytes || |
|
|
sz > (size_t) (rt->gcMaxBytes - rt->gcBytes)) { |
|
|
JS_UNLOCK_GC(rt); |
|
|
JS_ReportOutOfMemory(cx); |
|
2248 |
return JS_FALSE; |
return JS_FALSE; |
2249 |
} |
memset(obj, 0, sizeof(JSObject)); |
2250 |
|
/* The class must be set to something for finalization. */ |
2251 |
|
obj->classword = (jsuword) &js_ObjectClass; |
2252 |
|
obj->fslots[0] = OBJECT_TO_JSVAL(head); |
2253 |
|
i++; |
2254 |
|
obj->fslots[1] = INT_TO_JSVAL(i); |
2255 |
|
head = obj; |
2256 |
} |
} |
|
rt->gcBytes += (uint32) sz; |
|
|
return JS_TRUE; |
|
|
} |
|
2257 |
|
|
2258 |
void |
return JS_TRUE; |
|
js_RemoveAsGCBytes(JSRuntime *rt, size_t sz) |
|
|
{ |
|
|
JS_ASSERT((size_t) rt->gcBytes >= sz); |
|
|
rt->gcBytes -= (uint32) sz; |
|
2259 |
} |
} |
2260 |
|
#endif |
2261 |
|
|
2262 |
/* |
/* |
2263 |
* Shallow GC-things can be locked just by setting the GCF_LOCK bit, because |
* Shallow GC-things can be locked just by setting the GCF_LOCK bit, because |
2268 |
((flagp) && \ |
((flagp) && \ |
2269 |
((*(flagp) & GCF_TYPEMASK) >= GCX_EXTERNAL_STRING || \ |
((*(flagp) & GCF_TYPEMASK) >= GCX_EXTERNAL_STRING || \ |
2270 |
((*(flagp) & GCF_TYPEMASK) == GCX_STRING && \ |
((*(flagp) & GCF_TYPEMASK) == GCX_STRING && \ |
2271 |
!JSSTRING_IS_DEPENDENT((JSString *) (thing))))) |
!((JSString *) (thing))->isDependent()))) |
2272 |
|
|
2273 |
/* This is compatible with JSDHashEntryStub. */ |
/* This is compatible with JSDHashEntryStub. */ |
2274 |
typedef struct JSGCLockHashEntry { |
typedef struct JSGCLockHashEntry { |
2375 |
JS_PUBLIC_API(void) |
JS_PUBLIC_API(void) |
2376 |
JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind) |
JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind) |
2377 |
{ |
{ |
|
JSObject *obj; |
|
|
size_t nslots, i; |
|
|
jsval v; |
|
|
JSString *str; |
|
|
|
|
2378 |
switch (kind) { |
switch (kind) { |
2379 |
case JSTRACE_OBJECT: |
case JSTRACE_OBJECT: { |
2380 |
/* If obj has no map, it must be a newborn. */ |
/* If obj has no map, it must be a newborn. */ |
2381 |
obj = (JSObject *) thing; |
JSObject *obj = (JSObject *) thing; |
2382 |
if (!obj->map) |
if (!obj->map) |
2383 |
break; |
break; |
2384 |
if (obj->map->ops->trace) { |
obj->map->ops->trace(trc, obj); |
|
obj->map->ops->trace(trc, obj); |
|
|
} else { |
|
|
nslots = STOBJ_NSLOTS(obj); |
|
|
for (i = 0; i != nslots; ++i) { |
|
|
v = STOBJ_GET_SLOT(obj, i); |
|
|
if (JSVAL_IS_TRACEABLE(v)) { |
|
|
JS_SET_TRACING_INDEX(trc, "slot", i); |
|
|
JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), |
|
|
JSVAL_TRACE_KIND(v)); |
|
|
} |
|
|
} |
|
|
} |
|
2385 |
break; |
break; |
2386 |
|
} |
2387 |
|
|
2388 |
case JSTRACE_STRING: |
case JSTRACE_STRING: { |
2389 |
str = (JSString *)thing; |
JSString *str = (JSString *) thing; |
2390 |
if (JSSTRING_IS_DEPENDENT(str)) |
if (str->isDependent()) |
2391 |
JS_CALL_STRING_TRACER(trc, JSSTRDEP_BASE(str), "base"); |
JS_CALL_STRING_TRACER(trc, str->dependentBase(), "base"); |
2392 |
break; |
break; |
2393 |
|
} |
2394 |
|
|
2395 |
#if JS_HAS_XML_SUPPORT |
#if JS_HAS_XML_SUPPORT |
2396 |
case JSTRACE_XML: |
case JSTRACE_XML: |
2607 |
|
|
2608 |
case JSTRACE_STRING: |
case JSTRACE_STRING: |
2609 |
for (;;) { |
for (;;) { |
2610 |
|
if (JSString::isStatic(thing)) |
2611 |
|
goto out; |
2612 |
flagp = THING_TO_FLAGP(thing, sizeof(JSGCThing)); |
flagp = THING_TO_FLAGP(thing, sizeof(JSGCThing)); |
2613 |
JS_ASSERT((*flagp & GCF_FINAL) == 0); |
JS_ASSERT((*flagp & GCF_FINAL) == 0); |
2614 |
JS_ASSERT(kind == MapGCFlagsToTraceKind(*flagp)); |
JS_ASSERT(kind == MapGCFlagsToTraceKind(*flagp)); |
2615 |
if (!JSSTRING_IS_DEPENDENT((JSString *) thing)) { |
if (!((JSString *) thing)->isDependent()) { |
2616 |
*flagp |= GCF_MARK; |
*flagp |= GCF_MARK; |
2617 |
goto out; |
goto out; |
2618 |
} |
} |
2619 |
if (*flagp & GCF_MARK) |
if (*flagp & GCF_MARK) |
2620 |
goto out; |
goto out; |
2621 |
*flagp |= GCF_MARK; |
*flagp |= GCF_MARK; |
2622 |
thing = JSSTRDEP_BASE((JSString *) thing); |
thing = ((JSString *) thing)->dependentBase(); |
2623 |
} |
} |
2624 |
/* NOTREACHED */ |
/* NOTREACHED */ |
2625 |
} |
} |
2710 |
jsval *rp = (jsval *)rhe->root; |
jsval *rp = (jsval *)rhe->root; |
2711 |
jsval v = *rp; |
jsval v = *rp; |
2712 |
|
|
2713 |
/* Ignore null object and scalar values. */ |
/* Ignore null reference, scalar values, and static strings. */ |
2714 |
if (!JSVAL_IS_NULL(v) && JSVAL_IS_GCTHING(v)) { |
if (!JSVAL_IS_NULL(v) && |
2715 |
|
JSVAL_IS_GCTHING(v) && |
2716 |
|
!JSString::isStatic(JSVAL_TO_GCTHING(v))) { |
2717 |
#ifdef DEBUG |
#ifdef DEBUG |
2718 |
JSBool root_points_to_gcArenaList = JS_FALSE; |
JSBool root_points_to_gcArenaList = JS_FALSE; |
2719 |
jsuword thing = (jsuword) JSVAL_TO_GCTHING(v); |
jsuword thing = (jsuword) JSVAL_TO_GCTHING(v); |
2799 |
if (fp->callobj) |
if (fp->callobj) |
2800 |
JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call"); |
JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call"); |
2801 |
if (fp->argsobj) |
if (fp->argsobj) |
2802 |
JS_CALL_OBJECT_TRACER(trc, fp->argsobj, "arguments"); |
JS_CALL_OBJECT_TRACER(trc, JSVAL_TO_OBJECT(fp->argsobj), "arguments"); |
2803 |
if (fp->varobj) |
if (fp->varobj) |
2804 |
JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables"); |
JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables"); |
2805 |
if (fp->script) { |
if (fp->script) { |
2806 |
js_TraceScript(trc, fp->script); |
js_TraceScript(trc, fp->script); |
2807 |
if (fp->regs) { |
|
2808 |
|
/* fp->slots is null for watch pseudo-frames, see js_watch_set. */ |
2809 |
|
if (fp->slots) { |
2810 |
/* |
/* |
2811 |
* Don't mark what has not been pushed yet, or what has been |
* Don't mark what has not been pushed yet, or what has been |
2812 |
* popped already. |
* popped already. |
2813 |
*/ |
*/ |
2814 |
nslots = (uintN) (fp->regs->sp - fp->slots); |
if (fp->regs && fp->regs->sp) { |
2815 |
|
nslots = (uintN) (fp->regs->sp - fp->slots); |
2816 |
|
JS_ASSERT(nslots >= fp->script->nfixed); |
2817 |
|
} else { |
2818 |
|
nslots = fp->script->nfixed; |
2819 |
|
} |
2820 |
TRACE_JSVALS(trc, nslots, fp->slots, "slot"); |
TRACE_JSVALS(trc, nslots, fp->slots, "slot"); |
2821 |
} |
} |
2822 |
} else { |
} else { |
2829 |
(fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags))); |
(fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags))); |
2830 |
JS_CALL_VALUE_TRACER(trc, (jsval)fp->thisp, "this"); |
JS_CALL_VALUE_TRACER(trc, (jsval)fp->thisp, "this"); |
2831 |
|
|
|
if (fp->callee) |
|
|
JS_CALL_OBJECT_TRACER(trc, fp->callee, "callee"); |
|
|
|
|
2832 |
if (fp->argv) { |
if (fp->argv) { |
2833 |
|
JS_CALL_VALUE_TRACER(trc, fp->argv[-2], "callee"); |
2834 |
nslots = fp->argc; |
nslots = fp->argc; |
2835 |
skip = 0; |
skip = 0; |
2836 |
if (fp->fun) { |
if (fp->fun) { |
2852 |
JS_CALL_OBJECT_TRACER(trc, fp->scopeChain, "scope chain"); |
JS_CALL_OBJECT_TRACER(trc, fp->scopeChain, "scope chain"); |
2853 |
if (fp->sharpArray) |
if (fp->sharpArray) |
2854 |
JS_CALL_OBJECT_TRACER(trc, fp->sharpArray, "sharp array"); |
JS_CALL_OBJECT_TRACER(trc, fp->sharpArray, "sharp array"); |
|
|
|
|
if (fp->xmlNamespace) |
|
|
JS_CALL_OBJECT_TRACER(trc, fp->xmlNamespace, "xmlNamespace"); |
|
2855 |
} |
} |
2856 |
|
|
2857 |
static void |
static void |
2889 |
js_CallValueTracerIfGCThing(trc, wr->lastInternalResult); |
js_CallValueTracerIfGCThing(trc, wr->lastInternalResult); |
2890 |
} |
} |
2891 |
|
|
2892 |
JS_FRIEND_API(void) |
JS_REQUIRES_STACK JS_FRIEND_API(void) |
2893 |
js_TraceContext(JSTracer *trc, JSContext *acx) |
js_TraceContext(JSTracer *trc, JSContext *acx) |
2894 |
{ |
{ |
2895 |
JSStackFrame *fp, *nextChain; |
JSStackFrame *fp, *nextChain; |
2911 |
} \ |
} \ |
2912 |
JS_END_MACRO |
JS_END_MACRO |
2913 |
|
|
|
#ifdef JS_THREADSAFE |
|
|
js_RevokeGCLocalFreeLists(acx); |
|
|
#endif |
|
|
|
|
2914 |
/* |
/* |
2915 |
* Release the stackPool's arenas if the stackPool has existed for |
* Release the stackPool's arenas if the stackPool has existed for |
2916 |
* longer than the limit specified by gcEmptyArenaPoolLifespan. |
* longer than the limit specified by gcEmptyArenaPoolLifespan. |
2933 |
* Iterate frame chain and dormant chains. |
* Iterate frame chain and dormant chains. |
2934 |
* |
* |
2935 |
* (NB: see comment on this whole "dormant" thing in js_Execute.) |
* (NB: see comment on this whole "dormant" thing in js_Execute.) |
2936 |
|
* |
2937 |
|
* Since js_GetTopStackFrame needs to dereference cx->thread to check for |
2938 |
|
* JIT frames, we check for non-null thread here and avoid null checks |
2939 |
|
* there. See bug 471197. |
2940 |
*/ |
*/ |
2941 |
fp = acx->fp; |
#ifdef JS_THREADSAFE |
2942 |
nextChain = acx->dormantFrameChain; |
if (acx->thread) |
2943 |
if (!fp) |
#endif |
2944 |
goto next_chain; |
{ |
2945 |
|
fp = js_GetTopStackFrame(acx); |
2946 |
|
nextChain = acx->dormantFrameChain; |
2947 |
|
if (!fp) |
2948 |
|
goto next_chain; |
2949 |
|
|
2950 |
/* The top frame must not be dormant. */ |
/* The top frame must not be dormant. */ |
2951 |
JS_ASSERT(!fp->dormantNext); |
JS_ASSERT(!fp->dormantNext); |
2952 |
for (;;) { |
for (;;) { |
2953 |
do { |
do { |
2954 |
js_TraceStackFrame(trc, fp); |
js_TraceStackFrame(trc, fp); |
2955 |
} while ((fp = fp->down) != NULL); |
} while ((fp = fp->down) != NULL); |
2956 |
|
|
2957 |
next_chain: |
next_chain: |
2958 |
if (!nextChain) |
if (!nextChain) |
2959 |
break; |
break; |
2960 |
fp = nextChain; |
fp = nextChain; |
2961 |
nextChain = nextChain->dormantNext; |
nextChain = nextChain->dormantNext; |
2962 |
|
} |
2963 |
} |
} |
2964 |
|
|
2965 |
/* Mark other roots-by-definition in acx. */ |
/* Mark other roots-by-definition in acx. */ |
2966 |
if (acx->globalObject) |
if (acx->globalObject && !JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL)) |
2967 |
JS_CALL_OBJECT_TRACER(trc, acx->globalObject, "global object"); |
JS_CALL_OBJECT_TRACER(trc, acx->globalObject, "global object"); |
2968 |
TraceWeakRoots(trc, &acx->weakRoots); |
TraceWeakRoots(trc, &acx->weakRoots); |
2969 |
if (acx->throwing) { |
if (acx->throwing) { |
2996 |
tvr->u.trace(trc, tvr); |
tvr->u.trace(trc, tvr); |
2997 |
break; |
break; |
2998 |
case JSTVU_SPROP: |
case JSTVU_SPROP: |
2999 |
TRACE_SCOPE_PROPERTY(trc, tvr->u.sprop); |
tvr->u.sprop->trace(trc); |
3000 |
break; |
break; |
3001 |
case JSTVU_WEAK_ROOTS: |
case JSTVU_WEAK_ROOTS: |
3002 |
TraceWeakRoots(trc, tvr->u.weakRoots); |
TraceWeakRoots(trc, tvr->u.weakRoots); |
3003 |
break; |
break; |
3004 |
case JSTVU_PARSE_CONTEXT: |
case JSTVU_COMPILER: |
3005 |
js_TraceParseContext(trc, tvr->u.parseContext); |
tvr->u.compiler->trace(trc); |
3006 |
break; |
break; |
3007 |
case JSTVU_SCRIPT: |
case JSTVU_SCRIPT: |
3008 |
js_TraceScript(trc, tvr->u.script); |
js_TraceScript(trc, tvr->u.script); |
3009 |
break; |
break; |
3010 |
|
case JSTVU_ENUMERATOR: |
3011 |
|
static_cast<JSAutoEnumStateRooter *>(tvr)->mark(trc); |
3012 |
|
break; |
3013 |
default: |
default: |
3014 |
JS_ASSERT(tvr->count >= 0); |
JS_ASSERT(tvr->count >= 0); |
3015 |
TRACE_JSVALS(trc, tvr->count, tvr->u.array, "tvr->u.array"); |
TRACE_JSVALS(trc, tvr->count, tvr->u.array, "tvr->u.array"); |
3018 |
|
|
3019 |
if (acx->sharpObjectMap.depth > 0) |
if (acx->sharpObjectMap.depth > 0) |
3020 |
js_TraceSharpMap(trc, &acx->sharpObjectMap); |
js_TraceSharpMap(trc, &acx->sharpObjectMap); |
3021 |
|
|
3022 |
|
js_TraceRegExpStatics(trc, acx); |
3023 |
|
|
3024 |
|
#ifdef JS_TRACER |
3025 |
|
InterpState* state = acx->interpState; |
3026 |
|
while (state) { |
3027 |
|
if (state->nativeVp) |
3028 |
|
TRACE_JSVALS(trc, state->nativeVpLen, state->nativeVp, "nativeVp"); |
3029 |
|
state = state->prev; |
3030 |
|
} |
3031 |
|
#endif |
3032 |
} |
} |
3033 |
|
|
3034 |
void |
#ifdef JS_TRACER |
3035 |
js_TraceTraceMonitor(JSTracer *trc, JSTraceMonitor *tm) |
|
3036 |
|
static void |
3037 |
|
MarkReservedGCThings(JSTraceMonitor *tm) |
3038 |
{ |
{ |
3039 |
if (IS_GC_MARKING_TRACER(trc)) { |
/* Keep reserved doubles. */ |
3040 |
tm->recoveryDoublePoolPtr = tm->recoveryDoublePool; |
for (jsval *ptr = tm->reservedDoublePool; ptr < tm->reservedDoublePoolPtr; ++ptr) { |
3041 |
/* Make sure the global shape changes and will force a flush |
jsdouble* dp = JSVAL_TO_DOUBLE(*ptr); |
3042 |
of the code cache. */ |
JS_ASSERT(js_GetGCThingTraceKind(dp) == JSTRACE_DOUBLE); |
3043 |
tm->globalShape = -1; |
|
3044 |
|
JSGCArenaInfo *a = THING_TO_ARENA(dp); |
3045 |
|
JS_ASSERT(!a->list); |
3046 |
|
if (!a->u.hasMarkedDoubles) { |
3047 |
|
ClearDoubleArenaFlags(a); |
3048 |
|
a->u.hasMarkedDoubles = JS_TRUE; |
3049 |
|
} |
3050 |
|
jsuint index = DOUBLE_THING_TO_INDEX(dp); |
3051 |
|
JS_SET_BIT(DOUBLE_ARENA_BITMAP(a), index); |
3052 |
|
} |
3053 |
|
/* Keep reserved objects. */ |
3054 |
|
for (JSObject *obj = tm->reservedObjects; obj; obj = JSVAL_TO_OBJECT(obj->fslots[0])) { |
3055 |
|
uint8 *flagp = GetGCThingFlags(obj); |
3056 |
|
JS_ASSERT((*flagp & GCF_TYPEMASK) == GCX_OBJECT); |
3057 |
|
JS_ASSERT(*flagp != GCF_FINAL); |
3058 |
|
*flagp |= GCF_MARK; |
3059 |
} |
} |
3060 |
} |
} |
3061 |
|
|
3062 |
void |
#ifdef JS_THREADSAFE |
3063 |
|
static JSDHashOperator |
3064 |
|
reserved_gcthings_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, |
3065 |
|
uint32, void *) |
3066 |
|
{ |
3067 |
|
JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread; |
3068 |
|
|
3069 |
|
MarkReservedGCThings(&thread->data.traceMonitor); |
3070 |
|
return JS_DHASH_NEXT; |
3071 |
|
} |
3072 |
|
#endif |
3073 |
|
|
3074 |
|
#endif |
3075 |
|
|
3076 |
|
JS_REQUIRES_STACK void |
3077 |
js_TraceRuntime(JSTracer *trc, JSBool allAtoms) |
js_TraceRuntime(JSTracer *trc, JSBool allAtoms) |
3078 |
{ |
{ |
3079 |
JSRuntime *rt = trc->context->runtime; |
JSRuntime *rt = trc->context->runtime; |
3083 |
if (rt->gcLocksHash) |
if (rt->gcLocksHash) |
3084 |
JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc); |
JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc); |
3085 |
js_TraceAtomState(trc, allAtoms); |
js_TraceAtomState(trc, allAtoms); |
|
js_TraceNativeEnumerators(trc); |
|
3086 |
js_TraceRuntimeNumberState(trc); |
js_TraceRuntimeNumberState(trc); |
3087 |
|
|
3088 |
iter = NULL; |
iter = NULL; |
3089 |
while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) |
while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) |
3090 |
js_TraceContext(trc, acx); |
js_TraceContext(trc, acx); |
3091 |
|
|
3092 |
|
js_TraceThreads(rt, trc); |
3093 |
|
|
3094 |
if (rt->gcExtraRootsTraceOp) |
if (rt->gcExtraRootsTraceOp) |
3095 |
rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData); |
rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData); |
3096 |
|
|
3097 |
|
#ifdef JS_TRACER |
3098 |
|
for (int i = 0; i < JSBUILTIN_LIMIT; i++) { |
3099 |
|
if (rt->builtinFunctions[i]) |
3100 |
|
JS_CALL_OBJECT_TRACER(trc, rt->builtinFunctions[i], "builtin function"); |
3101 |
|
} |
3102 |
|
|
3103 |
|
/* Mark reserved gcthings unless we are shutting down. */ |
3104 |
|
if (IS_GC_MARKING_TRACER(trc) && rt->state != JSRTS_LANDING) { |
3105 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
3106 |
/* Trace the loop table(s) which can contain pointers to code objects. */ |
JS_DHashTableEnumerate(&rt->threads, reserved_gcthings_marker, NULL); |
|
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { |
|
|
if (!acx->thread) |
|
|
continue; |
|
|
js_TraceTraceMonitor(trc, &acx->thread->traceMonitor); |
|
|
} |
|
3107 |
#else |
#else |
3108 |
js_TraceTraceMonitor(trc, &rt->traceMonitor); |
MarkReservedGCThings(&rt->threadData.traceMonitor); |
3109 |
|
#endif |
3110 |
|
} |
3111 |
|
|
3112 |
|
#endif |
3113 |
|
} |
3114 |
|
|
3115 |
|
void |
3116 |
|
js_TriggerGC(JSContext *cx, JSBool gcLocked) |
3117 |
|
{ |
3118 |
|
JSRuntime *rt = cx->runtime; |
3119 |
|
|
3120 |
|
#ifdef JS_THREADSAFE |
3121 |
|
JS_ASSERT(cx->requestDepth > 0); |
3122 |
#endif |
#endif |
3123 |
|
JS_ASSERT(!rt->gcRunning); |
3124 |
|
if (rt->gcIsNeeded) |
3125 |
|
return; |
3126 |
|
|
3127 |
|
/* |
3128 |
|
* Trigger the GC when it is safe to call an operation callback on any |
3129 |
|
* thread. |
3130 |
|
*/ |
3131 |
|
rt->gcIsNeeded = JS_TRUE; |
3132 |
|
js_TriggerAllOperationCallbacks(rt, gcLocked); |
3133 |
} |
} |
3134 |
|
|
3135 |
static void |
static void |
3136 |
ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr) |
ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr) |
3137 |
{ |
{ |
3138 |
JSObject *obj, *pobj; |
JSObject *obj = ssr->obj; |
3139 |
uint32 slot; |
JSObject *pobj = ssr->pobj; |
3140 |
|
uint32 slot = ssr->slot; |
|
obj = ssr->obj; |
|
|
pobj = ssr->pobj; |
|
|
slot = ssr->slot; |
|
3141 |
|
|
3142 |
while (pobj) { |
while (pobj) { |
3143 |
pobj = js_GetWrappedObject(cx, pobj); |
pobj = js_GetWrappedObject(cx, pobj); |
3144 |
if (pobj == obj) { |
if (pobj == obj) { |
3145 |
ssr->errnum = JSMSG_CYCLIC_VALUE; |
ssr->cycle = true; |
3146 |
return; |
return; |
3147 |
} |
} |
3148 |
pobj = JSVAL_TO_OBJECT(STOBJ_GET_SLOT(pobj, slot)); |
pobj = JSVAL_TO_OBJECT(STOBJ_GET_SLOT(pobj, slot)); |
3149 |
} |
} |
3150 |
|
|
3151 |
pobj = ssr->pobj; |
pobj = ssr->pobj; |
3152 |
|
if (slot == JSSLOT_PROTO) { |
3153 |
|
obj->setProto(pobj); |
3154 |
|
} else { |
3155 |
|
JS_ASSERT(slot == JSSLOT_PARENT); |
3156 |
|
obj->setParent(pobj); |
3157 |
|
} |
3158 |
|
} |
3159 |
|
|
3160 |
if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) { |
void |
3161 |
JSScope *scope, *newscope; |
js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data) |
3162 |
JSObject *oldproto; |
{ |
3163 |
|
JSScript **listp, *script; |
|
/* Check to see whether obj shares its prototype's scope. */ |
|
|
scope = OBJ_SCOPE(obj); |
|
|
oldproto = STOBJ_GET_PROTO(obj); |
|
|
if (oldproto && OBJ_SCOPE(oldproto) == scope) { |
|
|
/* Either obj needs a new empty scope, or it should share pobj's. */ |
|
|
if (!pobj || |
|
|
!OBJ_IS_NATIVE(pobj) || |
|
|
OBJ_GET_CLASS(cx, pobj) != STOBJ_GET_CLASS(oldproto)) { |
|
|
/* |
|
|
* With no proto and no scope of its own, obj is truly empty. |
|
|
* |
|
|
* If pobj is not native, obj needs its own empty scope -- it |
|
|
* should not continue to share oldproto's scope once oldproto |
|
|
* is not on obj's prototype chain. That would put properties |
|
|
* from oldproto's scope ahead of properties defined by pobj, |
|
|
* in lookup order. |
|
|
* |
|
|
* If pobj's class differs from oldproto's, we may need a new |
|
|
* scope to handle differences in private and reserved slots, |
|
|
* so we suboptimally but safely make one. |
|
|
*/ |
|
|
if (!js_GetMutableScope(cx, obj)) { |
|
|
ssr->errnum = JSMSG_OUT_OF_MEMORY; |
|
|
return; |
|
|
} |
|
|
} else if (OBJ_SCOPE(pobj) != scope) { |
|
|
newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map); |
|
|
obj->map = &newscope->map; |
|
|
js_DropObjectMap(cx, &scope->map, obj); |
|
|
JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); |
|
|
} |
|
|
} |
|
3164 |
|
|
3165 |
/* |
for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) { |
3166 |
* Regenerate property cache shape ids for all of the scopes along the |
listp = &data->scriptsToGC[i]; |
3167 |
* old prototype chain, in case any property cache entries were filled |
while ((script = *listp) != NULL) { |
3168 |
* by looking up starting from obj. |
*listp = script->u.nextToGC; |
3169 |
*/ |
script->u.nextToGC = NULL; |
3170 |
while (oldproto && OBJ_IS_NATIVE(oldproto)) { |
js_DestroyScript(cx, script); |
|
scope = OBJ_SCOPE(oldproto); |
|
|
SCOPE_MAKE_UNIQUE_SHAPE(cx, scope); |
|
|
oldproto = STOBJ_GET_PROTO(scope->object); |
|
3171 |
} |
} |
3172 |
} |
} |
|
|
|
|
/* Finally, do the deed. */ |
|
|
STOBJ_SET_SLOT(obj, slot, OBJECT_TO_JSVAL(pobj)); |
|
3173 |
} |
} |
3174 |
|
|
3175 |
static void |
static void |
3176 |
DestroyScriptsToGC(JSContext *cx, JSScript **listp) |
FinalizeObject(JSContext *cx, JSObject *obj) |
3177 |
|
{ |
3178 |
|
/* Cope with stillborn objects that have no map. */ |
3179 |
|
if (!obj->map) |
3180 |
|
return; |
3181 |
|
|
3182 |
|
if (JS_UNLIKELY(cx->debugHooks->objectHook != NULL)) { |
3183 |
|
cx->debugHooks->objectHook(cx, obj, JS_FALSE, |
3184 |
|
cx->debugHooks->objectHookData); |
3185 |
|
} |
3186 |
|
|
3187 |
|
/* Finalize obj first, in case it needs map and slots. */ |
3188 |
|
JSClass *clasp = STOBJ_GET_CLASS(obj); |
3189 |
|
if (clasp->finalize) |
3190 |
|
clasp->finalize(cx, obj); |
3191 |
|
|
3192 |
|
#ifdef INCLUDE_MOZILLA_DTRACE |
3193 |
|
if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED()) |
3194 |
|
jsdtrace_object_finalize(obj); |
3195 |
|
#endif |
3196 |
|
|
3197 |
|
if (JS_LIKELY(OBJ_IS_NATIVE(obj))) |
3198 |
|
OBJ_SCOPE(obj)->drop(cx, obj); |
3199 |
|
js_FreeSlots(cx, obj); |
3200 |
|
} |
3201 |
|
|
3202 |
|
static JSStringFinalizeOp str_finalizers[GCX_NTYPES - GCX_EXTERNAL_STRING] = { |
3203 |
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL |
3204 |
|
}; |
3205 |
|
|
3206 |
|
intN |
3207 |
|
js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, |
3208 |
|
JSStringFinalizeOp newop) |
3209 |
{ |
{ |
3210 |
JSScript *script; |
uintN i; |
3211 |
|
|
3212 |
|
for (i = 0; i != JS_ARRAY_LENGTH(str_finalizers); i++) { |
3213 |
|
if (str_finalizers[i] == oldop) { |
3214 |
|
str_finalizers[i] = newop; |
3215 |
|
return (intN) i; |
3216 |
|
} |
3217 |
|
} |
3218 |
|
return -1; |
3219 |
|
} |
3220 |
|
|
3221 |
while ((script = *listp) != NULL) { |
/* |
3222 |
*listp = script->u.nextToGC; |
* cx is NULL when we are called from js_FinishAtomState to force the |
3223 |
script->u.nextToGC = NULL; |
* finalization of the permanently interned strings. |
3224 |
js_DestroyScript(cx, script); |
*/ |
3225 |
|
void |
3226 |
|
js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx) |
3227 |
|
{ |
3228 |
|
jschar *chars; |
3229 |
|
JSBool valid; |
3230 |
|
JSStringFinalizeOp finalizer; |
3231 |
|
|
3232 |
|
JS_RUNTIME_UNMETER(rt, liveStrings); |
3233 |
|
JS_ASSERT(!JSString::isStatic(str)); |
3234 |
|
if (str->isDependent()) { |
3235 |
|
/* A dependent string can not be external and must be valid. */ |
3236 |
|
JS_ASSERT(type < 0); |
3237 |
|
JS_ASSERT(str->dependentBase()); |
3238 |
|
JS_RUNTIME_UNMETER(rt, liveDependentStrings); |
3239 |
|
valid = JS_TRUE; |
3240 |
|
} else { |
3241 |
|
/* A stillborn string has null chars, so is not valid. */ |
3242 |
|
chars = str->flatChars(); |
3243 |
|
valid = (chars != NULL); |
3244 |
|
if (valid) { |
3245 |
|
if (type < 0) { |
3246 |
|
if (cx) |
3247 |
|
cx->free(chars); |
3248 |
|
else |
3249 |
|
rt->free(chars); |
3250 |
|
} else { |
3251 |
|
JS_ASSERT((uintN) type < JS_ARRAY_LENGTH(str_finalizers)); |
3252 |
|
finalizer = str_finalizers[type]; |
3253 |
|
if (finalizer) { |
3254 |
|
/* |
3255 |
|
* Assume that the finalizer for the permanently interned |
3256 |
|
* string knows how to deal with null context. |
3257 |
|
*/ |
3258 |
|
finalizer(cx, str); |
3259 |
|
} |
3260 |
|
} |
3261 |
|
} |
3262 |
} |
} |
3263 |
|
if (valid && str->isDeflated()) |
3264 |
|
js_PurgeDeflatedStringCache(rt, str); |
3265 |
} |
} |
3266 |
|
|
3267 |
/* |
/* |
3284 |
JSBool allClear; |
JSBool allClear; |
3285 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
3286 |
uint32 requestDebit; |
uint32 requestDebit; |
|
JSContext *acx, *iter; |
|
3287 |
#endif |
#endif |
3288 |
#ifdef JS_GCMETER |
#ifdef JS_GCMETER |
3289 |
uint32 nlivearenas, nkilledarenas, nthings; |
uint32 nlivearenas, nkilledarenas, nthings; |
3291 |
|
|
3292 |
JS_ASSERT_IF(gckind == GC_LAST_DITCH, !JS_ON_TRACE(cx)); |
JS_ASSERT_IF(gckind == GC_LAST_DITCH, !JS_ON_TRACE(cx)); |
3293 |
rt = cx->runtime; |
rt = cx->runtime; |
3294 |
|
|
3295 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
3296 |
|
/* |
3297 |
|
* We allow js_GC calls outside a request but the context must be bound |
3298 |
|
* to the current thread. |
3299 |
|
*/ |
3300 |
|
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); |
3301 |
|
|
3302 |
/* Avoid deadlock. */ |
/* Avoid deadlock. */ |
3303 |
JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); |
JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); |
3304 |
#endif |
#endif |
3359 |
rt->gcPoke = JS_FALSE; |
rt->gcPoke = JS_FALSE; |
3360 |
|
|
3361 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
3362 |
JS_ASSERT(cx->thread->id == js_CurrentThreadId()); |
/* |
3363 |
|
* Check if the GC is already running on this or another thread and |
3364 |
|
* delegate the job to it. |
3365 |
|
*/ |
3366 |
|
if (rt->gcLevel > 0) { |
3367 |
|
JS_ASSERT(rt->gcThread); |
3368 |
|
|
3369 |
/* Bump gcLevel and return rather than nest on this thread. */ |
/* Bump gcLevel to restart the current GC, so it finds new garbage. */ |
|
if (rt->gcThread == cx->thread) { |
|
|
JS_ASSERT(rt->gcLevel > 0); |
|
3370 |
rt->gcLevel++; |
rt->gcLevel++; |
3371 |
METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel); |
METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel); |
|
if (!(gckind & GC_LOCK_HELD)) |
|
|
JS_UNLOCK_GC(rt); |
|
|
return; |
|
|
} |
|
|
|
|
|
/* |
|
|
* If we're in one or more requests (possibly on more than one context) |
|
|
* running on the current thread, indicate, temporarily, that all these |
|
|
* requests are inactive. If cx->thread is NULL, then cx is not using |
|
|
* the request model, and does not contribute to rt->requestCount. |
|
|
*/ |
|
|
requestDebit = 0; |
|
|
if (cx->thread) { |
|
|
JSCList *head, *link; |
|
3372 |
|
|
3373 |
/* |
/* |
3374 |
* Check all contexts on cx->thread->contextList for active requests, |
* If the GC runs on another thread, temporarily suspend the current |
3375 |
* counting each such context against requestDebit. |
* request and wait until the GC is done. |
3376 |
*/ |
*/ |
3377 |
head = &cx->thread->contextList; |
if (rt->gcThread != cx->thread) { |
3378 |
for (link = head->next; link != head; link = link->next) { |
requestDebit = js_DiscountRequestsForGC(cx); |
3379 |
acx = CX_FROM_THREAD_LINKS(link); |
js_RecountRequestsAfterGC(rt, requestDebit); |
|
JS_ASSERT(acx->thread == cx->thread); |
|
|
if (acx->requestDepth) |
|
|
requestDebit++; |
|
3380 |
} |
} |
|
} else { |
|
|
/* |
|
|
* We assert, but check anyway, in case someone is misusing the API. |
|
|
* Avoiding the loop over all of rt's contexts is a win in the event |
|
|
* that the GC runs only on request-less contexts with null threads, |
|
|
* in a special thread such as might be used by the UI/DOM/Layout |
|
|
* "mozilla" or "main" thread in Mozilla-the-browser. |
|
|
*/ |
|
|
JS_ASSERT(cx->requestDepth == 0); |
|
|
if (cx->requestDepth) |
|
|
requestDebit = 1; |
|
|
} |
|
|
if (requestDebit) { |
|
|
JS_ASSERT(requestDebit <= rt->requestCount); |
|
|
rt->requestCount -= requestDebit; |
|
|
if (rt->requestCount == 0) |
|
|
JS_NOTIFY_REQUEST_DONE(rt); |
|
|
} |
|
|
|
|
|
/* If another thread is already in GC, don't attempt GC; wait instead. */ |
|
|
if (rt->gcLevel > 0) { |
|
|
/* Bump gcLevel to restart the current GC, so it finds new garbage. */ |
|
|
rt->gcLevel++; |
|
|
METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel); |
|
|
|
|
|
/* Wait for the other thread to finish, then resume our request. */ |
|
|
while (rt->gcLevel > 0) |
|
|
JS_AWAIT_GC_DONE(rt); |
|
|
if (requestDebit) |
|
|
rt->requestCount += requestDebit; |
|
3381 |
if (!(gckind & GC_LOCK_HELD)) |
if (!(gckind & GC_LOCK_HELD)) |
3382 |
JS_UNLOCK_GC(rt); |
JS_UNLOCK_GC(rt); |
3383 |
return; |
return; |
3387 |
rt->gcLevel = 1; |
rt->gcLevel = 1; |
3388 |
rt->gcThread = cx->thread; |
rt->gcThread = cx->thread; |
3389 |
|
|
3390 |
/* Wait for all other requests to finish. */ |
/* |
3391 |
|
* Notify all operation callbacks, which will give them a chance to |
3392 |
|
* yield their current request. Contexts that are not currently |
3393 |
|
* executing will perform their callback at some later point, |
3394 |
|
* which then will be unnecessary, but harmless. |
3395 |
|
*/ |
3396 |
|
js_NudgeOtherContexts(cx); |
3397 |
|
|
3398 |
|
/* |
3399 |
|
* Discount all the requests on the current thread from contributing |
3400 |
|
* to rt->requestCount before we wait for all other requests to finish. |
3401 |
|
* JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on |
3402 |
|
* rt->requestCount transitions to 0. |
3403 |
|
*/ |
3404 |
|
requestDebit = js_CountThreadRequests(cx); |
3405 |
|
JS_ASSERT_IF(cx->requestDepth != 0, requestDebit >= 1); |
3406 |
|
rt->requestCount -= requestDebit; |
3407 |
while (rt->requestCount > 0) |
while (rt->requestCount > 0) |
3408 |
JS_AWAIT_REQUEST_DONE(rt); |
JS_AWAIT_REQUEST_DONE(rt); |
3409 |
|
rt->requestCount += requestDebit; |
3410 |
|
|
3411 |
#else /* !JS_THREADSAFE */ |
#else /* !JS_THREADSAFE */ |
3412 |
|
|
3446 |
* collect garbage only if a racing thread attempted GC and is waiting |
* collect garbage only if a racing thread attempted GC and is waiting |
3447 |
* for us to finish (gcLevel > 1) or if someone already poked us. |
* for us to finish (gcLevel > 1) or if someone already poked us. |
3448 |
*/ |
*/ |
3449 |
if (rt->gcLevel == 1 && !rt->gcPoke) |
if (rt->gcLevel == 1 && !rt->gcPoke && !rt->gcIsNeeded) |
3450 |
goto done_running; |
goto done_running; |
3451 |
|
|
3452 |
rt->gcLevel = 0; |
rt->gcLevel = 0; |
3454 |
rt->gcRunning = JS_FALSE; |
rt->gcRunning = JS_FALSE; |
3455 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
3456 |
rt->gcThread = NULL; |
rt->gcThread = NULL; |
|
rt->requestCount += requestDebit; |
|
3457 |
#endif |
#endif |
3458 |
gckind = GC_LOCK_HELD; |
gckind = GC_LOCK_HELD; |
3459 |
goto restart_at_beginning; |
goto restart_at_beginning; |
3465 |
if (JS_ON_TRACE(cx)) |
if (JS_ON_TRACE(cx)) |
3466 |
goto out; |
goto out; |
3467 |
#endif |
#endif |
3468 |
|
VOUCH_HAVE_STACK(); |
3469 |
|
|
3470 |
|
/* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */ |
3471 |
|
rt->gcIsNeeded = JS_FALSE; |
3472 |
|
|
3473 |
/* Reset malloc counter. */ |
/* Reset malloc counter. */ |
3474 |
rt->gcMallocBytes = 0; |
rt->gcMallocBytes = 0; |
3479 |
} |
} |
3480 |
#endif |
#endif |
3481 |
|
|
|
/* Clear property and JIT oracle caches (only for cx->thread if JS_THREADSAFE). */ |
|
|
js_FlushPropertyCache(cx); |
|
3482 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
3483 |
js_FlushJITOracle(cx); |
js_PurgeJITOracle(); |
3484 |
#endif |
#endif |
3485 |
|
|
3486 |
/* Destroy eval'ed scripts. */ |
restart: |
3487 |
DestroyScriptsToGC(cx, &JS_SCRIPTS_TO_GC(cx)); |
rt->gcNumber++; |
3488 |
|
JS_ASSERT(!rt->gcUntracedArenaStackTop); |
3489 |
|
JS_ASSERT(rt->gcTraceLaterCount == 0); |
3490 |
|
|
|
#ifdef JS_THREADSAFE |
|
3491 |
/* |
/* |
3492 |
* Clear thread-based caches. To avoid redundant clearing we unroll the |
* Reset the property cache's type id generator so we can compress ids. |
3493 |
* current thread's step. |
* Same for the protoHazardShape proxy-shape standing in for all object |
3494 |
* |
* prototypes having readonly or setter properties. |
|
* In case a JSScript wrapped within an object was finalized, we null |
|
|
* acx->thread->gsnCache.script and finish the cache's hashtable. Note |
|
|
* that js_DestroyScript, called from script_finalize, will have already |
|
|
* cleared cx->thread->gsnCache above during finalization, so we don't |
|
|
* have to here. |
|
3495 |
*/ |
*/ |
3496 |
iter = NULL; |
if (rt->shapeGen & SHAPE_OVERFLOW_BIT |
3497 |
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { |
#ifdef JS_GC_ZEAL |
3498 |
if (!acx->thread || acx->thread == cx->thread) |
|| rt->gcZeal >= 1 |
|
continue; |
|
|
GSN_CACHE_CLEAR(&acx->thread->gsnCache); |
|
|
js_FlushPropertyCache(acx); |
|
|
#ifdef JS_TRACER |
|
|
js_FlushJITOracle(acx); |
|
3499 |
#endif |
#endif |
3500 |
DestroyScriptsToGC(cx, &acx->thread->scriptsToGC); |
) { |
3501 |
|
rt->gcRegenShapes = true; |
3502 |
|
rt->gcRegenShapesScopeFlag ^= JSScope::SHAPE_REGEN; |
3503 |
|
rt->shapeGen = 0; |
3504 |
|
rt->protoHazardShape = 0; |
3505 |
} |
} |
|
#else |
|
|
/* The thread-unsafe case just has to clear the runtime's GSN cache. */ |
|
|
GSN_CACHE_CLEAR(&rt->gsnCache); |
|
|
#endif |
|
|
|
|
|
restart: |
|
|
rt->gcNumber++; |
|
|
JS_ASSERT(!rt->gcUntracedArenaStackTop); |
|
|
JS_ASSERT(rt->gcTraceLaterCount == 0); |
|
3506 |
|
|
3507 |
/* Reset the property cache's type id generator so we can compress ids. */ |
js_PurgeThreads(cx); |
3508 |
rt->shapeGen = 0; |
#ifdef JS_TRACER |
3509 |
|
if (gckind == GC_LAST_CONTEXT) { |
3510 |
|
/* Clear builtin functions, which are recreated on demand. */ |
3511 |
|
memset(rt->builtinFunctions, 0, sizeof rt->builtinFunctions); |
3512 |
|
} |
3513 |
|
#endif |
3514 |
|
|
3515 |
/* |
/* |
3516 |
* Mark phase. |
* Mark phase. |
3542 |
|
|
3543 |
rt->gcMarkingTracer = NULL; |
rt->gcMarkingTracer = NULL; |
3544 |
|
|
3545 |
|
#ifdef JS_THREADSAFE |
3546 |
|
cx->createDeallocatorTask(); |
3547 |
|
#endif |
3548 |
|
|
3549 |
/* |
/* |
3550 |
* Sweep phase. |
* Sweep phase. |
3551 |
* |
* |
3619 |
type = flags & GCF_TYPEMASK; |
type = flags & GCF_TYPEMASK; |
3620 |
switch (type) { |
switch (type) { |
3621 |
case GCX_OBJECT: |
case GCX_OBJECT: |
3622 |
js_FinalizeObject(cx, (JSObject *) thing); |
FinalizeObject(cx, (JSObject *) thing); |
|
break; |
|
|
case GCX_DOUBLE: |
|
|
/* Do nothing. */ |
|
3623 |
break; |
break; |
3624 |
#if JS_HAS_XML_SUPPORT |
#if JS_HAS_XML_SUPPORT |
3625 |
case GCX_XML: |
case GCX_XML: |
3653 |
*/ |
*/ |
3654 |
freeList = arenaList->freeList; |
freeList = arenaList->freeList; |
3655 |
if (a == arenaList->last) |
if (a == arenaList->last) |
3656 |
arenaList->lastCount = (uint16) indexLimit; |
arenaList->lastCount = indexLimit; |
3657 |
*ap = a->prev; |
*ap = a->prev; |
3658 |
a->prev = emptyArenas; |
a->prev = emptyArenas; |
3659 |
emptyArenas = a; |
emptyArenas = a; |
3677 |
nlivearenas, nkilledarenas, nthings)); |
nlivearenas, nkilledarenas, nthings)); |
3678 |
} |
} |
3679 |
|
|
|
#ifdef JS_THREADSAFE |
|
|
/* |
|
|
* Release all but two free list sets to avoid allocating a new set in |
|
|
* js_NewGCThing. |
|
|
*/ |
|
|
TrimGCFreeListsPool(rt, 2); |
|
|
#endif |
|
|
|
|
3680 |
ap = &rt->gcDoubleArenaList.first; |
ap = &rt->gcDoubleArenaList.first; |
3681 |
METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0)); |
METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0)); |
3682 |
while ((a = *ap) != NULL) { |
while ((a = *ap) != NULL) { |
3724 |
*/ |
*/ |
3725 |
DestroyGCArenas(rt, emptyArenas); |
DestroyGCArenas(rt, emptyArenas); |
3726 |
|
|
3727 |
|
#ifdef JS_THREADSAFE |
3728 |
|
cx->submitDeallocatorTask(); |
3729 |
|
#endif |
3730 |
|
|
3731 |
if (rt->gcCallback) |
if (rt->gcCallback) |
3732 |
(void) rt->gcCallback(cx, JSGC_FINALIZE_END); |
(void) rt->gcCallback(cx, JSGC_FINALIZE_END); |
3733 |
#ifdef DEBUG_srcnotesize |
#ifdef DEBUG_srcnotesize |
3774 |
* We want to restart GC if js_GC was called recursively or if any of the |
* We want to restart GC if js_GC was called recursively or if any of the |
3775 |
* finalizers called js_RemoveRoot or js_UnlockGCThingRT. |
* finalizers called js_RemoveRoot or js_UnlockGCThingRT. |
3776 |
*/ |
*/ |
3777 |
if (rt->gcLevel > 1 || rt->gcPoke) { |
if (!JS_ON_TRACE(cx) && (rt->gcLevel > 1 || rt->gcPoke)) { |
3778 |
|
VOUCH_HAVE_STACK(); |
3779 |
rt->gcLevel = 1; |
rt->gcLevel = 1; |
3780 |
rt->gcPoke = JS_FALSE; |
rt->gcPoke = JS_FALSE; |
3781 |
JS_UNLOCK_GC(rt); |
JS_UNLOCK_GC(rt); |
3782 |
goto restart; |
goto restart; |
3783 |
} |
} |
3784 |
|
|
3785 |
if (rt->shapeGen >= SHAPE_OVERFLOW_BIT - 1) { |
rt->setGCLastBytes(rt->gcBytes); |
|
/* |
|
|
* FIXME bug 440834: The shape id space has overflowed. Currently we |
|
|
* cope badly with this. Every call to js_GenerateShape does GC, and |
|
|
* we never re-enable the property cache. |
|
|
*/ |
|
|
js_DisablePropertyCache(cx); |
|
|
#ifdef JS_THREADSAFE |
|
|
iter = NULL; |
|
|
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { |
|
|
if (!acx->thread || acx->thread == cx->thread) |
|
|
continue; |
|
|
js_DisablePropertyCache(acx); |
|
|
} |
|
|
#endif |
|
|
} |
|
|
|
|
|
rt->gcLastBytes = rt->gcBytes; |
|
3786 |
done_running: |
done_running: |
3787 |
rt->gcLevel = 0; |
rt->gcLevel = 0; |
3788 |
rt->gcRunning = JS_FALSE; |
rt->gcRunning = rt->gcRegenShapes = false; |
3789 |
|
|
3790 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
|
/* If we were invoked during a request, pay back the temporary debit. */ |
|
|
if (requestDebit) |
|
|
rt->requestCount += requestDebit; |
|
3791 |
rt->gcThread = NULL; |
rt->gcThread = NULL; |
3792 |
JS_NOTIFY_GC_DONE(rt); |
JS_NOTIFY_GC_DONE(rt); |
3793 |
|
|
3834 |
} |
} |
3835 |
} |
} |
3836 |
} |
} |
|
|
|
|
void |
|
|
js_UpdateMallocCounter(JSContext *cx, size_t nbytes) |
|
|
{ |
|
|
uint32 *pbytes, bytes; |
|
|
|
|
|
#ifdef JS_THREADSAFE |
|
|
pbytes = &cx->thread->gcMallocBytes; |
|
|
#else |
|
|
pbytes = &cx->runtime->gcMallocBytes; |
|
|
#endif |
|
|
bytes = *pbytes; |
|
|
*pbytes = ((uint32)-1 - bytes <= nbytes) ? (uint32)-1 : bytes + nbytes; |
|
|
} |
|