📄 jsscope.c
字号:
#ifdef DEBUG_notbrendan#define CHECK_ANCESTOR_LINE(scope, sparse) \ JS_BEGIN_MACRO \ if ((scope)->table) CheckAncestorLine(scope, sparse); \ JS_END_MACROstatic voidCheckAncestorLine(JSScope *scope, JSBool sparse){ uint32 size; JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop; uint32 entryCount, ancestorCount; ancestorLine = SCOPE_LAST_PROP(scope); if (ancestorLine) JS_ASSERT(SCOPE_HAS_PROPERTY(scope, ancestorLine)); entryCount = 0; size = SCOPE_CAPACITY(scope); start = scope->table; for (spp = start, end = start + size; spp < end; spp++) { sprop = SPROP_FETCH(spp); if (sprop) { entryCount++; for (aprop = ancestorLine; aprop; aprop = aprop->parent) { if (aprop == sprop) break; } JS_ASSERT(aprop); } } JS_ASSERT(entryCount == scope->entryCount); ancestorCount = 0; for (sprop = ancestorLine; sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) { JS_ASSERT(sparse || (sprop->flags & SPROP_IS_DUPLICATE)); continue; } ancestorCount++; } JS_ASSERT(ancestorCount == scope->entryCount);}#else#define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */#endifstatic voidReportReadOnlyScope(JSContext *cx, JSScope *scope){ JSString *str; str = js_ValueToString(cx, OBJECT_TO_JSVAL(scope->object)); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, str ? JS_GetStringBytes(str) : LOCKED_OBJ_GET_CLASS(scope->object)->name);}JSScopeProperty *js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid){ JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child; uint32 size, splen, i; int change; JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); CHECK_ANCESTOR_LINE(scope, JS_TRUE); /* * You can't add properties to a sealed scope. But note well that you can * change property attributes in a sealed scope, even though that replaces * a JSScopeProperty * in the scope's hash table -- but no id is added, so * the scope remains sealed. */ if (SCOPE_IS_SEALED(scope)) { ReportReadOnlyScope(cx, scope); return NULL; } /* * Normalize stub getter and setter values for faster is-stub testing in * the SPROP_CALL_[GS]ETTER macros. */ if (getter == JS_PropertyStub) getter = NULL; if (setter == JS_PropertyStub) setter = NULL; /* * Search for id in order to claim its entry, allocating a property tree * node if one doesn't already exist for our parameters. */ spp = js_SearchScope(scope, id, JS_TRUE); sprop = overwriting = SPROP_FETCH(spp); if (!sprop) { /* Check whether we need to grow, if the load factor is >= .75. */ size = SCOPE_CAPACITY(scope); if (scope->entryCount + scope->removedCount >= size - (size >> 2)) { if (scope->removedCount >= size >> 2) { METER(compresses); change = 0; } else { METER(grows); change = 1; } if (!ChangeScope(cx, scope, change) && scope->entryCount + scope->removedCount == size - 1) { METER(addFailures); return NULL; } spp = js_SearchScope(scope, id, JS_TRUE); JS_ASSERT(!SPROP_FETCH(spp)); } } else { /* Property exists: js_SearchScope must have returned a valid entry. */ JS_ASSERT(!SPROP_IS_REMOVED(*spp)); /* * If all property members match, this is a redundant add and we can * return early. If the caller wants to allocate a slot, but doesn't * care which slot, copy sprop->slot into slot so we can match sprop, * if all other members match. */ if (!(attrs & JSPROP_SHARED) && slot == SPROP_INVALID_SLOT && SPROP_HAS_VALID_SLOT(sprop, scope)) { slot = sprop->slot; } if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs, flags, shortid)) { METER(redundantAdds); return sprop; } /* * Duplicate formal parameters require us to leave the old property * on the ancestor line, so the decompiler can find it, even though * its entry in scope->table is overwritten to point at a new property * descending from the old one. The SPROP_IS_DUPLICATE flag helps us * cope with the consequent disparity between ancestor line height and * scope->entryCount. */ if (flags & SPROP_IS_DUPLICATE) { sprop->flags |= SPROP_IS_DUPLICATE; } else { /* * If we are clearing sprop to force an existing property to be * overwritten (apart from a duplicate formal parameter), we must * unlink it from the ancestor line at scope->lastProp, lazily if * sprop is not lastProp. And we must remove the entry at *spp, * precisely so the lazy "middle delete" fixup code further below * won't find sprop in scope->table, in spite of sprop being on * the ancestor line. * * When we finally succeed in finding or creating a new sprop * and storing its pointer at *spp, we'll use the |overwriting| * local saved when we first looked up id to decide whether we're * indeed creating a new entry, or merely overwriting an existing * property. */ 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)) { /* * If we have no hash table yet, we need one now. The middle * delete code is simple-minded that way! */ if (!scope->table) { if (!CreateScopeTable(scope)) { JS_ReportOutOfMemory(cx); return NULL; } spp = js_SearchScope(scope, id, JS_TRUE); sprop = overwriting = SPROP_FETCH(spp); } SCOPE_SET_MIDDLE_DELETE(scope); } } /* * If we fail later on trying to find or create a new sprop, we will * goto fail_overwrite and restore *spp from |overwriting|. Note that * we don't bother to keep scope->removedCount in sync, because we'll * fix up *spp and scope->entryCount shortly, no matter how control * flow returns from this function. */ if (scope->table) SPROP_STORE_PRESERVING_COLLISION(spp, NULL); scope->entryCount--; CHECK_ANCESTOR_LINE(scope, JS_TRUE); sprop = NULL; } if (!sprop) { /* * If properties were deleted from the middle of the list starting at * scope->lastProp, we may need to fork the property tree and squeeze * all deleted properties out of scope's ancestor line. Otherwise we * risk adding a node with the same id as a "middle" node, violating * the rule that properties along an ancestor line have distinct ids * (unless flagged SPROP_IS_DUPLICATE). */ if (SCOPE_HAD_MIDDLE_DELETE(scope)) { JS_ASSERT(scope->table); CHECK_ANCESTOR_LINE(scope, JS_TRUE); splen = scope->entryCount; if (splen == 0) { JS_ASSERT(scope->lastProp == NULL); } else { /* * Enumerate live entries in scope->table using a temporary * vector, by walking the (possibly sparse, due to deletions) * ancestor line from scope->lastProp. */ spvec = (JSScopeProperty **) JS_malloc(cx, SCOPE_TABLE_NBYTES(splen)); if (!spvec) goto fail_overwrite; i = splen; sprop = SCOPE_LAST_PROP(scope); JS_ASSERT(sprop); do { /* * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY -- * the latter insists that sprop->id maps to sprop, while * the former simply tests whether sprop->id is bound in * scope. We must allow for duplicate formal parameters * along the ancestor line, and fork them as needed. */ if (!SCOPE_GET_PROPERTY(scope, sprop->id)) continue; JS_ASSERT(sprop != overwriting); if (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; } 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)) { setter = js_WrapWatchedSetter(cx, id, attrs, setter); 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(scope); } 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -