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:
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user