зеркало из https://github.com/mozilla/pjs.git
Bug 341896: GC finalizes the states of native iterators before finalizing the rest of object. It avoids using expensive close hooks. r=brendan
This commit is contained in:
Родитель
2e17692115
Коммит
83062e5eb3
|
@ -147,6 +147,14 @@ extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i);
|
|||
JS_END_MACRO
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Internal function.
|
||||
* Compute the log of the least power of 2 greater than or equal to n.
|
||||
* This is a version of JS_CeilingLog2 that operates on jsuword with
|
||||
* CPU-dependant size.
|
||||
*/
|
||||
#define JS_CEILING_LOG2W(n) ((n) <= 1 ? 0 : 1 + JS_FLOOR_LOG2W((n) - 1))
|
||||
|
||||
/*
|
||||
* Internal function.
|
||||
* Compute the log of the greatest power of 2 less than or equal to n.
|
||||
|
|
|
@ -172,7 +172,13 @@ struct JSRuntime {
|
|||
* Table for tracking objects of extended classes that have non-null close
|
||||
* hooks, and need the GC to perform two-phase finalization.
|
||||
*/
|
||||
JSGCCloseTable gcCloseTable;
|
||||
JSPtrTable gcCloseTable;
|
||||
|
||||
/*
|
||||
* Table for tracking iterators to ensure that we close iterator's state
|
||||
* before finalizing the iterable object.
|
||||
*/
|
||||
JSPtrTable gcIteratorTable;
|
||||
|
||||
#ifdef JS_GCMETER
|
||||
JSGCStats gcStats;
|
||||
|
|
283
js/src/jsgc.c
283
js/src/jsgc.c
|
@ -64,6 +64,7 @@
|
|||
#include "jsfun.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsinterp.h"
|
||||
#include "jsiter.h"
|
||||
#include "jslock.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsobj.h"
|
||||
|
@ -237,6 +238,149 @@ JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble));
|
|||
JS_STATIC_ASSERT(GC_FLAGS_SIZE >= GC_PAGE_SIZE);
|
||||
JS_STATIC_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval));
|
||||
|
||||
/*
|
||||
* JSPtrTable capacity growth descriptor. The table grows by powers of two
|
||||
* starting from capacity JSPtrTableInfo.minCapacity, but switching to linear
|
||||
* growth when capacity reaches JSPtrTableInfo.linearGrowthThreshold.
|
||||
*/
|
||||
typedef struct JSPtrTableInfo {
|
||||
uint16 minCapacity;
|
||||
uint16 linearGrowthThreshold;
|
||||
} JSPtrTableInfo;
|
||||
|
||||
#define GC_CLOSE_TABLE_MIN 4
|
||||
#define GC_CLOSE_TABLE_LINEAR 1024
|
||||
#define GC_ITERATOR_TABLE_MIN 4
|
||||
#define GC_ITERATOR_TABLE_LINEAR 1024
|
||||
|
||||
static const JSPtrTableInfo closeTableInfo = {
|
||||
GC_CLOSE_TABLE_MIN,
|
||||
GC_CLOSE_TABLE_LINEAR
|
||||
};
|
||||
|
||||
static const JSPtrTableInfo iteratorTableInfo = {
|
||||
GC_ITERATOR_TABLE_MIN,
|
||||
GC_ITERATOR_TABLE_LINEAR
|
||||
};
|
||||
|
||||
/* Calculate table capacity based on the current value of JSPtrTable.count. */
|
||||
static size_t
|
||||
PtrTableCapacity(size_t count, const JSPtrTableInfo *info)
|
||||
{
|
||||
size_t linear, log, capacity;
|
||||
|
||||
linear = info->linearGrowthThreshold;
|
||||
JS_ASSERT(info->minCapacity <= linear);
|
||||
|
||||
if (count == 0) {
|
||||
capacity = 0;
|
||||
} else if (count < linear) {
|
||||
log = JS_CEILING_LOG2W(count);
|
||||
JS_ASSERT(log != JS_BITS_PER_WORD);
|
||||
capacity = (size_t)1 << log;
|
||||
if (capacity < info->minCapacity)
|
||||
capacity = info->minCapacity;
|
||||
} else {
|
||||
capacity = JS_ROUNDUP(count, linear);
|
||||
}
|
||||
|
||||
JS_ASSERT(capacity >= count);
|
||||
return capacity;
|
||||
}
|
||||
|
||||
static void
|
||||
FreePtrTable(JSPtrTable *table, const JSPtrTableInfo *info)
|
||||
{
|
||||
if (table->array) {
|
||||
JS_ASSERT(table->count > 0);
|
||||
free(table->array);
|
||||
table->array = NULL;
|
||||
table->count = 0;
|
||||
}
|
||||
JS_ASSERT(table->count == 0);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
AddToPtrTable(JSContext *cx, JSPtrTable *table, const JSPtrTableInfo *info,
|
||||
void *ptr)
|
||||
{
|
||||
size_t count, capacity;
|
||||
void **array;
|
||||
|
||||
count = table->count;
|
||||
capacity = PtrTableCapacity(count, info);
|
||||
|
||||
if (count == capacity) {
|
||||
if (capacity < info->minCapacity) {
|
||||
JS_ASSERT(capacity == 0);
|
||||
JS_ASSERT(!table->array);
|
||||
capacity = info->minCapacity;
|
||||
} else {
|
||||
/*
|
||||
* Simplify the overflow detection assuming pointer is bigger
|
||||
* than byte.
|
||||
*/
|
||||
JS_STATIC_ASSERT(2 <= sizeof table->array[0]);
|
||||
capacity = (capacity < info->linearGrowthThreshold)
|
||||
? 2 * capacity
|
||||
: capacity + info->linearGrowthThreshold;
|
||||
if (capacity > (size_t)-1 / sizeof table->array[0])
|
||||
goto bad;
|
||||
}
|
||||
array = (void **) realloc(table->array,
|
||||
capacity * sizeof table->array[0]);
|
||||
if (!array)
|
||||
goto bad;
|
||||
#ifdef DEBUG
|
||||
memset(array + count, JS_FREE_PATTERN,
|
||||
(capacity - count) * sizeof table->array[0]);
|
||||
#endif
|
||||
table->array = array;
|
||||
}
|
||||
|
||||
table->array[count] = ptr;
|
||||
table->count = count + 1;
|
||||
|
||||
return JS_TRUE;
|
||||
|
||||
bad:
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
ShrinkPtrTable(JSPtrTable *table, const JSPtrTableInfo *info,
|
||||
size_t newCount)
|
||||
{
|
||||
size_t oldCapacity, capacity;
|
||||
void **array;
|
||||
|
||||
JS_ASSERT(newCount <= table->count);
|
||||
if (newCount == table->count)
|
||||
return;
|
||||
|
||||
oldCapacity = PtrTableCapacity(table->count, info);
|
||||
table->count = newCount;
|
||||
capacity = PtrTableCapacity(newCount, info);
|
||||
|
||||
if (oldCapacity != capacity) {
|
||||
array = table->array;
|
||||
JS_ASSERT(array);
|
||||
if (capacity == 0) {
|
||||
free(array);
|
||||
table->array = NULL;
|
||||
return;
|
||||
}
|
||||
array = (void **) realloc(array, capacity * sizeof array[0]);
|
||||
if (array)
|
||||
table->array = array;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(table->array + newCount, JS_FREE_PATTERN,
|
||||
(capacity - newCount) * sizeof table->array[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JS_GCMETER
|
||||
# define METER(x) x
|
||||
#else
|
||||
|
@ -343,11 +487,8 @@ FinishGCArenaLists(JSRuntime *rt)
|
|||
arenaList->freeList = NULL;
|
||||
}
|
||||
|
||||
if (rt->gcCloseTable.array) {
|
||||
free(rt->gcCloseTable.array);
|
||||
rt->gcCloseTable.array = NULL;
|
||||
rt->gcCloseTable.count = 0;
|
||||
}
|
||||
FreePtrTable(&rt->gcCloseTable, &closeTableInfo);
|
||||
FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);
|
||||
}
|
||||
|
||||
uint8 *
|
||||
|
@ -698,12 +839,49 @@ js_RemoveRoot(JSRuntime *rt, void *rp)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_RegisterCloseableIterator(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSBool ok;
|
||||
|
||||
rt = cx->runtime;
|
||||
JS_ASSERT(!rt->gcRunning || rt->gcClosePhase);
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
ok = AddToPtrTable(cx, &rt->gcIteratorTable, &iteratorTableInfo, obj);
|
||||
JS_UNLOCK_GC(rt);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void
|
||||
CloseIteratorStates(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
size_t count, newCount, i;
|
||||
void **array;
|
||||
JSObject *obj;
|
||||
|
||||
rt = cx->runtime;
|
||||
count = rt->gcIteratorTable.count;
|
||||
array = rt->gcIteratorTable.array;
|
||||
|
||||
newCount = 0;
|
||||
for (i = 0; i != count; ++i) {
|
||||
obj = (JSObject *)array[i];
|
||||
if (js_IsAboutToBeFinalized(cx, obj))
|
||||
js_CloseIteratorState(cx, obj);
|
||||
else
|
||||
array[newCount++] = obj;
|
||||
}
|
||||
ShrinkPtrTable(&rt->gcIteratorTable, &iteratorTableInfo, newCount);
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_AddObjectToCloseTable(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSObject **array;
|
||||
uint32 count, length;
|
||||
JSBool ok;
|
||||
|
||||
/*
|
||||
* Return early without doing anything if shutting down, to prevent a bad
|
||||
|
@ -725,30 +903,9 @@ js_AddObjectToCloseTable(JSContext *cx, JSObject *obj)
|
|||
}
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
array = rt->gcCloseTable.array;
|
||||
count = rt->gcCloseTable.count;
|
||||
length = GC_CLOSE_TABLE_LENGTH(count);
|
||||
|
||||
if (!array || count == length) {
|
||||
length = (length < GC_CLOSE_TABLE_LINEAR)
|
||||
? 2 * length
|
||||
: length + GC_CLOSE_TABLE_LINEAR;
|
||||
array = (JSObject **) realloc(array, length * sizeof(JSObject *));
|
||||
if (!array) {
|
||||
JS_UNLOCK_GC(rt);
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(array + count, 0, (length - count) * sizeof(JSObject *));
|
||||
#endif
|
||||
rt->gcCloseTable.array = array;
|
||||
}
|
||||
|
||||
array[count++] = obj;
|
||||
rt->gcCloseTable.count = count;
|
||||
ok = AddToPtrTable(cx, &rt->gcCloseTable, &closeTableInfo, obj);
|
||||
JS_UNLOCK_GC(rt);
|
||||
return JS_TRUE;
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -757,10 +914,10 @@ js_AddObjectToCloseTable(JSContext *cx, JSObject *obj)
|
|||
* with indexes from the [startIndex, startIndex + count) range.
|
||||
*/
|
||||
typedef struct JSObjectsToClose {
|
||||
uint32 count;
|
||||
uint32 startIndex;
|
||||
size_t count;
|
||||
size_t startIndex;
|
||||
#ifdef DEBUG
|
||||
JSGCCloseTable tableSnapshot;
|
||||
JSPtrTable tableSnapshot;
|
||||
#endif
|
||||
} JSObjectsToClose;
|
||||
|
||||
|
@ -771,8 +928,9 @@ static void
|
|||
FindAndMarkObjectsToClose(JSContext *cx, JSObjectsToClose *toClose)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSObject **array, *obj;
|
||||
uint32 count, index, pivot;
|
||||
void **array;
|
||||
JSObject *obj;
|
||||
size_t count, index, pivot;
|
||||
|
||||
/*
|
||||
* Find unmarked objects reachable from rt->gcCloseTable and set them
|
||||
|
@ -783,7 +941,7 @@ FindAndMarkObjectsToClose(JSContext *cx, JSObjectsToClose *toClose)
|
|||
count = pivot = rt->gcCloseTable.count;
|
||||
index = 0;
|
||||
while (index != pivot) {
|
||||
obj = array[index];
|
||||
obj = (JSObject *)array[index];
|
||||
if (*js_GetGCThingFlags(obj) & GCF_MARK) {
|
||||
++index;
|
||||
} else {
|
||||
|
@ -827,16 +985,16 @@ ExecuteCloseHooks(JSContext *cx, const JSObjectsToClose *toClose)
|
|||
JSRuntime *rt;
|
||||
JSStackFrame *fp;
|
||||
uint32 index, endIndex;
|
||||
JSObject *obj, **array;
|
||||
JSObject *obj;
|
||||
JSExtendedClass *xclasp;
|
||||
uint32 added, length;
|
||||
void **array;
|
||||
|
||||
rt = cx->runtime;
|
||||
JS_ASSERT(toClose->count > 0);
|
||||
|
||||
/* Close table manupulations are not allowed during the marking phase. */
|
||||
JS_ASSERT(toClose->tableSnapshot.array == rt->gcCloseTable.array);
|
||||
JS_ASSERT(toClose->tableSnapshot.count == rt->gcCloseTable.count);
|
||||
JS_ASSERT(memcmp(&toClose->tableSnapshot, &rt->gcCloseTable,
|
||||
sizeof toClose->tableSnapshot) == 0);
|
||||
|
||||
/*
|
||||
* Execute the close hooks. Temporarily set aside cx->fp here to prevent
|
||||
|
@ -853,7 +1011,7 @@ ExecuteCloseHooks(JSContext *cx, const JSObjectsToClose *toClose)
|
|||
* Reload rt->gcCloseTable.array because close hooks may create
|
||||
* new objects that have close hooks and reallocate the table.
|
||||
*/
|
||||
obj = rt->gcCloseTable.array[index];
|
||||
obj = (JSObject *)rt->gcCloseTable.array[index];
|
||||
xclasp = (JSExtendedClass *) LOCKED_OBJ_GET_CLASS(obj);
|
||||
JS_ASSERT(xclasp->base.flags & JSCLASS_IS_EXTENDED);
|
||||
xclasp->close(cx, obj);
|
||||
|
@ -867,36 +1025,10 @@ ExecuteCloseHooks(JSContext *cx, const JSObjectsToClose *toClose)
|
|||
* processed, and update the table's count.
|
||||
*/
|
||||
array = rt->gcCloseTable.array;
|
||||
added = rt->gcCloseTable.count - endIndex;
|
||||
memmove(array + toClose->startIndex, array + endIndex,
|
||||
added * sizeof array[0]);
|
||||
rt->gcCloseTable.count -= toClose->count;
|
||||
|
||||
/*
|
||||
* Check whether we need to shrink the table now. The previous
|
||||
* value, which is possibly the maximum, of the table count is
|
||||
* (endIndex + added). If as many or more objects were added during
|
||||
* the above close loop as were closed, we won't shrink.
|
||||
*
|
||||
* Note that we might not shrink even if more objects were closed
|
||||
* than were added, since we may not have closed enough to justify
|
||||
* a shrink by power of two or linear growth increment. So use
|
||||
* straightforward code here, computing current and previous
|
||||
* lengths and comparing.
|
||||
*/
|
||||
length = GC_CLOSE_TABLE_LENGTH(rt->gcCloseTable.count);
|
||||
endIndex += added;
|
||||
if (length < GC_CLOSE_TABLE_LENGTH(endIndex)) {
|
||||
JS_ASSERT(toClose->count > added);
|
||||
array = (JSObject **) realloc(array, length * sizeof array[0]);
|
||||
if (array) {
|
||||
rt->gcCloseTable.array = array;
|
||||
#ifdef DEBUG
|
||||
memset(array + rt->gcCloseTable.count, JS_FREE_PATTERN,
|
||||
(length - rt->gcCloseTable.count) * sizeof array[0]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
(rt->gcCloseTable.count - endIndex) * sizeof array[0]);
|
||||
ShrinkPtrTable(&rt->gcCloseTable, &closeTableInfo,
|
||||
rt->gcCloseTable.count - toClose->count);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_brendan) || defined(DEBUG_timeless)
|
||||
|
@ -2485,18 +2617,23 @@ restart:
|
|||
}
|
||||
JS_ASSERT(rt->gcUnscannedBagSize == 0);
|
||||
|
||||
/* Finalize iterator states before the objects they iterate over. */
|
||||
CloseIteratorStates(cx);
|
||||
|
||||
/*
|
||||
* Sweep phase.
|
||||
*
|
||||
* Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set
|
||||
* so that any attempt to allocate a GC-thing from a finalizer will fail,
|
||||
* rather than nest badly and leave the unmarked newborn to be swept.
|
||||
*
|
||||
* Finalize smaller objects before larger, to guarantee finalization of
|
||||
* GC-allocated obj->slots after obj. See FreeSlots in jsobj.c.
|
||||
*/
|
||||
js_SweepAtomState(&rt->atomState);
|
||||
js_SweepScopeProperties(rt);
|
||||
|
||||
/*
|
||||
* Finalize smaller objects before larger, to guarantee finalization of
|
||||
* GC-allocated obj->slots after obj. See FreeSlots in jsobj.c.
|
||||
*/
|
||||
for (i = 0; i < GC_NUM_FREELISTS; i++) {
|
||||
arenaList = &rt->gcArenaList[i];
|
||||
nbytes = GC_FREELIST_NBYTES(i);
|
||||
|
|
|
@ -131,32 +131,18 @@ js_AddRootRT(JSRuntime *rt, void *rp, const char *name);
|
|||
extern JSBool
|
||||
js_RemoveRoot(JSRuntime *rt, void *rp);
|
||||
|
||||
/*
|
||||
* Table for tracking objects of extended classes that have non-null close
|
||||
* hooks, and need the GC to perform two-phase finalization. The array grows
|
||||
* by powers of two starting from length GC_CLOSE_TABLE_MIN, but switching to
|
||||
* linear growth when length reaches GC_CLOSE_TABLE_LINEAR. The count member
|
||||
* counts valid slots in array, so the current allocated length is given by
|
||||
* GC_CLOSE_TABLE_LENGTH(count).
|
||||
*/
|
||||
typedef struct JSGCCloseTable {
|
||||
JSObject **array;
|
||||
uint32 count;
|
||||
} JSGCCloseTable;
|
||||
/* Table of pointers with count valid members. */
|
||||
typedef struct JSPtrTable {
|
||||
size_t count;
|
||||
void **array;
|
||||
} JSPtrTable;
|
||||
|
||||
extern JSBool
|
||||
js_RegisterCloseableIterator(JSContext *cx, JSObject *obj);
|
||||
|
||||
extern JSBool
|
||||
js_AddObjectToCloseTable(JSContext *cx, JSObject *obj);
|
||||
|
||||
#define GC_CLOSE_TABLE_MIN_LOG2 3
|
||||
#define GC_CLOSE_TABLE_MIN JS_BIT(GC_CLOSE_TABLE_MIN_LOG2)
|
||||
#define GC_CLOSE_TABLE_LINEAR_LOG2 10
|
||||
#define GC_CLOSE_TABLE_LINEAR JS_BIT(GC_CLOSE_TABLE_LINEAR_LOG2)
|
||||
|
||||
#define GC_CLOSE_TABLE_LENGTH(count) \
|
||||
(((count) < GC_CLOSE_TABLE_LINEAR) \
|
||||
? JS_MAX(JS_BIT(JS_CeilingLog2(count)), GC_CLOSE_TABLE_MIN) \
|
||||
: JS_ROUNDUP(count, GC_CLOSE_TABLE_LINEAR))
|
||||
|
||||
/*
|
||||
* The private JSGCThing struct, which describes a gcFreeList element.
|
||||
*/
|
||||
|
|
|
@ -76,23 +76,31 @@ extern const char js_throw_str[]; /* from jsscan.h */
|
|||
#error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS.
|
||||
#endif
|
||||
|
||||
static void
|
||||
iterator_close(JSContext *cx, JSObject *obj)
|
||||
/*
|
||||
* Shared code to close iterator's state either through an explicit call or
|
||||
* when GC detects that the iterator is no longer reachable.
|
||||
*/
|
||||
void
|
||||
js_CloseIteratorState(JSContext *cx, JSObject *iterobj)
|
||||
{
|
||||
jsval *slots;
|
||||
jsval state, parent;
|
||||
JSObject *iterable;
|
||||
|
||||
JS_ASSERT(JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL));
|
||||
slots = iterobj->slots;
|
||||
|
||||
/* Avoid double work if js_CloseNativeIterator was called on obj. */
|
||||
state = obj->slots[JSSLOT_ITER_STATE];
|
||||
if (JSVAL_IS_VOID(state))
|
||||
state = slots[JSSLOT_ITER_STATE];
|
||||
if (JSVAL_IS_NULL(state))
|
||||
return;
|
||||
|
||||
/* Protect against failure to fully initialize obj. */
|
||||
parent = obj->slots[JSSLOT_PARENT];
|
||||
if (!JSVAL_IS_NULL(state) && !JSVAL_IS_PRIMITIVE(parent)) {
|
||||
parent = slots[JSSLOT_PARENT];
|
||||
if (!JSVAL_IS_PRIMITIVE(parent)) {
|
||||
iterable = JSVAL_TO_OBJECT(parent);
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if ((JSVAL_TO_INT(obj->slots[JSSLOT_ITER_FLAGS]) & JSITER_FOREACH) &&
|
||||
if ((JSVAL_TO_INT(slots[JSSLOT_ITER_FLAGS]) & JSITER_FOREACH) &&
|
||||
OBJECT_IS_XML(cx, iterable)) {
|
||||
((JSXMLObjectOps *) iterable->map->ops)->
|
||||
enumerateValues(cx, iterable, JSENUMERATE_DESTROY, &state,
|
||||
|
@ -101,18 +109,16 @@ iterator_close(JSContext *cx, JSObject *obj)
|
|||
#endif
|
||||
OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL);
|
||||
}
|
||||
slots[JSSLOT_ITER_STATE] = JSVAL_NULL;
|
||||
}
|
||||
|
||||
JSExtendedClass js_IteratorClass = {
|
||||
{ "Iterator",
|
||||
JSCLASS_IS_EXTENDED |
|
||||
JSClass js_IteratorClass = {
|
||||
"Iterator",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
||||
JSCLASS_NO_OPTIONAL_MEMBERS },
|
||||
NULL, NULL, NULL, iterator_close,
|
||||
JSCLASS_NO_RESERVED_MEMBERS
|
||||
JSCLASS_NO_OPTIONAL_MEMBERS
|
||||
};
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
|
@ -217,7 +223,7 @@ iterator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
JSBool foreach, ok;
|
||||
jsid id;
|
||||
|
||||
if (!JS_InstanceOf(cx, obj, &js_IteratorClass.base, argv))
|
||||
if (!JS_InstanceOf(cx, obj, &js_IteratorClass, argv))
|
||||
return JS_FALSE;
|
||||
|
||||
iterable = OBJ_GET_PARENT(cx, obj);
|
||||
|
@ -290,7 +296,7 @@ js_NewNativeIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
|
|||
* scope chain to lookup the iterator's constructor. Since we use the
|
||||
* parent slot to keep track of the iterable, we must fix it up later.
|
||||
*/
|
||||
iterobj = js_NewObject(cx, &js_IteratorClass.base, NULL, NULL);
|
||||
iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL);
|
||||
if (!iterobj)
|
||||
return JS_FALSE;
|
||||
|
||||
|
@ -301,6 +307,8 @@ js_NewNativeIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
|
|||
iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj);
|
||||
iterobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL;
|
||||
iterobj->slots[JSSLOT_ITER_FLAGS] = INT_TO_JSVAL(flags);
|
||||
if (!js_RegisterCloseableIterator(cx, iterobj))
|
||||
return JS_FALSE;
|
||||
|
||||
ok =
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
|
@ -320,7 +328,7 @@ js_NewNativeIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
|
|||
uintN
|
||||
js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj)
|
||||
{
|
||||
if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass.base)
|
||||
if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass)
|
||||
return 0;
|
||||
return JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS));
|
||||
}
|
||||
|
@ -330,7 +338,7 @@ js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
|
|||
{
|
||||
uintN flags;
|
||||
|
||||
if (!JS_InstanceOf(cx, iterobj, &js_IteratorClass.base, NULL))
|
||||
if (!JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL))
|
||||
return;
|
||||
|
||||
/*
|
||||
|
@ -351,9 +359,7 @@ js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
|
|||
if (iterobj == cx->cachedIterObj)
|
||||
cx->cachedIterObj = NULL;
|
||||
|
||||
/* Close iterobj, then mark its ITER_STATE slot to flag it as dead. */
|
||||
iterator_close(cx, iterobj);
|
||||
iterobj->slots[JSSLOT_ITER_STATE] = JSVAL_VOID;
|
||||
js_CloseIteratorState(cx, iterobj);
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
@ -362,7 +368,7 @@ js_DefaultIterator(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
{
|
||||
JSBool keyonly;
|
||||
|
||||
if (OBJ_GET_CLASS(cx, obj) == &js_IteratorClass.base) {
|
||||
if (OBJ_GET_CLASS(cx, obj) == &js_IteratorClass) {
|
||||
*rval = OBJECT_TO_JSVAL(obj);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -407,7 +413,7 @@ js_ValueToIterator(JSContext *cx, jsval v, uintN flags)
|
|||
if (JSVAL_IS_PRIMITIVE(rval))
|
||||
goto bad_iterator;
|
||||
iterobj = JSVAL_TO_OBJECT(rval);
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass.base);
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass);
|
||||
iterobj->slots[JSSLOT_ITER_FLAGS] |= INT_TO_JSVAL(JSITER_HIDDEN);
|
||||
goto out;
|
||||
}
|
||||
|
@ -427,7 +433,7 @@ js_ValueToIterator(JSContext *cx, jsval v, uintN flags)
|
|||
* code -- the js_CloseNativeIteration early-finalization optimization
|
||||
* based on it will break badly if script can reach iterobj.
|
||||
*/
|
||||
if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass.base &&
|
||||
if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass &&
|
||||
VALUE_IS_FUNCTION(cx, fval)) {
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval));
|
||||
if (!FUN_INTERPRETED(fun) && fun->u.n.native == js_DefaultIterator)
|
||||
|
@ -467,7 +473,7 @@ js_CallIteratorNext(JSContext *cx, JSObject *iterobj, uintN flags,
|
|||
|
||||
/* Fastest path for repeated call from for-in loop bytecode. */
|
||||
if (iterobj == cx->cachedIterObj) {
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass.base);
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass);
|
||||
JS_ASSERT(flags & JSITER_HIDDEN);
|
||||
if (!iterator_next(cx, iterobj, 0, NULL, rval) ||
|
||||
!CheckKeyValueReturn(cx, idp, rval)) {
|
||||
|
@ -484,7 +490,7 @@ js_CallIteratorNext(JSContext *cx, JSObject *iterobj, uintN flags,
|
|||
scope = OBJ_SCOPE(obj);
|
||||
sprop = NULL;
|
||||
|
||||
while (LOCKED_OBJ_GET_CLASS(obj) == &js_IteratorClass.base) {
|
||||
while (LOCKED_OBJ_GET_CLASS(obj) == &js_IteratorClass) {
|
||||
obj = scope->object;
|
||||
sprop = SCOPE_GET_PROPERTY(scope, id);
|
||||
if (sprop)
|
||||
|
@ -873,7 +879,7 @@ js_InitIteratorClasses(JSContext *cx, JSObject *obj)
|
|||
|
||||
#if JS_HAS_GENERATORS
|
||||
/* Expose Iterator and initialize the generator internals if configured. */
|
||||
proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass.base, Iterator, 2,
|
||||
proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2,
|
||||
NULL, iterator_methods, NULL, NULL);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
|
|
|
@ -58,6 +58,9 @@ js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj);
|
|||
extern void
|
||||
js_CloseNativeIterator(JSContext *cx, JSObject *iterobj);
|
||||
|
||||
extern void
|
||||
js_CloseIteratorState(JSContext *cx, JSObject *iterobj);
|
||||
|
||||
extern JSBool
|
||||
js_DefaultIterator(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval);
|
||||
|
@ -93,7 +96,7 @@ extern JSObject *
|
|||
js_NewGenerator(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
extern JSExtendedClass js_GeneratorClass;
|
||||
extern JSExtendedClass js_IteratorClass;
|
||||
extern JSClass js_IteratorClass;
|
||||
extern JSClass js_StopIterationClass;
|
||||
extern JSClass js_GeneratorExitClass;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче