58 |
#include "jsnum.h" |
#include "jsnum.h" |
59 |
#include "jsscope.h" |
#include "jsscope.h" |
60 |
#include "jsstr.h" |
#include "jsstr.h" |
61 |
|
#include "jsarray.h" |
62 |
|
|
63 |
JSScope * |
JSScope * |
64 |
js_GetMutableScope(JSContext *cx, JSObject *obj) |
js_GetMutableScope(JSContext *cx, JSObject *obj) |
71 |
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); |
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); |
72 |
if (scope->object == obj) |
if (scope->object == obj) |
73 |
return scope; |
return scope; |
74 |
newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), |
|
75 |
obj); |
/* |
76 |
|
* Compile-time block objects each have their own scope, created at |
77 |
|
* birth, and runtime clone of a block objects are never mutated. |
78 |
|
*/ |
79 |
|
JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass); |
80 |
|
newscope = js_NewScope(cx, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), obj); |
81 |
if (!newscope) |
if (!newscope) |
82 |
return NULL; |
return NULL; |
83 |
JS_LOCK_SCOPE(cx, newscope); |
JS_LOCK_SCOPE(cx, newscope); |
84 |
obj->map = js_HoldObjectMap(cx, &newscope->map); |
obj->map = &newscope->map; |
85 |
JS_ASSERT(newscope->map.freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); |
|
86 |
|
JS_ASSERT(newscope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); |
87 |
clasp = STOBJ_GET_CLASS(obj); |
clasp = STOBJ_GET_CLASS(obj); |
88 |
if (clasp->reserveSlots) { |
if (clasp->reserveSlots) { |
89 |
freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj); |
freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj); |
90 |
if (freeslot > STOBJ_NSLOTS(obj)) |
if (freeslot > STOBJ_NSLOTS(obj)) |
91 |
freeslot = STOBJ_NSLOTS(obj); |
freeslot = STOBJ_NSLOTS(obj); |
92 |
if (newscope->map.freeslot < freeslot) |
if (newscope->freeslot < freeslot) |
93 |
newscope->map.freeslot = freeslot; |
newscope->freeslot = freeslot; |
94 |
} |
} |
|
scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); |
|
95 |
JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); |
JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); |
96 |
|
js_DropScope(cx, scope, obj); |
97 |
return newscope; |
return newscope; |
98 |
} |
} |
99 |
|
|
108 |
#define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *)) |
#define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *)) |
109 |
|
|
110 |
static void |
static void |
111 |
InitMinimalScope(JSScope *scope) |
InitMinimalScope(JSContext *cx, JSScope *scope) |
112 |
{ |
{ |
113 |
|
js_LeaveTraceIfGlobalObject(cx, scope->object); |
114 |
scope->shape = 0; |
scope->shape = 0; |
115 |
scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; |
scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; |
116 |
scope->entryCount = scope->removedCount = 0; |
scope->entryCount = scope->removedCount = 0; |
160 |
} |
} |
161 |
|
|
162 |
JSScope * |
JSScope * |
163 |
js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, |
js_NewScope(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj) |
|
JSObject *obj) |
|
164 |
{ |
{ |
165 |
JSScope *scope; |
JS_ASSERT(OPS_IS_NATIVE(ops)); |
166 |
|
JS_ASSERT(obj); |
167 |
|
|
168 |
scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); |
JSScope *scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); |
169 |
if (!scope) |
if (!scope) |
170 |
return NULL; |
return NULL; |
171 |
|
|
172 |
js_InitObjectMap(&scope->map, nrefs, ops, clasp); |
scope->map.ops = ops; |
173 |
scope->object = obj; |
scope->object = obj; |
174 |
|
scope->nrefs = 1; |
175 |
|
scope->freeslot = JSSLOT_FREE(clasp); |
176 |
scope->flags = 0; |
scope->flags = 0; |
177 |
InitMinimalScope(scope); |
InitMinimalScope(cx, scope); |
178 |
|
|
179 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
180 |
js_InitTitle(cx, &scope->title); |
js_InitTitle(cx, &scope->title); |
184 |
return scope; |
return scope; |
185 |
} |
} |
186 |
|
|
|
#ifdef DEBUG_SCOPE_COUNT |
|
|
extern void |
|
|
js_unlog_scope(JSScope *scope); |
|
|
#endif |
|
|
|
|
187 |
#if defined DEBUG || defined JS_DUMP_PROPTREE_STATS |
#if defined DEBUG || defined JS_DUMP_PROPTREE_STATS |
188 |
# include "jsprf.h" |
# include "jsprf.h" |
189 |
# define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr) |
# define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr) |
194 |
void |
void |
195 |
js_DestroyScope(JSContext *cx, JSScope *scope) |
js_DestroyScope(JSContext *cx, JSScope *scope) |
196 |
{ |
{ |
|
#ifdef DEBUG_SCOPE_COUNT |
|
|
js_unlog_scope(scope); |
|
|
#endif |
|
|
|
|
197 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
198 |
js_FinishTitle(cx, &scope->title); |
js_FinishTitle(cx, &scope->title); |
199 |
#endif |
#endif |
205 |
JS_free(cx, scope); |
JS_free(cx, scope); |
206 |
} |
} |
207 |
|
|
208 |
|
void |
209 |
|
js_HoldScope(JSScope *scope) |
210 |
|
{ |
211 |
|
JS_ASSERT(scope->nrefs >= 0); |
212 |
|
JS_ATOMIC_INCREMENT(&scope->nrefs); |
213 |
|
} |
214 |
|
|
215 |
|
JSBool |
216 |
|
js_DropScope(JSContext *cx, JSScope *scope, JSObject *obj) |
217 |
|
{ |
218 |
|
JS_ASSERT(scope->nrefs > 0); |
219 |
|
JS_ATOMIC_DECREMENT(&scope->nrefs); |
220 |
|
|
221 |
|
if (scope->nrefs == 0) { |
222 |
|
js_DestroyScope(cx, scope); |
223 |
|
return false; |
224 |
|
} |
225 |
|
if (scope->object == obj) |
226 |
|
scope->object = NULL; |
227 |
|
return true; |
228 |
|
} |
229 |
|
|
230 |
#ifdef JS_DUMP_PROPTREE_STATS |
#ifdef JS_DUMP_PROPTREE_STATS |
231 |
typedef struct JSScopeStats { |
typedef struct JSScopeStats { |
232 |
jsrefcount searches; |
jsrefcount searches; |
819 |
* only when inserting a new child. Thus there may be races to find or add a |
* only when inserting a new child. Thus there may be races to find or add a |
820 |
* node that result in duplicates. We expect such races to be rare! |
* node that result in duplicates. We expect such races to be rare! |
821 |
* |
* |
822 |
* We use rt->gcLock, not rt->rtLock, to allow the GC potentially to nest here |
* We use rt->gcLock, not rt->rtLock, to avoid nesting the former inside the |
823 |
* under js_GenerateShape. |
* latter in js_GenerateShape below. |
824 |
*/ |
*/ |
825 |
static JSScopeProperty * |
static JSScopeProperty * |
826 |
GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, |
GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, |
1052 |
spp = js_SearchScope(scope, id, JS_TRUE); |
spp = js_SearchScope(scope, id, JS_TRUE); |
1053 |
sprop = overwriting = SPROP_FETCH(spp); |
sprop = overwriting = SPROP_FETCH(spp); |
1054 |
if (!sprop) { |
if (!sprop) { |
|
JS_COUNT_OPERATION(cx, JSOW_NEW_PROPERTY); |
|
|
|
|
1055 |
/* Check whether we need to grow, if the load factor is >= .75. */ |
/* Check whether we need to grow, if the load factor is >= .75. */ |
1056 |
size = SCOPE_CAPACITY(scope); |
size = SCOPE_CAPACITY(scope); |
1057 |
if (scope->entryCount + scope->removedCount >= size - (size >> 2)) { |
if (scope->entryCount + scope->removedCount >= size - (size >> 2)) { |
1092 |
} |
} |
1093 |
|
|
1094 |
/* |
/* |
1095 |
* If we are clearing sprop to force an existing property to be |
* If we are clearing sprop to force the existing property that it |
1096 |
* overwritten (apart from a duplicate formal parameter), we must |
* describes to be overwritten, then we have to unlink sprop from the |
1097 |
* unlink it from the ancestor line at scope->lastProp, lazily if |
* ancestor line at scope->lastProp, lazily if sprop is not lastProp. |
1098 |
* sprop is not lastProp. And we must remove the entry at *spp, |
* And we must remove the entry at *spp, precisely so the lazy "middle |
1099 |
* precisely so the lazy "middle delete" fixup code further below |
* delete" fixup code further below won't find sprop in scope->table, |
1100 |
* won't find sprop in scope->table, in spite of sprop being on |
* in spite of sprop being on the ancestor line. |
|
* the ancestor line. |
|
1101 |
* |
* |
1102 |
* When we finally succeed in finding or creating a new sprop |
* When we finally succeed in finding or creating a new sprop |
1103 |
* and storing its pointer at *spp, we'll use the |overwriting| |
* and storing its pointer at *spp, we'll use the |overwriting| |
1125 |
} |
} |
1126 |
SCOPE_SET_MIDDLE_DELETE(scope); |
SCOPE_SET_MIDDLE_DELETE(scope); |
1127 |
} |
} |
1128 |
SCOPE_MAKE_UNIQUE_SHAPE(cx, scope); |
js_MakeScopeShapeUnique(cx, scope); |
1129 |
|
|
1130 |
/* |
/* |
1131 |
* If we fail later on trying to find or create a new sprop, we will |
* If we fail later on trying to find or create a new sprop, we will |
1153 |
JS_ASSERT(scope->table); |
JS_ASSERT(scope->table); |
1154 |
CHECK_ANCESTOR_LINE(scope, JS_TRUE); |
CHECK_ANCESTOR_LINE(scope, JS_TRUE); |
1155 |
|
|
1156 |
splen = scope->entryCount; |
/* |
1157 |
if (splen == 0) { |
* Our forking heuristic tries to balance the desire to avoid |
1158 |
JS_ASSERT(scope->lastProp == NULL); |
* over-compacting (over-forking) against the desire to |
1159 |
} else { |
* *periodically* fork anyways, in order to prevent paying scan |
1160 |
|
* penalties on each insert indefinitely, on a lineage with only |
1161 |
|
* a few old middle-deletions. So we fork if either: |
1162 |
|
* |
1163 |
|
* - A quick scan finds a true conflict. |
1164 |
|
* - We are passing through a doubling-threshold in size and |
1165 |
|
* have accumulated a nonzero count of uncompacted deletions. |
1166 |
|
*/ |
1167 |
|
|
1168 |
|
bool conflicts = false; |
1169 |
|
uint32 count = 0; |
1170 |
|
uint32 threshold = JS_BIT(JS_CeilingLog2(scope->entryCount)); |
1171 |
|
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { |
1172 |
|
++count; |
1173 |
|
if (sprop->id == id) { |
1174 |
|
conflicts = true; |
1175 |
|
break; |
1176 |
|
} |
1177 |
|
} |
1178 |
|
|
1179 |
|
if (conflicts || count > threshold) { |
1180 |
/* |
/* |
1181 |
* Enumerate live entries in scope->table using a temporary |
* Enumerate live entries in scope->table using a temporary |
1182 |
* vector, by walking the (possibly sparse, due to deletions) |
* vector, by walking the (possibly sparse, due to deletions) |
1183 |
* ancestor line from scope->lastProp. |
* ancestor line from scope->lastProp. |
1184 |
*/ |
*/ |
1185 |
|
splen = scope->entryCount; |
1186 |
|
JS_ASSERT(splen != 0); |
1187 |
spvec = (JSScopeProperty **) |
spvec = (JSScopeProperty **) |
1188 |
JS_malloc(cx, SCOPE_TABLE_NBYTES(splen)); |
JS_malloc(cx, SCOPE_TABLE_NBYTES(splen)); |
1189 |
if (!spvec) |
if (!spvec) |
1193 |
JS_ASSERT(sprop); |
JS_ASSERT(sprop); |
1194 |
do { |
do { |
1195 |
/* |
/* |
1196 |
* NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY -- |
* NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY, |
1197 |
* the latter insists that sprop->id maps to sprop, while |
* as the latter macro insists that sprop->id maps to |
1198 |
* the former simply tests whether sprop->id is bound in |
* sprop, while the former simply tests whether sprop->id |
1199 |
* scope. We must allow for duplicate formal parameters |
* is bound in scope. |
|
* along the ancestor line, and fork them as needed. |
|
1200 |
*/ |
*/ |
1201 |
if (!SCOPE_GET_PROPERTY(scope, sprop->id)) |
if (!SCOPE_GET_PROPERTY(scope, sprop->id)) |
1202 |
continue; |
continue; |
1203 |
|
|
1204 |
JS_ASSERT(sprop != overwriting); |
JS_ASSERT(sprop != overwriting); |
1205 |
if (i == 0) { |
JS_ASSERT(i != 0); |
|
/* |
|
|
* If our original splen estimate, scope->entryCount, |
|
|
* is less than the ancestor line height, there must |
|
|
* be duplicate formal parameters in this (function |
|
|
* object) scope. Count remaining ancestors in order |
|
|
* to realloc spvec. |
|
|
*/ |
|
|
JSScopeProperty *tmp = sprop; |
|
|
do { |
|
|
if (SCOPE_GET_PROPERTY(scope, tmp->id)) |
|
|
i++; |
|
|
} while ((tmp = tmp->parent) != NULL); |
|
|
spp2 = (JSScopeProperty **) |
|
|
JS_realloc(cx, spvec, SCOPE_TABLE_NBYTES(splen+i)); |
|
|
if (!spp2) { |
|
|
JS_free(cx, spvec); |
|
|
goto fail_overwrite; |
|
|
} |
|
|
|
|
|
spvec = spp2; |
|
|
memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen)); |
|
|
splen += i; |
|
|
} |
|
|
|
|
1206 |
spvec[--i] = sprop; |
spvec[--i] = sprop; |
1207 |
} while ((sprop = sprop->parent) != NULL); |
} while ((sprop = sprop->parent) != NULL); |
1208 |
JS_ASSERT(i == 0); |
JS_ASSERT(i == 0); |
1232 |
/* |
/* |
1233 |
* Now sprop points to the last property in scope, where the |
* Now sprop points to the last property in scope, where the |
1234 |
* ancestor line from sprop to the root is dense w.r.t. scope: |
* ancestor line from sprop to the root is dense w.r.t. scope: |
1235 |
* it contains no nodes not mapped by scope->table, apart from |
* it contains no nodes not mapped by scope->table. |
|
* any stinking ECMA-mandated duplicate formal parameters. |
|
1236 |
*/ |
*/ |
1237 |
scope->lastProp = sprop; |
scope->lastProp = sprop; |
1238 |
CHECK_ANCESTOR_LINE(scope, JS_FALSE); |
CHECK_ANCESTOR_LINE(scope, JS_FALSE); |
1239 |
JS_RUNTIME_METER(cx->runtime, middleDeleteFixups); |
JS_RUNTIME_METER(cx->runtime, middleDeleteFixups); |
1240 |
|
SCOPE_CLR_MIDDLE_DELETE(scope); |
1241 |
} |
} |
|
|
|
|
SCOPE_CLR_MIDDLE_DELETE(scope); |
|
1242 |
} |
} |
1243 |
|
|
1244 |
/* |
/* |
1271 |
*/ |
*/ |
1272 |
if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && |
if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && |
1273 |
js_FindWatchPoint(cx->runtime, scope, id)) { |
js_FindWatchPoint(cx->runtime, scope, id)) { |
1274 |
JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr); |
if (overwriting) |
1275 |
|
JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr); |
1276 |
setter = js_WrapWatchedSetter(cx, id, attrs, setter); |
setter = js_WrapWatchedSetter(cx, id, attrs, setter); |
1277 |
JS_POP_TEMP_ROOT(cx, &tvr); |
if (overwriting) |
1278 |
|
JS_POP_TEMP_ROOT(cx, &tvr); |
1279 |
if (!setter) |
if (!setter) |
1280 |
goto fail_overwrite; |
goto fail_overwrite; |
1281 |
} |
} |
1297 |
* be regenerated later as the scope diverges (from the property cache |
* be regenerated later as the scope diverges (from the property cache |
1298 |
* point of view) from the structural type associated with sprop. |
* point of view) from the structural type associated with sprop. |
1299 |
*/ |
*/ |
1300 |
SCOPE_EXTEND_SHAPE(cx, scope, sprop); |
js_ExtendScopeShape(cx, scope, sprop); |
1301 |
|
|
1302 |
/* Store the tree node pointer in the table entry for id. */ |
/* Store the tree node pointer in the table entry for id. */ |
1303 |
if (scope->table) |
if (scope->table) |
1324 |
(void) CreateScopeTable(cx, scope, JS_FALSE); |
(void) CreateScopeTable(cx, scope, JS_FALSE); |
1325 |
} |
} |
1326 |
|
|
1327 |
|
jsuint index; |
1328 |
|
if (js_IdIsIndex(sprop->id, &index)) |
1329 |
|
SCOPE_SET_INDEXED_PROPERTIES(scope); |
1330 |
|
|
1331 |
METER(adds); |
METER(adds); |
1332 |
return sprop; |
return sprop; |
1333 |
|
|
1437 |
} |
} |
1438 |
|
|
1439 |
if (newsprop) { |
if (newsprop) { |
1440 |
|
js_LeaveTraceIfGlobalObject(cx, scope->object); |
1441 |
if (scope->shape == sprop->shape) |
if (scope->shape == sprop->shape) |
1442 |
scope->shape = newsprop->shape; |
scope->shape = newsprop->shape; |
1443 |
else |
else |
1444 |
SCOPE_MAKE_UNIQUE_SHAPE(cx, scope); |
js_MakeScopeShapeUnique(cx, scope); |
1445 |
} |
} |
1446 |
#ifdef JS_DUMP_PROPTREE_STATS |
#ifdef JS_DUMP_PROPTREE_STATS |
1447 |
else |
else |
1508 |
break; |
break; |
1509 |
sprop = SCOPE_LAST_PROP(scope); |
sprop = SCOPE_LAST_PROP(scope); |
1510 |
} while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); |
} while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); |
1511 |
|
if (!SCOPE_LAST_PROP(scope)) |
1512 |
|
SCOPE_CLR_MIDDLE_DELETE(scope); |
1513 |
} else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { |
} else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { |
1514 |
SCOPE_SET_MIDDLE_DELETE(scope); |
SCOPE_SET_MIDDLE_DELETE(scope); |
1515 |
} |
} |
1516 |
SCOPE_MAKE_UNIQUE_SHAPE(cx, scope); |
js_MakeScopeShapeUnique(cx, scope); |
1517 |
CHECK_ANCESTOR_LINE(scope, JS_TRUE); |
CHECK_ANCESTOR_LINE(scope, JS_TRUE); |
1518 |
|
|
1519 |
/* Last, consider shrinking scope's table if its load factor is <= .25. */ |
/* Last, consider shrinking scope's table if its load factor is <= .25. */ |
1535 |
if (scope->table) |
if (scope->table) |
1536 |
free(scope->table); |
free(scope->table); |
1537 |
SCOPE_CLR_MIDDLE_DELETE(scope); |
SCOPE_CLR_MIDDLE_DELETE(scope); |
1538 |
InitMinimalScope(scope); |
InitMinimalScope(cx, scope); |
1539 |
JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); |
JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); |
1540 |
} |
} |
1541 |
|
|
1586 |
#if JS_HAS_GETTER_SETTER |
#if JS_HAS_GETTER_SETTER |
1587 |
if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { |
if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { |
1588 |
if (sprop->attrs & JSPROP_GETTER) { |
if (sprop->attrs & JSPROP_GETTER) { |
|
JS_ASSERT(JSVAL_IS_OBJECT((jsval) sprop->getter)); |
|
1589 |
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 0); |
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 0); |
1590 |
JS_CallTracer(trc, JSVAL_TO_OBJECT((jsval) sprop->getter), |
JS_CallTracer(trc, js_CastAsObject(sprop->getter), JSTRACE_OBJECT); |
|
JSTRACE_OBJECT); |
|
1591 |
} |
} |
1592 |
if (sprop->attrs & JSPROP_SETTER) { |
if (sprop->attrs & JSPROP_SETTER) { |
|
JS_ASSERT(JSVAL_IS_OBJECT((jsval) sprop->setter)); |
|
1593 |
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 1); |
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 1); |
1594 |
JS_CallTracer(trc, JSVAL_TO_OBJECT((jsval) sprop->setter), |
JS_CallTracer(trc, js_CastAsObject(sprop->setter), JSTRACE_OBJECT); |
|
JSTRACE_OBJECT); |
|
1595 |
} |
} |
1596 |
} |
} |
1597 |
#endif /* JS_HAS_GETTER_SETTER */ |
#endif /* JS_HAS_GETTER_SETTER */ |
1757 |
*/ |
*/ |
1758 |
if (sprop->flags & SPROP_MARK) { |
if (sprop->flags & SPROP_MARK) { |
1759 |
sprop->flags &= ~SPROP_MARK; |
sprop->flags &= ~SPROP_MARK; |
1760 |
if (sprop->flags & SPROP_FLAG_SHAPE_REGEN) { |
if (sprop->flags & SPROP_FLAG_SHAPE_REGEN) |
1761 |
sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN; |
sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN; |
1762 |
} else { |
else |
1763 |
sprop->shape = ++cx->runtime->shapeGen; |
sprop->shape = js_RegenerateShapeForGC(cx); |
|
JS_ASSERT(sprop->shape != 0); |
|
|
} |
|
1764 |
liveCount++; |
liveCount++; |
1765 |
continue; |
continue; |
1766 |
} |
} |