Bug 331456: Cache of deflated string bytes is per runtime now. To preserve

API compatibility JS_GetStringBytes(JSString *str) calls newly introduced
js_GetGCStringRuntime(JSString *str) to extract JSRuntime* instance based
on the layout of GC structures. r=brendan
This commit is contained in:
igor%mir2.org 2006-03-25 22:55:00 +00:00
parent ee372ff167
commit dc37a48f53
10 changed files with 107 additions and 89 deletions

View File

@ -663,8 +663,6 @@ JS_NewRuntime(uint32 maxbytes)
JS_END_MACRO;
#endif /* DEBUG */
if (!js_InitStringGlobals())
return NULL;
rt = (JSRuntime *) malloc(sizeof(JSRuntime));
if (!rt)
return NULL;
@ -764,7 +762,6 @@ JS_ShutDown(void)
{
JS_ArenaShutDown();
js_FinishDtoa();
js_FreeStringGlobals();
#ifdef JS_THREADSAFE
js_CleanupLocks();
#endif
@ -4259,7 +4256,7 @@ JS_NewString(JSContext *cx, char *bytes, size_t nbytes)
}
/* Hand off bytes to the deflated string cache, if possible. */
if (!js_SetStringBytes(str, bytes, nbytes))
if (!js_SetStringBytes(cx->runtime, str, bytes, nbytes))
JS_free(cx, bytes);
return str;
}
@ -4356,9 +4353,11 @@ JS_InternUCString(JSContext *cx, const jschar *s)
JS_PUBLIC_API(char *)
JS_GetStringBytes(JSString *str)
{
JSRuntime *rt;
char *bytes;
bytes = js_GetStringBytes(str);
rt = js_GetGCStringRuntime(str);
bytes = js_GetStringBytes(rt, str);
return bytes ? bytes : "";
}

View File

@ -134,6 +134,14 @@ struct JSRuntime {
jsdouble *jsNegativeInfinity;
jsdouble *jsPositiveInfinity;
#ifdef JS_THREADSAFE
JSLock *deflatedStringCacheLock;
#endif
JSHashTable *deflatedStringCache;
#ifdef DEBUG
uint32 deflatedStringCacheBytes;
#endif
/* Empty string held for use by this runtime's contexts. */
JSString *emptyString;

View File

@ -1116,7 +1116,7 @@ js_ReportUncaughtException(JSContext *cx)
} else {
if (vp)
vp[1] = STRING_TO_JSVAL(str);
bytes = js_GetStringBytes(str);
bytes = js_GetStringBytes(cx->runtime, str);
}
ok = JS_TRUE;

View File

