📄 jsscope.c
字号:
} spvec = spp2; memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen)); splen += i; } spvec[--i] = sprop; } while ((sprop = sprop->parent) != NULL); JS_ASSERT(i == 0); /* * Now loop forward through spvec, forking the property tree * whenever we see a "parent gap" due to deletions from scope. * NB: sprop is null on first entry to the loop body. */ do { if (spvec[i]->parent == sprop) { sprop = spvec[i]; } else { sprop = GetPropertyTreeChild(cx, sprop, spvec[i]); if (!sprop) { JS_free(cx, spvec); goto fail_overwrite; } spp2 = js_SearchScope(scope, sprop->id, JS_FALSE); JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]); SPROP_STORE_PRESERVING_COLLISION(spp2, sprop); } } while (++i < splen); JS_free(cx, spvec); /* * Now sprop points to the last property in scope, where the * ancestor line from sprop to the root is dense w.r.t. scope: * it contains no nodes not mapped by scope->table, apart from * any stinking ECMA-mandated duplicate formal parameters. */ scope->lastProp = sprop; CHECK_ANCESTOR_LINE(scope, JS_FALSE); JS_RUNTIME_METER(cx->runtime, middleDeleteFixups); } SCOPE_CLR_MIDDLE_DELETE(scope); } /* * Aliases share another property's slot, passed in the |slot| param. * Shared properties have no slot. Unshared properties that do not * alias another property's slot get one here, but may lose it due to * a JS_ClearScope call. */ if (!(flags & SPROP_IS_ALIAS)) { if (attrs & JSPROP_SHARED) { slot = SPROP_INVALID_SLOT; } else { /* * We may have set slot from a nearly-matching sprop, above. * If so, we're overwriting that nearly-matching sprop, so we * can reuse its slot -- we don't need to allocate a new one. * Callers should therefore pass SPROP_INVALID_SLOT for all * non-alias, unshared property adds. */ if (slot != SPROP_INVALID_SLOT) JS_ASSERT(overwriting); else if (!js_AllocSlot(cx, scope->object, &slot)) goto fail_overwrite; } } /* * Check for a watchpoint on a deleted property; if one exists, change * setter to js_watch_set. * XXXbe this could get expensive with lots of watchpoints... */ if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && js_FindWatchPoint(cx->runtime, scope, id)) { JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr); setter = js_WrapWatchedSetter(cx, id, attrs, setter); JS_POP_TEMP_ROOT(cx, &tvr); if (!setter) goto fail_overwrite; } /* Find or create a property tree node labeled by our arguments. */ child.id = id; child.getter = getter; child.setter = setter; child.slot = slot; child.attrs = attrs; child.flags = flags; child.shortid = shortid; sprop = GetPropertyTreeChild(cx, scope->lastProp, &child); if (!sprop) goto fail_overwrite; /* Store the tree node pointer in the table entry for id. */ if (scope->table) SPROP_STORE_PRESERVING_COLLISION(spp, sprop); scope->entryCount++; scope->lastProp = sprop; CHECK_ANCESTOR_LINE(scope, JS_FALSE); if (!overwriting) { JS_RUNTIME_METER(cx->runtime, liveScopeProps); JS_RUNTIME_METER(cx->runtime, totalScopeProps); } /* * If we reach the hashing threshold, try to allocate scope->table. * If we can't (a rare event, preceded by swapping to death on most * modern OSes), stick with linear search rather than whining about * this little set-back. Therefore we must test !scope->table and * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the * entry count just reached the threshold. */ if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD) (void) CreateScopeTable(cx, scope, JS_FALSE); } METER(adds); return sprop;fail_overwrite: if (overwriting) { /* * We may or may not have forked overwriting out of scope's ancestor * line, so we must check (the alternative is to set a flag above, but * that hurts the common, non-error case). If we did fork overwriting * out, we'll add it back at scope->lastProp. This means enumeration * order can change due to a failure to overwrite an id. * XXXbe very minor incompatibility */ for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) { if (!sprop) { sprop = SCOPE_LAST_PROP(scope); if (overwriting->parent == sprop) { scope->lastProp = overwriting; } else { sprop = GetPropertyTreeChild(cx, sprop, overwriting); if (sprop) { JS_ASSERT(sprop != overwriting); scope->lastProp = sprop; } overwriting = sprop; } break; } if (sprop == overwriting) break; } if (overwriting) { if (scope->table) SPROP_STORE_PRESERVING_COLLISION(spp, overwriting); scope->entryCount++; } CHECK_ANCESTOR_LINE(scope, JS_TRUE); } METER(addFailures); return NULL;}JSScopeProperty *js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, JSScopeProperty *sprop, uintN attrs, uintN mask, JSPropertyOp getter, JSPropertyOp setter){ JSScopeProperty child, *newsprop, **spp; CHECK_ANCESTOR_LINE(scope, JS_TRUE); /* Allow only shared (slot-less) => unshared (slot-full) transition. */ attrs |= sprop->attrs & mask; JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) || !(attrs & JSPROP_SHARED)); if (getter == JS_PropertyStub) getter = NULL; if (setter == JS_PropertyStub) setter = NULL; if (sprop->attrs == attrs && sprop->getter == getter && sprop->setter == setter) { return sprop; } child.id = sprop->id; child.getter = getter; child.setter = setter; child.slot = sprop->slot; child.attrs = attrs; child.flags = sprop->flags; child.shortid = sprop->shortid; if (SCOPE_LAST_PROP(scope) == sprop) { /* * Optimize the case where the last property added to scope is changed * to have a different attrs, getter, or setter. In the last property * case, we need not fork the property tree. But since we do not call * js_AddScopeProperty, we may need to allocate a new slot directly. */ if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) { JS_ASSERT(child.slot == SPROP_INVALID_SLOT); if (!js_AllocSlot(cx, scope->object, &child.slot)) return NULL; } newsprop = GetPropertyTreeChild(cx, sprop->parent, &child); if (newsprop) { spp = js_SearchScope(scope, sprop->id, JS_FALSE); JS_ASSERT(SPROP_FETCH(spp) == sprop); if (scope->table) SPROP_STORE_PRESERVING_COLLISION(spp, newsprop); scope->lastProp = newsprop; CHECK_ANCESTOR_LINE(scope, JS_TRUE); } } else { /* * Let js_AddScopeProperty handle this |overwriting| case, including * the conservation of sprop->slot (if it's valid). We must not call * js_RemoveScopeProperty here, it will free a valid sprop->slot and * js_AddScopeProperty won't re-allocate it. */ newsprop = js_AddScopeProperty(cx, scope, child.id, child.getter, child.setter, child.slot, child.attrs, child.flags, child.shortid); }#ifdef DUMP_SCOPE_STATS if (!newsprop) METER(changeFailures);#endif return newsprop;}JSBooljs_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id){ JSScopeProperty **spp, *stored, *sprop; uint32 size; JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); CHECK_ANCESTOR_LINE(scope, JS_TRUE); if (SCOPE_IS_SEALED(scope)) { ReportReadOnlyScope(cx, scope); return JS_FALSE; } METER(removes); spp = js_SearchScope(scope, id, JS_FALSE); stored = *spp; sprop = SPROP_CLEAR_COLLISION(stored); if (!sprop) { METER(uselessRemoves); return JS_TRUE; } /* Convert from a list to a hash so we can handle "middle deletes". */ if (!scope->table && sprop != scope->lastProp) { if (!CreateScopeTable(cx, scope, JS_TRUE)) return JS_FALSE; spp = js_SearchScope(scope, id, JS_FALSE); stored = *spp; sprop = SPROP_CLEAR_COLLISION(stored); } /* First, if sprop is unshared and not cleared, free its slot number. */ if (SPROP_HAS_VALID_SLOT(sprop, scope)) { js_FreeSlot(cx, scope->object, sprop->slot); JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); } /* Next, remove id by setting its entry to a removed or free sentinel. */ if (SPROP_HAD_COLLISION(stored)) { JS_ASSERT(scope->table); *spp = SPROP_REMOVED; scope->removedCount++; } else { METER(removeFrees); if (scope->table) *spp = NULL; } scope->entryCount--; JS_RUNTIME_UNMETER(cx->runtime, liveScopeProps); /* Update scope->lastProp directly, or set its deferred update flag. */ if (sprop == SCOPE_LAST_PROP(scope)) { do { SCOPE_REMOVE_LAST_PROP(scope); if (!SCOPE_HAD_MIDDLE_DELETE(scope)) break; sprop = SCOPE_LAST_PROP(scope); } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { SCOPE_SET_MIDDLE_DELETE(scope); } CHECK_ANCESTOR_LINE(scope, JS_TRUE); /* Last, consider shrinking scope's table if its load factor is <= .25. */ size = SCOPE_CAPACITY(scope); if (size > MIN_SCOPE_SIZE && scope->entryCount <= size >> 2) { METER(shrinks); (void) ChangeScope(cx, scope, -1); } return JS_TRUE;}voidjs_ClearScope(JSContext *cx, JSScope *scope){ CHECK_ANCESTOR_LINE(scope, JS_TRUE);#ifdef DEBUG JS_LOCK_RUNTIME_VOID(cx->runtime, cx->runtime->liveScopeProps -= scope->entryCount);#endif if (scope->table) free(scope->table); SCOPE_CLR_MIDDLE_DELETE(scope); InitMinimalScope(scope); JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);}voidjs_MarkId(JSContext *cx, jsid id){ if (JSID_IS_ATOM(id)) GC_MARK_ATOM(cx, JSID_TO_ATOM(id)); else if (JSID_IS_OBJECT(id)) GC_MARK(cx, JSID_TO_OBJECT(id), "id"); else JS_ASSERT(JSID_IS_INT(id));}#if defined GC_MARK_DEBUG || defined DUMP_SCOPE_STATS# include "jsprf.h"#endifvoidjs_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop){ sprop->flags |= SPROP_MARK; MARK_ID(cx, sprop->id);#if JS_HAS_GETTER_SETTER if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {#ifdef GC_MARK_DEBUG char buf[64]; char buf2[11]; const char *id; if (JSID_IS_ATOM(sprop->id)) { JSAtom *atom = JSID_TO_ATOM(sprop->id); id = (atom && ATOM_IS_STRING(atom))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -