Fix O(n^2) growth bug in js_GetSrcNote (347306, r=igor).

git-svn-id: svn://10.0.0.236/trunk@213295 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
brendan%mozilla.org
2006-10-07 19:14:55 +00:00
parent c347bdb40b
commit 8fc53af0eb
5 changed files with 111 additions and 11 deletions

View File

@@ -439,6 +439,8 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
JS_free(cx, lrs);
}
JS_CLEAR_GSN_CACHE(cx);
#ifdef JS_THREADSAFE
js_ClearContextThread(cx);
#endif

View File

@@ -1,4 +1,5 @@
/* -*- 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
@@ -694,9 +695,37 @@ struct JSContext {
/* Top of the GC mark stack. */
void *gcCurrentMarkNode;
#endif
/*
* js_GetSrcNote cache to avoid O(n^2) growth in finding a source note for
* a given pc in a script.
*/
struct JSGSNCache {
JSScript *script;
JSDHashTable table;
#ifdef JS_GSNMETER
uint32 hits;
uint32 misses;
uint32 fills;
uint32 clears;
# define GSN_CACHE_METER(cx,cnt) (++(cx)->gsnCache.cnt)
#else
# define GSN_CACHE_METER(cx,cnt) /* nothing */
#endif
} gsnCache;
};
#define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0)
#define JS_CLEAR_GSN_CACHE(cx) \
JS_BEGIN_MACRO \
(cx)->gsnCache.script = NULL; \
if ((cx)->gsnCache.table.ops) { \
JS_DHashTableFinish(&(cx)->gsnCache.table); \
(cx)->gsnCache.table.ops = NULL; \
} \
GSN_CACHE_METER(cx, clears); \
JS_END_MACRO
#define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0)
#ifdef __cplusplus
/* FIXME(bug 332648): Move this into a public header. */

View File

@@ -2723,6 +2723,9 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
* Set all thread local freelists to NULL. We may visit a thread's
* freelist more than once. To avoid redundant clearing we unroll the
* current thread's step.
*
* Also, in case a JSScript wrapped in an object was finalized, we clear
* the acx->gsnCache.script pointer and finish the cache's hashtable.
*/
memset(cx->thread->gcFreeLists, 0, sizeof cx->thread->gcFreeLists);
iter = NULL;
@@ -2730,6 +2733,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
if (!acx->thread || acx->thread == cx->thread)
continue;
memset(acx->thread->gcFreeLists, 0, sizeof acx->thread->gcFreeLists);
JS_CLEAR_GSN_CACHE(acx);
}
#endif

View File

@@ -1393,6 +1393,8 @@ js_DestroyScript(JSContext *cx, JSScript *script)
js_FreeAtomMap(cx, &script->atomMap);
if (script->principals)
JSPRINCIPALS_DROP(cx, script->principals);
if (cx->gsnCache.script == script)
JS_CLEAR_GSN_CACHE(cx);
JS_free(cx, script);
}
@@ -1413,22 +1415,77 @@ js_MarkScript(JSContext *cx, JSScript *script)
js_MarkScriptFilename(script->filename);
}
typedef struct GSNCacheEntry {
JSDHashEntryHdr hdr;
jsbytecode *pc;
jssrcnote *sn;
} GSNCacheEntry;
#define GSN_CACHE_THRESHOLD 100
jssrcnote *
js_GetSrcNote(JSScript *script, jsbytecode *pc)
js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
{
jssrcnote *sn;
ptrdiff_t offset, target;
ptrdiff_t target, offset;
GSNCacheEntry *entry;
jssrcnote *sn, *result;
uintN nsrcnotes;
target = PTRDIFF(pc, script->code, jsbytecode);
if ((uint32)target >= script->length)
return NULL;
offset = 0;
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
offset += SN_DELTA(sn);
if (offset == target && SN_IS_GETTABLE(sn))
return sn;
if (cx->gsnCache.script == script) {
GSN_CACHE_METER(cx, hits);
entry = (GSNCacheEntry *)
JS_DHashTableOperate(&cx->gsnCache.table, pc, JS_DHASH_LOOKUP);
return entry->sn;
}
return NULL;
GSN_CACHE_METER(cx, misses);
offset = 0;
for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) {
if (SN_IS_TERMINATOR(sn)) {
result = NULL;
break;
}
offset += SN_DELTA(sn);
if (offset == target && SN_IS_GETTABLE(sn)) {
result = sn;
break;
}
}
if (cx->gsnCache.script != script &&
script->length >= GSN_CACHE_THRESHOLD) {
JS_CLEAR_GSN_CACHE(cx);
nsrcnotes = 0;
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
sn = SN_NEXT(sn)) {
if (SN_IS_GETTABLE(sn))
++nsrcnotes;
}
if (JS_DHashTableInit(&cx->gsnCache.table, JS_DHashGetStubOps(), NULL,
sizeof(GSNCacheEntry), nsrcnotes)) {
pc = script->code;
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
sn = SN_NEXT(sn)) {
pc += SN_DELTA(sn);
if (SN_IS_GETTABLE(sn)) {
entry = (GSNCacheEntry *)
JS_DHashTableOperate(&cx->gsnCache.table, pc,
JS_DHASH_ADD);
entry->pc = pc;
entry->sn = sn;
}
}
cx->gsnCache.script = script;
GSN_CACHE_METER(cx, fills);
}
}
return result;
}
uintN

View File

@@ -1,4 +1,5 @@
/* -*- 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
@@ -187,8 +188,15 @@ js_DestroyScript(JSContext *cx, JSScript *script);
extern void
js_MarkScript(JSContext *cx, JSScript *script);
/*
* To perturb as little code as possible, we introduce a js_GetSrcNote lookup
* cache without adding an explicit cx parameter. Thus js_GetSrcNote becomes
* a macro that uses cx from its calls' lexical environments.
*/
#define js_GetSrcNote(script,pc) js_GetSrcNoteCached(cx, script, pc)
extern jssrcnote *
js_GetSrcNote(JSScript *script, jsbytecode *pc);
js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc);
/* XXX need cx to lock function objects declared by prolog bytecodes. */
extern uintN