@ -198,6 +198,9 @@ struct JSGCArena {
#define PAGE_INDEX(pi) \
((size_t)((pi)->offsetInArena >> GC_PAGE_SHIFT))
#define THING_TO_PAGE(thing) \
((JSGCPageInfo *)((jsuword)(thing) & ~GC_PAGE_MASK))
/*
* Given a thing size n, return the size of the gap from the page start before
* the first thing. We know that any n not a power of two packs from
@ -299,17 +302,33 @@ FinishGCArenaList(JSGCArenaList *arenaList)
uint8 *
js_GetGCThingFlags(void *thing)
{
jsuword pageAddress, offsetInArena, thingIndex;
JSGCPageInfo *pi;
jsuword offsetInArena, thingIndex;
pageAddress = (jsuword)thing & ~GC_PAGE_MASK;
offsetInArena = ((JSGCPageInfo *)pageAddress)->offsetInArena;
pi = THING_TO_PAGE(thing);
offsetInArena = pi->offsetInArena;
JS_ASSERT(offsetInArena < GC_THINGS_SIZE);
thingIndex = ((offsetInArena & ~GC_PAGE_MASK) |
((jsuword)thing & GC_PAGE_MASK)) / sizeof(JSGCThing);
JS_ASSERT(thingIndex < GC_PAGE_SIZE);
if (thingIndex >= (offsetInArena & GC_PAGE_MASK))
thingIndex += GC_THINGS_SIZE;
return (uint8 *)(pageAddress - offsetInArena + thingIndex);
return (uint8 *)pi - offsetInArena + thingIndex;
}
JSRuntime*
js_GetGCStringRuntime(JSString *str)
{
JSGCPageInfo *pi;
JSGCArenaList *list;
pi = THING_TO_PAGE(str);
list = PAGE_TO_ARENA(pi)->list;
JS_ASSERT(list->thingSize == sizeof(JSGCThing));
JS_ASSERT(GC_FREELIST_INDEX(sizeof(JSGCThing)) == 0);
return (JSRuntime *)((uint8 *)list - offsetof(JSRuntime, gcArenaList));
}
JSBool
@ -1425,7 +1444,7 @@ AddThingToUnscannedBag(JSRuntime *rt, void *thing, uint8 *flagp)
rt->gcStats.maxunscanned = rt->gcUnscannedBagSize);
#endif
pi = (JSGCPageInfo *) ((jsuword)thing & ~GC_PAGE_MASK);
pi = THING_TO_PAGE(thing);
arena = PAGE_TO_ARENA(pi);
thingSize = arena->list->thingSize;
GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap);
@ -2106,7 +2125,7 @@ restart:
thing = (JSGCThing *)(firstPage + offset);
*flagp = (uint8)(flags | GCF_FINAL);
if (type >= GCX_EXTERNAL_STRING)
js_PurgeDeflatedStringCache((JSString *)thing);
js_PurgeDeflatedStringCache(rt, (JSString *)thing);
finalizer(cx, thing);
}

View File

@ -81,6 +81,13 @@ JS_BEGIN_EXTERN_C
extern uint8 *
js_GetGCThingFlags(void *thing);
/*
* The sole purpose of the function is to preserve public API compatibility
* in JS_GetStringBytes which takes only single JSString* argument.
*/
JSRuntime*
js_GetGCStringRuntime(JSString *str);
/* These are compatible with JSDHashEntryStub. */
struct JSGCRootHashEntry {
JSDHashEntryHdr hdr;

View File

@ -309,7 +309,7 @@ num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
return JS_FALSE;
JS_ASSERT(JSVAL_IS_STRING(*rval));
numStr = JSVAL_TO_STRING(*rval);
num = js_GetStringBytes(numStr);
num = js_GetStringBytes(cx->runtime, numStr);
/* Find bit before the decimal. */
dec = strchr(num, '.');

View File

@ -633,7 +633,7 @@ ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
fun = cx->fp->fun;
if (fun->atom) {
char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));
char *name = js_GetStringBytes(cx->runtime, ATOM_TO_STRING(fun->atom));
ok = js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS |
JSREPORT_WARNING |

View File

@ -2426,43 +2426,6 @@ static JSFunctionSpec string_static_methods[] = {
{0,0,0,0,0}
};
static JSHashTable *deflated_string_cache;
#ifdef DEBUG
static uint32 deflated_string_cache_bytes;
#endif
#ifdef JS_THREADSAFE
static JSLock *deflated_string_cache_lock;
#endif
JSBool
js_InitStringGlobals(void)
{
#ifdef JS_THREADSAFE
/* Must come through here once in primordial thread to init safely! */
if (!deflated_string_cache_lock) {
deflated_string_cache_lock = JS_NEW_LOCK();
if (!deflated_string_cache_lock)
return JS_FALSE;
}
#endif
return JS_TRUE;
}
void
js_FreeStringGlobals()
{
if (deflated_string_cache) {
JS_HashTableDestroy(deflated_string_cache);
deflated_string_cache = NULL;
}
#ifdef JS_THREADSAFE
if (deflated_string_cache_lock) {
JS_DESTROY_LOCK(deflated_string_cache_lock);
deflated_string_cache_lock = NULL;
}
#endif
}
JSBool
js_InitRuntimeStringState(JSContext *cx)
{
@ -2471,21 +2434,38 @@ js_InitRuntimeStringState(JSContext *cx)
JSAtom *atom;
rt = cx->runtime;
JS_ASSERT(!rt->emptyString);
/* Initialize string cache */
#ifdef JS_THREADSAFE
JS_ASSERT(!rt->deflatedStringCacheLock);
rt->deflatedStringCacheLock = JS_NEW_LOCK();
if (!rt->deflatedStringCacheLock)
return JS_FALSE;
#endif
/* Make a permanently locked empty string. */
JS_ASSERT(!rt->emptyString);
empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK);
if (!empty)
return JS_FALSE;
goto bad;
/* Atomize it for scripts that use '' + x to convert x to string. */
atom = js_AtomizeString(cx, empty, ATOM_PINNED);
if (!atom)
return JS_FALSE;
goto bad;
rt->emptyString = empty;
rt->atomState.emptyAtom = atom;
return JS_TRUE;
bad:
#ifdef JS_THREADSAFE
JS_DESTROY_LOCK(rt->deflatedStringCacheLock);
rt->deflatedStringCacheLock = NULL;
#endif
return JS_FALSE;
}
void
@ -2495,6 +2475,17 @@ js_FinishRuntimeStringState(JSContext *cx)
js_UnlockGCThingRT(rt, rt->emptyString);
rt->emptyString = NULL;
if (rt->deflatedStringCache) {
JS_HashTableDestroy(rt->deflatedStringCache);
rt->deflatedStringCache = NULL;
}
#ifdef JS_THREADSAFE
if (rt->deflatedStringCacheLock) {
JS_DESTROY_LOCK(rt->deflatedStringCacheLock);
rt->deflatedStringCacheLock = NULL;
}
#endif
}
JSObject *
@ -2673,26 +2664,26 @@ js_hash_string_pointer(const void *key)
}
void
js_PurgeDeflatedStringCache(JSString *str)
js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str)
{
JSHashNumber hash;
JSHashEntry *he, **hep;
if (!deflated_string_cache)
if (!rt->deflatedStringCache)
return;
hash = js_hash_string_pointer(str);
JS_ACQUIRE_LOCK(deflated_string_cache_lock);
hep = JS_HashTableRawLookup(deflated_string_cache, hash, str);
JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock);
hep = JS_HashTableRawLookup(rt->deflatedStringCache, hash, str);
he = *hep;
if (he) {
#ifdef DEBUG
deflated_string_cache_bytes -= JSSTRING_LENGTH(str);
rt->deflatedStringCacheBytes -= JSSTRING_LENGTH(str);
#endif
free(he->value);
JS_HashTableRawRemove(deflated_string_cache, hep, he);
JS_HashTableRawRemove(rt->deflatedStringCache, hep, he);
}
JS_RELEASE_LOCK(deflated_string_cache_lock);
JS_RELEASE_LOCK(rt->deflatedStringCacheLock);
}
void
@ -2719,7 +2710,7 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str)
free(str->chars);
}
if (valid) {
js_PurgeDeflatedStringCache(str);
js_PurgeDeflatedStringCache(rt, str);
str->chars = NULL;
}
str->length = 0;
@ -2749,7 +2740,7 @@ js_ValueToPrintableString(JSContext *cx, jsval v)
str = js_QuoteString(cx, str, 0);
if (!str)
return NULL;
bytes = js_GetStringBytes(str);
bytes = js_GetStringBytes(cx->runtime, str);
if (!bytes)
JS_ReportOutOfMemory(cx);
return bytes;
@ -3189,31 +3180,31 @@ js_DeflateString(JSContext *cx, const jschar *chars, size_t length)
#endif
static JSHashTable *
GetDeflatedStringCache(void)
GetDeflatedStringCache(JSRuntime *rt)
{
JSHashTable *cache;
cache = deflated_string_cache;
cache = rt->deflatedStringCache;
if (!cache) {
cache = JS_NewHashTable(8, js_hash_string_pointer,
JS_CompareValues, JS_CompareValues,
NULL, NULL);
deflated_string_cache = cache;
rt->deflatedStringCache = cache;
}
return cache;
}
JSBool
js_SetStringBytes(JSString *str, char *bytes, size_t length)
js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length)
{
JSHashTable *cache;
JSBool ok;
JSHashNumber hash;
JSHashEntry **hep;
JS_ACQUIRE_LOCK(deflated_string_cache_lock);
JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock);
cache = GetDeflatedStringCache();
cache = GetDeflatedStringCache(rt);
if (!cache) {
ok = JS_FALSE;
} else {
@ -3223,25 +3214,25 @@ js_SetStringBytes(JSString *str, char *bytes, size_t length)
ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL;
#ifdef DEBUG
if (ok)
deflated_string_cache_bytes += length;
rt->deflatedStringCacheBytes += length;
#endif
}
JS_RELEASE_LOCK(deflated_string_cache_lock);
JS_RELEASE_LOCK(rt->deflatedStringCacheLock);
return ok;
}
char *
js_GetStringBytes(JSString *str)
js_GetStringBytes(JSRuntime *rt, JSString *str)
{
JSHashTable *cache;
char *bytes;
JSHashNumber hash;
JSHashEntry *he, **hep;
JS_ACQUIRE_LOCK(deflated_string_cache_lock);
JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock);
cache = GetDeflatedStringCache();
cache = GetDeflatedStringCache(rt);
if (!cache) {
bytes = NULL;
} else {
@ -3260,7 +3251,7 @@ js_GetStringBytes(JSString *str)
if (bytes) {
if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) {
#ifdef DEBUG
deflated_string_cache_bytes += JSSTRING_LENGTH(str);
rt->deflatedStringCacheBytes += JSSTRING_LENGTH(str);
#endif
} else {
free(bytes);
@ -3270,7 +3261,7 @@ js_GetStringBytes(JSString *str)
}
}
JS_RELEASE_LOCK(deflated_string_cache_lock);
JS_RELEASE_LOCK(rt->deflatedStringCacheLock);
return bytes;
}

