📄 jsscope.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* JS symbol tables.
*/
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h"
#include "jsbit.h"
#include "jsclist.h"
#include "jsdhash.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsdbgapi.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsscope.h"
#include "jsstr.h"
JSScope *
js_GetMutableScope(JSContext *cx, JSObject *obj)
{
JSScope *scope, *newscope;
scope = OBJ_SCOPE(obj);
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
if (scope->object == obj)
return scope;
newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj),
obj);
if (!newscope)
return NULL;
JS_LOCK_SCOPE(cx, newscope);
obj->map = js_HoldObjectMap(cx, &newscope->map);
scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj);
JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
return newscope;
}
/*
* JSScope uses multiplicative hashing, _a la_ jsdhash.[ch], but specialized
* to minimize footprint. But if a scope has fewer than SCOPE_HASH_THRESHOLD
* entries, we use linear search and avoid allocating scope->table.
*/
#define SCOPE_HASH_THRESHOLD 6
#define MIN_SCOPE_SIZE_LOG2 4
#define MIN_SCOPE_SIZE JS_BIT(MIN_SCOPE_SIZE_LOG2)
#define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *))
static void
InitMinimalScope(JSScope *scope)
{
scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2;
scope->entryCount = scope->removedCount = 0;
scope->table = NULL;
scope->lastProp = NULL;
}
static JSBool
CreateScopeTable(JSScope *scope)
{
int sizeLog2;
JSScopeProperty *sprop, **spp;
JS_ASSERT(!scope->table);
JS_ASSERT(scope->lastProp);
if (scope->entryCount > SCOPE_HASH_THRESHOLD) {
/*
* Ouch: calloc failed at least once already -- let's try again,
* overallocating to hold at least twice the current population.
*/
sizeLog2 = JS_CeilingLog2(2 * scope->entryCount);
scope->hashShift = JS_DHASH_BITS - sizeLog2;
} else {
JS_ASSERT(scope->hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2);
sizeLog2 = MIN_SCOPE_SIZE_LOG2;
}
scope->table = (JSScopeProperty **)
calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *));
if (!scope->table)
return JS_FALSE;
scope->hashShift = JS_DHASH_BITS - sizeLog2;
for (sprop = scope->lastProp; sprop; sprop = sprop->parent) {
spp = js_SearchScope(scope, sprop->id, JS_TRUE);
SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
}
return JS_TRUE;
}
JSScope *
js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp,
JSObject *obj)
{
JSScope *scope;
scope = (JSScope *) JS_malloc(cx, sizeof(JSScope));
if (!scope)
return NULL;
js_InitObjectMap(&scope->map, nrefs, ops, clasp);
scope->object = obj;
scope->flags = 0;
InitMinimalScope(scope);
#ifdef JS_THREADSAFE
scope->ownercx = cx;
memset(&scope->lock, 0, sizeof scope->lock);
/*
* Set u.link = NULL, not u.count = 0, in case the target architecture's
* null pointer has a non-zero integer representation.
*/
scope->u.link = NULL;
#ifdef DEBUG
scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL;
scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0;
#endif
#endif
JS_RUNTIME_METER(cx->runtime, liveScopes);
JS_RUNTIME_METER(cx->runtime, totalScopes);
return scope;
}
#ifdef DEBUG_SCOPE_COUNT
extern void
js_unlog_scope(JSScope *scope);
#endif
void
js_DestroyScope(JSContext *cx, JSScope *scope)
{
#ifdef DEBUG_SCOPE_COUNT
js_unlog_scope(scope);
#endif
#ifdef JS_THREADSAFE
/* Scope must be single-threaded at this point, so set scope->ownercx. */
JS_ASSERT(scope->u.count == 0);
scope->ownercx = cx;
js_FinishLock(&scope->lock);
#endif
if (scope->table)
JS_free(cx, scope->table);
#ifdef DEBUG
JS_LOCK_RUNTIME_VOID(cx->runtime,
cx->runtime->liveScopeProps -= scope->entryCount);
#endif
JS_RUNTIME_UNMETER(cx->runtime, liveScopes);
JS_free(cx, scope);
}
#ifdef DEBUG_brendan
typedef struct JSScopeStats {
jsrefcount searches;
jsrefcount steps;
jsrefcount hits;
jsrefcount misses;
jsrefcount stepHits;
jsrefcount stepMisses;
jsrefcount adds;
jsrefcount redundantAdds;
jsrefcount addFailures;
jsrefcount changeFailures;
jsrefcount compresses;
jsrefcount grows;
jsrefcount removes;
jsrefcount removeFrees;
jsrefcount uselessRemoves;
jsrefcount shrinks;
} JSScopeStats;
JS_FRIEND_DATA(JSScopeStats) js_scope_stats;
# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
#else
# define METER(x) /* nothing */
#endif
/*
* Double hashing needs the second hash code to be relatively prime to table
* size, so we simply make hash2 odd. The inputs to multiplicative hash are
* the golden ratio, expressed as a fixed-point 32 bit fraction, and the int
* property index or named property's atom number (observe that most objects
* have either no indexed properties, or almost all indexed and a few names,
* so collisions between index and atom number are unlikely).
*/
#define SCOPE_HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO)
#define SCOPE_HASH1(hash0,shift) ((hash0) >> (shift))
#define SCOPE_HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
JS_FRIEND_API(JSScopeProperty **)
js_SearchScope(JSScope *scope, jsid id, JSBool adding)
{
JSHashNumber hash0, hash1, hash2;
int hashShift, sizeLog2;
JSScopeProperty *stored, *sprop, **spp, **firstRemoved;
uint32 sizeMask;
METER(searches);
if (!scope->table) {
/* Not enough properties to justify hashing: search from lastProp. */
JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope));
for (spp = &scope->lastProp; (sprop = *spp); spp = &sprop->parent) {
if (sprop->id == id) {
METER(hits);
return spp;
}
}
METER(misses);
return spp;
}
/* Compute the primary hash address. */
hash0 = SCOPE_HASH0(id);
hashShift = scope->hashShift;
hash1 = SCOPE_HASH1(hash0, hashShift);
spp = scope->table + hash1;
/* Miss: return space for a new entry. */
stored = *spp;
if (SPROP_IS_FREE(stored)) {
METER(misses);
return spp;
}
/* Hit: return entry. */
sprop = SPROP_CLEAR_COLLISION(stored);
if (sprop && sprop->id == id) {
METER(hits);
return spp;
}
/* Collision: double hash. */
sizeLog2 = JS_DHASH_BITS - hashShift;
hash2 = SCOPE_HASH2(hash0, sizeLog2, hashShift);
sizeMask = JS_BITMASK(sizeLog2);
/* Save the first removed entry pointer so we can recycle it if adding. */
if (SPROP_IS_REMOVED(stored)) {
firstRemoved = spp;
} else {
firstRemoved = NULL;
if (adding && !SPROP_HAD_COLLISION(stored))
SPROP_FLAG_COLLISION(spp, sprop);
}
for (;;) {
METER(steps);
hash1 -= hash2;
hash1 &= sizeMask;
spp = scope->table + hash1;
stored = *spp;
if (SPROP_IS_FREE(stored)) {
METER(stepMisses);
return (adding && firstRemoved) ? firstRemoved : spp;
}
sprop = SPROP_CLEAR_COLLISION(stored);
if (sprop && sprop->id == id) {
METER(stepHits);
return spp;
}
if (SPROP_IS_REMOVED(stored)) {
if (!firstRemoved)
firstRemoved = spp;
} else {
if (adding && !SPROP_HAD_COLLISION(stored))
SPROP_FLAG_COLLISION(spp, sprop);
}
}
/* NOTREACHED */
return NULL;
}
static JSBool
ChangeScope(JSContext *cx, JSScope *scope, int change)
{
int oldlog2, newlog2;
uint32 oldsize, newsize, nbytes;
JSScopeProperty **table, **oldtable, **spp, **oldspp, *sprop;
/* Grow, shrink, or compress by changing scope->table. */
oldlog2 = JS_DHASH_BITS - scope->hashShift;
newlog2 = oldlog2 + change;
oldsize = JS_BIT(oldlog2);
newsize = JS_BIT(newlog2);
nbytes = SCOPE_TABLE_NBYTES(newsize);
table = (JSScopeProperty **) calloc(nbytes, 1);
if (!table) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
/* Now that we have a new table allocated, update scope members. */
scope->hashShift = JS_DHASH_BITS - newlog2;
scope->removedCount = 0;
oldtable = scope->table;
scope->table = table;
/* Copy only live entries, leaving removed and free ones behind. */
for (oldspp = oldtable; oldsize != 0; oldspp++) {
sprop = SPROP_FETCH(oldspp);
if (sprop) {
spp = js_SearchScope(scope, sprop->id, JS_TRUE);
JS_ASSERT(SPROP_IS_FREE(*spp));
*spp = sprop;
}
oldsize--;
}
/* Finally, free the old table storage. */
JS_free(cx, oldtable);
return JS_TRUE;
}
/*
* Take care to exclude the mark and duplicate bits, in case we're called from
* the GC, or we are searching for a property that has not yet been flagged as
* a duplicate when making a duplicate formal parameter.
*/
#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_IS_DUPLICATE)
JS_STATIC_DLL_CALLBACK(JSDHashNumber)
js_HashScopeProperty(JSDHashTable *table, const void *key)
{
const JSScopeProperty *sprop = (const JSScopeProperty *)key;
JSDHashNumber hash;
JSPropertyOp gsop;
/* Accumulate from least to most random so the low bits are most random. */
hash = 0;
gsop = sprop->getter;
if (gsop)
hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop;
gsop = sprop->setter;
if (gsop)
hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop;
hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4)
^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED);
hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->attrs;
hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->shortid;
hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->slot;
hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->id;
return hash;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -