diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 2980b9560af5..68bc9e179032 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -660,7 +660,7 @@ check-malloc-function-usage: $(filter-out %jsalloc.h %jscntxt.h %jsutil.h, $(ALL # We desire these numbers to go down, not up. See "User guide to memory # management within SpiderMonkey" in jsutil.h. - $(srcdir)/config/check_source_count.py OffTheBooks:: 54 \ + $(srcdir)/config/check_source_count.py OffTheBooks:: 52 \ "in Makefile.in" "{cx,rt}->{new_,new_array,malloc_,calloc_,realloc_}" $^ # This should go to zero, if possible. $(srcdir)/config/check_source_count.py UnwantedForeground:: 33 \ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ebd1f44b25b3..54df812f1ab9 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -695,8 +695,6 @@ JSRuntime::init(uint32 maxbytes) return false; if (!InitRuntimeNumberState(this)) return false; - if (!InitRuntimeScriptState(this)) - return false; return true; } @@ -724,7 +722,6 @@ JSRuntime::~JSRuntime() FinishJIT(); #endif - FreeRuntimeScriptState(this); FinishRuntimeNumberState(this); js_FinishThreads(this); js_FinishAtomState(this); diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 371935f73a16..583afb9f3c19 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -131,6 +131,9 @@ JSCompartment::init() if (!crossCompartmentWrappers.init()) return false; + if (!scriptFilenameTable.init()) + return false; + regExpAllocator = rt->new_(); if (!regExpAllocator) return false; diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 13e52f1485a4..79e46b12b851 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -363,6 +363,25 @@ class DtoaCache { }; +struct ScriptFilenameEntry +{ + bool marked; + char filename[1]; +}; + +struct ScriptFilenameHasher +{ + typedef const char *Lookup; + static HashNumber hash(const char *l) { return JS_HashString(l); } + static bool match(const ScriptFilenameEntry *e, const char *l) { + return strcmp(e->filename, l) == 0; + } +}; + +typedef HashSet ScriptFilenameTable; + } /* namespace js */ struct JS_FRIEND_API(JSCompartment) { @@ -473,6 +492,8 @@ struct JS_FRIEND_API(JSCompartment) { typedef js::Maybe LazyToSourceCache; LazyToSourceCache toSourceCache; + js::ScriptFilenameTable scriptFilenameTable; + JSCompartment(JSRuntime *rt); ~JSCompartment(); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 8c46bc58d1d1..668e4ea4e235 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1820,6 +1820,17 @@ MarkContext(JSTracer *trc, JSContext *acx) MarkValue(trc, acx->iterValue, "iterValue"); } +#define PER_COMPARTMENT_OP(rt, op) \ + if ((rt)->gcCurrentCompartment) { \ + JSCompartment *c = (rt)->gcCurrentCompartment; \ + op; \ + } else { \ + for (JSCompartment **i = rt->compartments.begin(); i != rt->compartments.end(); ++i) { \ + JSCompartment *c = *i; \ + op; \ + } \ + } + JS_REQUIRES_STACK void MarkRuntime(JSTracer *trc) { @@ -1842,9 +1853,7 @@ MarkRuntime(JSTracer *trc) MarkContext(trc, acx); #ifdef JS_TRACER - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - if ((*c)->hasTraceMonitor()) - (*c)->traceMonitor()->mark(trc); + PER_COMPARTMENT_OP(rt, if (c->hasTraceMonitor()) c->traceMonitor()->mark(trc)); #endif for (ThreadDataIter i(rt); !i.empty(); i.popFront()) @@ -2250,12 +2259,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM rt->protoHazardShape = 0; } - if (rt->gcCurrentCompartment) { - rt->gcCurrentCompartment->purge(cx); - } else { - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->purge(cx); - } + PER_COMPARTMENT_OP(rt, c->purge(cx)); js_PurgeThreads(cx); { @@ -2284,8 +2288,6 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM if (comp) { for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) (*c)->markCrossCompartmentWrappers(&gcmarker); - } else { - js_MarkScriptFilenames(rt); } MarkRuntime(&gcmarker); @@ -2385,17 +2387,17 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM PropertyTree::dumpShapes(cx); #endif + /* + * Sweep script filenames after sweeping functions in the generic loop + * above. In this way when a scripted function's finalizer destroys the + * script and calls rt->destroyScriptHook, the hook can still access the + * script's filename. See bug 323267. + */ + PER_COMPARTMENT_OP(rt, js_SweepScriptFilenames(c)); + if (!comp) { SweepCompartments(cx, gckind); - /* - * Sweep script filenames after sweeping functions in the generic loop - * above. In this way when a scripted function's finalizer destroys the - * script and calls rt->destroyScriptHook, the hook can still access the - * script's filename. See bug 323267. - */ - js_SweepScriptFilenames(rt); - /* non-compartmental sweep pieces */ Probes::GCEndSweepPhase(NULL); } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 6ec9ca701068..d8a276ed2cfa 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -783,112 +783,29 @@ Class js_ScriptClass = { /* * Shared script filename management. */ -static int -js_compare_strings(const void *k1, const void *k2) -{ - return strcmp((const char *) k1, (const char *) k2) == 0; -} - -/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ -typedef struct ScriptFilenameEntry { - JSHashEntry *next; /* hash chain linkage */ - JSHashNumber keyHash; /* key hash function result */ - const void *key; /* ptr to filename, below */ - JSPackedBool mark; /* GC mark flag */ - char filename[3]; /* two or more bytes, NUL-terminated */ -} ScriptFilenameEntry; - -static void * -js_alloc_table_space(void *priv, size_t size) -{ - return OffTheBooks::malloc_(size); -} - -static void -js_free_table_space(void *priv, void *item, size_t size) -{ - UnwantedForeground::free_(item); -} - -static JSHashEntry * -js_alloc_sftbl_entry(void *priv, const void *key) -{ - size_t nbytes = offsetof(ScriptFilenameEntry, filename) + - strlen((const char *) key) + 1; - - return (JSHashEntry *) OffTheBooks::malloc_(JS_MAX(nbytes, sizeof(JSHashEntry))); -} - -static void -js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) -{ - if (flag != HT_FREE_ENTRY) - return; - UnwantedForeground::free_(he); -} - -static JSHashAllocOps sftbl_alloc_ops = { - js_alloc_table_space, js_free_table_space, - js_alloc_sftbl_entry, js_free_sftbl_entry -}; - -namespace js { - -bool -InitRuntimeScriptState(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->scriptFilenameTableLock); - rt->scriptFilenameTableLock = JS_NEW_LOCK(); - if (!rt->scriptFilenameTableLock) - return false; -#endif - JS_ASSERT(!rt->scriptFilenameTable); - rt->scriptFilenameTable = - JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, - &sftbl_alloc_ops, NULL); - if (!rt->scriptFilenameTable) - return false; - - return true; -} - -void -FreeRuntimeScriptState(JSRuntime *rt) -{ - if (rt->scriptFilenameTable) - JS_HashTableDestroy(rt->scriptFilenameTable); -#ifdef JS_THREADSAFE - if (rt->scriptFilenameTableLock) - JS_DESTROY_LOCK(rt->scriptFilenameTableLock); -#endif -} - -} /* namespace js */ static const char * SaveScriptFilename(JSContext *cx, const char *filename) { - JSRuntime *rt = cx->runtime; - JS_AUTO_LOCK_GUARD(g, rt->scriptFilenameTableLock); + JSCompartment *comp = cx->compartment; - JSHashTable *table = rt->scriptFilenameTable; - JSHashNumber hash = JS_HashString(filename); - JSHashEntry **hep = JS_HashTableRawLookup(table, hash, filename); + ScriptFilenameTable::AddPtr p = comp->scriptFilenameTable.lookupForAdd(filename); + if (!p) { + size_t size = offsetof(ScriptFilenameEntry, filename) + strlen(filename) + 1; + ScriptFilenameEntry *entry = (ScriptFilenameEntry *) cx->malloc_(size); + if (!entry) + return NULL; + entry->marked = false; + strcpy(entry->filename, filename); - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) *hep; - if (!sfe) { - sfe = (ScriptFilenameEntry *) - JS_HashTableRawAdd(table, hep, hash, filename, NULL); - if (!sfe) { + if (!comp->scriptFilenameTable.add(p, entry)) { + Foreground::free_(entry); JS_ReportOutOfMemory(cx); return NULL; } - sfe->key = strcpy(sfe->filename, filename); - sfe->mark = JS_FALSE; } - return sfe->filename; + return (*p)->filename; } /* @@ -897,72 +814,28 @@ SaveScriptFilename(JSContext *cx, const char *filename) #define FILENAME_TO_SFE(fn) \ ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) -/* - * The sfe->key member, redundant given sfe->filename but required by the old - * jshash.c code, here gives us a useful sanity check. This assertion will - * very likely botch if someone tries to mark a string that wasn't allocated - * as an sfe->filename. - */ -#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) - void js_MarkScriptFilename(const char *filename) { - ScriptFilenameEntry *sfe; - - sfe = FILENAME_TO_SFE(filename); - ASSERT_VALID_SFE(sfe); - sfe->mark = JS_TRUE; -} - -static intN -js_script_filename_marker(JSHashEntry *he, intN i, void *arg) -{ - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; - - sfe->mark = JS_TRUE; - return HT_ENUMERATE_NEXT; + ScriptFilenameEntry *sfe = FILENAME_TO_SFE(filename); + sfe->marked = true; } void -js_MarkScriptFilenames(JSRuntime *rt) +js_SweepScriptFilenames(JSCompartment *comp) { - if (!rt->scriptFilenameTable) - return; - - if (rt->gcKeepAtoms) { - JS_HashTableEnumerateEntries(rt->scriptFilenameTable, - js_script_filename_marker, - rt); + ScriptFilenameTable &table = comp->scriptFilenameTable; + for (ScriptFilenameTable::Enum e(table); !e.empty(); e.popFront()) { + ScriptFilenameEntry *entry = e.front(); + if (entry->marked) { + entry->marked = false; + } else if (!comp->rt->gcKeepAtoms) { + Foreground::free_(entry); + e.removeFront(); + } } } -static intN -js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) -{ - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; - - if (!sfe->mark) - return HT_ENUMERATE_REMOVE; - sfe->mark = JS_FALSE; - return HT_ENUMERATE_NEXT; -} - -void -js_SweepScriptFilenames(JSRuntime *rt) -{ - if (!rt->scriptFilenameTable) - return; - - /* - * JS_HashTableEnumerateEntries shrinks the table if many entries are - * removed preventing wasting memory on a too sparse table. - */ - JS_HashTableEnumerateEntries(rt->scriptFilenameTable, - js_script_filename_sweeper, - rt); -} - /* * JSScript data structures memory alignment: * diff --git a/js/src/jsscript.h b/js/src/jsscript.h index dc2e540e9a11..e84bfbb31cbd 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -695,30 +695,11 @@ extern JS_FRIEND_DATA(js::Class) js_ScriptClass; extern JSObject * js_InitScriptClass(JSContext *cx, JSObject *obj); -namespace js { - -extern bool -InitRuntimeScriptState(JSRuntime *rt); - -/* - * On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any - * script filename table entries that have not been GC'd. - * - * This allows script filename prefixes to outlive any context in rt. - */ -extern void -FreeRuntimeScriptState(JSRuntime *rt); - -} /* namespace js */ - extern void js_MarkScriptFilename(const char *filename); extern void -js_MarkScriptFilenames(JSRuntime *rt); - -extern void -js_SweepScriptFilenames(JSRuntime *rt); +js_SweepScriptFilenames(JSCompartment *comp); /* * New-script-hook calling is factored from js_NewScriptFromCG so that it