View File

@ -294,16 +294,6 @@ typedef enum JSCharType {
#define JS7_UNHEX(c) (uintN)(isdigit(c) ? (c) - '0' : 10 + tolower(c) - 'a')
#define JS7_ISLET(c) ((c) < 128 && isalpha(c))
/* Initialize truly global state associated with JS strings. */
extern JSBool
js_InitStringGlobals(void);
extern void
js_FreeStringGlobals(void);
extern void
js_PurgeDeflatedStringCache(JSString *str);
/* Initialize per-runtime string state for the first context in the runtime. */
extern JSBool
js_InitRuntimeStringState(JSContext *cx);
@ -463,14 +453,18 @@ js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t charsLength,
* successful association, false on out of memory.
*/
extern JSBool
js_SetStringBytes(JSString *str, char *bytes, size_t length);
js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length);
/*
* Find or create a deflated string cache entry for str that contains its
* characters chopped from Unicode code points into bytes.
*/
extern char *
js_GetStringBytes(JSString *str);
js_GetStringBytes(JSRuntime *rt, JSString *str);
/* Remove a deflated string cache entry associated with str if any. */
extern void
js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str);
JSBool
js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

View File

@ -7677,7 +7677,7 @@ js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
* Reallocating str (because we know it has no other references) requires
* purging any deflated string cached for it.
*/
js_PurgeDeflatedStringCache(str);
js_PurgeDeflatedStringCache(cx->runtime, str);
str->chars = chars;
str->length = newlen;