📄 jsscope.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** 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 voidInitMinimalScope(JSScope *scope){ scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; scope->entryCount = scope->removedCount = 0; scope->table = NULL; scope->lastProp = NULL;}static JSBoolCreateScopeTable(JSContext *cx, JSScope *scope, JSBool report){ 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) { if (report) JS_ReportOutOfMemory(cx); return JS_FALSE; } js_UpdateMallocCounter(cx, JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); 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_COUNTextern voidjs_unlog_scope(JSScope *scope);#endifvoidjs_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 DUMP_SCOPE_STATStypedef 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 JSBoolChangeScope(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; /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */ cx->runtime->gcMallocBytes += nbytes;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -