зеркало из https://github.com/mozilla/pjs.git
Bug 393368: new API to limit heap consumption by stack-like data structures used by compiler, decompiler and interpreter.
This commit is contained in:
Родитель
4ded1d172a
Коммит
6cc27f8fb6
35
js/src/js.c
35
js/src/js.c
|
@ -98,8 +98,10 @@ size_t gStackChunkSize = 8192;
|
|||
|
||||
/* Assume that we can not use more than 5e5 bytes of C stack by default. */
|
||||
static size_t gMaxStackSize = 500000;
|
||||
|
||||
static jsuword gStackBase;
|
||||
|
||||
static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
|
||||
|
||||
int gExitCode = 0;
|
||||
JSBool gQuitting = JS_FALSE;
|
||||
FILE *gErrFile = NULL;
|
||||
|
@ -1750,6 +1752,21 @@ StringsAreUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
StackQuota(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
uint32 n;
|
||||
|
||||
if (argc == 0)
|
||||
return JS_NewNumberValue(cx, (double) gScriptStackQuota, vp);
|
||||
if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[0], &n))
|
||||
return JS_FALSE;
|
||||
gScriptStackQuota = n;
|
||||
JS_SetScriptStackQuota(cx, gScriptStackQuota);
|
||||
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static const char* badUTF8 = "...\xC0...";
|
||||
static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
|
||||
static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
|
||||
|
@ -2222,6 +2239,7 @@ static JSFunctionSpec shell_functions[] = {
|
|||
JS_FS("untrap", Untrap, 2,0,0),
|
||||
JS_FS("line2pc", LineToPC, 0,0,0),
|
||||
JS_FS("pc2line", PCToLine, 0,0,0),
|
||||
JS_FN("stackQuota", StackQuota, 0,0,0,0),
|
||||
JS_FS("stringsAreUTF8", StringsAreUTF8, 0,0,0),
|
||||
JS_FS("testUTF8", TestUTF8, 1,0,0),
|
||||
JS_FS("throwError", ThrowError, 0,0,0),
|
||||
|
@ -2269,6 +2287,7 @@ static char *shell_help_messages[] = {
|
|||
"untrap(fun[, pc]) Remove a trap",
|
||||
"line2pc([fun,] line) Map line number to PC",
|
||||
"pc2line(fun[, pc]) Map PC to line number",
|
||||
"stackQuota([number]) Query/set script stack quota",
|
||||
"stringsAreUTF8() Check if strings are UTF-8 encoded",
|
||||
"testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)",
|
||||
"throwError() Throw an error from JS_ReportError",
|
||||
|
@ -3122,6 +3141,17 @@ snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
|
||||
#endif /* NARCISSUS */
|
||||
|
||||
JSBool
|
||||
ContextCallback(JSContext *cx, uintN contextOp)
|
||||
{
|
||||
if (contextOp == JSCONTEXT_NEW) {
|
||||
JS_SetErrorReporter(cx, my_ErrorReporter);
|
||||
JS_SetVersion(cx, JSVERSION_LATEST);
|
||||
JS_SetScriptStackQuota(cx, gScriptStackQuota);
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv, char **envp)
|
||||
{
|
||||
|
@ -3157,12 +3187,11 @@ main(int argc, char **argv, char **envp)
|
|||
rt = JS_NewRuntime(64L * 1024L * 1024L);
|
||||
if (!rt)
|
||||
return 1;
|
||||
JS_SetContextCallback(rt, ContextCallback);
|
||||
|
||||
cx = JS_NewContext(rt, gStackChunkSize);
|
||||
if (!cx)
|
||||
return 1;
|
||||
JS_SetErrorReporter(cx, my_ErrorReporter);
|
||||
JS_SetVersion(cx, JSVERSION_LATEST);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_BeginRequest(cx);
|
||||
|
|
|
@ -2540,6 +2540,12 @@ JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr)
|
|||
cx->stackLimit = limitAddr;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetScriptStackQuota(JSContext *cx, size_t quota)
|
||||
{
|
||||
cx->scriptStackQuota = quota;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
@ -4334,8 +4340,10 @@ CompileTokenStream(JSContext *cx, JSObject *obj, JSTokenStream *ts,
|
|||
JSScript *script;
|
||||
|
||||
eof = JS_FALSE;
|
||||
JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode));
|
||||
JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote));
|
||||
JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
|
||||
&cx->scriptStackQuota);
|
||||
JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote),
|
||||
&cx->scriptStackQuota);
|
||||
js_InitParseContext(cx, &pc);
|
||||
JS_ASSERT(!ts->parseContext);
|
||||
ts->parseContext = &pc;
|
||||
|
|
|
@ -1162,6 +1162,21 @@ JS_GetExternalStringGCType(JSRuntime *rt, JSString *str);
|
|||
extern JS_PUBLIC_API(void)
|
||||
JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr);
|
||||
|
||||
/*
|
||||
* Set the quota on the number of bytes that stack-like data structures can
|
||||
* use when the runtime compiles and executes scripts. These structures
|
||||
* consume heap space, so JS_SetThreadStackLimit does not bound their size.
|
||||
* The default quota is 32MB which is quite generous.
|
||||
*
|
||||
* The function must be called before any script compilation or execution API
|
||||
* calls, i.e. either immediately after JS_NewContext or from JSCONTEXT_NEW
|
||||
* context callback.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetScriptStackQuota(JSContext *cx, size_t quota);
|
||||
|
||||
#define JS_DEFAULT_SCRIPT_STACK_QUOTA ((size_t) 0x2000000)
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
/*
|
||||
|
|
|
@ -62,7 +62,7 @@ static JSArenaStats *arena_stats_list;
|
|||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_INIT_NAMED_ARENA_POOL(JSArenaPool *pool, const char *name, size_t size,
|
||||
size_t align)
|
||||
size_t align, size_t *quotap)
|
||||
{
|
||||
if (align == 0)
|
||||
align = JS_ARENA_DEFAULT_ALIGN;
|
||||
|
@ -72,6 +72,7 @@ JS_INIT_NAMED_ARENA_POOL(JSArenaPool *pool, const char *name, size_t size,
|
|||
JS_ARENA_ALIGN(pool, &pool->first + 1);
|
||||
pool->current = &pool->first;
|
||||
pool->arenasize = size;
|
||||
pool->quotap = quotap;
|
||||
#ifdef JS_ARENAMETER
|
||||
memset(&pool->stats, 0, sizeof pool->stats);
|
||||
pool->stats.name = strdup(name);
|
||||
|
@ -156,9 +157,19 @@ JS_ArenaAllocate(JSArenaPool *pool, size_t nb)
|
|||
gross = hdrsz + JS_MAX(nb, pool->arenasize);
|
||||
if (gross < nb)
|
||||
return NULL;
|
||||
b = (JSArena *) malloc(gross);
|
||||
if (!b)
|
||||
return NULL;
|
||||
if (pool->quotap) {
|
||||
if (gross > *pool->quotap)
|
||||
return NULL;
|
||||
b = (JSArena *) malloc(gross);
|
||||
if (!b)
|
||||
return NULL;
|
||||
*pool->quotap -= gross;
|
||||
} else {
|
||||
b = (JSArena *) malloc(gross);
|
||||
if (!b)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b->next = NULL;
|
||||
b->limit = (jsuword)b + gross;
|
||||
JS_COUNT_ARENA(pool,++);
|
||||
|
@ -189,7 +200,7 @@ JS_PUBLIC_API(void *)
|
|||
JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr)
|
||||
{
|
||||
JSArena **ap, *a, *b;
|
||||
jsuword boff, aoff, extra, hdrsz, gross;
|
||||
jsuword boff, aoff, extra, hdrsz, gross, growth;
|
||||
|
||||
/*
|
||||
* Use the oversized-single-allocation header to avoid searching for ap.
|
||||
|
@ -212,9 +223,19 @@ JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr)
|
|||
hdrsz = sizeof *a + extra + pool->mask; /* header and alignment slop */
|
||||
gross = hdrsz + aoff;
|
||||
JS_ASSERT(gross > aoff);
|
||||
a = (JSArena *) realloc(a, gross);
|
||||
if (!a)
|
||||
return NULL;
|
||||
if (pool->quotap) {
|
||||
growth = gross - (a->limit - (jsuword) a);
|
||||
if (growth > *pool->quotap)
|
||||
return NULL;
|
||||
a = (JSArena *) realloc(a, gross);
|
||||
if (!a)
|
||||
return NULL;
|
||||
*pool->quotap -= growth;
|
||||
} else {
|
||||
a = (JSArena *) realloc(a, gross);
|
||||
if (!a)
|
||||
return NULL;
|
||||
}
|
||||
#ifdef JS_ARENAMETER
|
||||
pool->stats.nreallocs++;
|
||||
#endif
|
||||
|
@ -290,6 +311,8 @@ FreeArenaList(JSArenaPool *pool, JSArena *head)
|
|||
|
||||
do {
|
||||
*ap = a->next;
|
||||
if (pool->quotap)
|
||||
*pool->quotap += a->limit - (jsuword) a;
|
||||
JS_CLEAR_ARENA(a);
|
||||
JS_COUNT_ARENA(pool,--);
|
||||
free(a);
|
||||
|
@ -374,6 +397,8 @@ JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size)
|
|||
JS_ASSERT(GET_HEADER(pool, b) == &a->next);
|
||||
SET_HEADER(pool, b, ap);
|
||||
}
|
||||
if (pool->quotap)
|
||||
*pool->quotap += a->limit - (jsuword) a;
|
||||
JS_CLEAR_ARENA(a);
|
||||
JS_COUNT_ARENA(pool,--);
|
||||
free(a);
|
||||
|
|
|
@ -88,17 +88,19 @@ struct JSArenaPool {
|
|||
JSArena *current; /* arena from which to allocate space */
|
||||
size_t arenasize; /* net exact size of a new arena */
|
||||
jsuword mask; /* alignment mask (power-of-2 - 1) */
|
||||
size_t *quotap; /* pointer to the quota on pool allocation
|
||||
size or null if pool is unlimited */
|
||||
#ifdef JS_ARENAMETER
|
||||
JSArenaStats stats;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef JS_ARENAMETER
|
||||
#define JS_INIT_NAMED_ARENA_POOL(pool, name, size, align) \
|
||||
JS_InitArenaPool(pool, name, size, align)
|
||||
#define JS_INIT_NAMED_ARENA_POOL(pool, name, size, align, quotap) \
|
||||
JS_InitArenaPool(pool, name, size, align, quotap)
|
||||
#else
|
||||
#define JS_INIT_NAMED_ARENA_POOL(pool, name, size, align) \
|
||||
JS_InitArenaPool(pool, size, align)
|
||||
#define JS_INIT_NAMED_ARENA_POOL(pool, name, size, align, quotap) \
|
||||
JS_InitArenaPool(pool, size, align, quotap)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -110,14 +112,15 @@ struct JSArenaPool {
|
|||
#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + JS_ARENA_CONST_ALIGN_MASK) \
|
||||
& ~(jsuword)JS_ARENA_CONST_ALIGN_MASK)
|
||||
|
||||
#define JS_INIT_ARENA_POOL(pool, name, size) \
|
||||
JS_INIT_NAMED_ARENA_POOL(pool, name, size, JS_ARENA_CONST_ALIGN_MASK + 1)
|
||||
#define JS_INIT_ARENA_POOL(pool, name, size, quotap) \
|
||||
JS_INIT_NAMED_ARENA_POOL(pool, name, size, JS_ARENA_CONST_ALIGN_MASK + 1, \
|
||||
quotap)
|
||||
|
||||
#else
|
||||
#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask)
|
||||
|
||||
#define JS_INIT_ARENA_POOL(pool, name, size, align) \
|
||||
JS_INIT_NAMED_ARENA_POOL(pool, name, size, align)
|
||||
#define JS_INIT_ARENA_POOL(pool, name, size, align, quotap) \
|
||||
JS_INIT_NAMED_ARENA_POOL(pool, name, size, align, quotap)
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -235,7 +238,7 @@ struct JSArenaPool {
|
|||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_INIT_NAMED_ARENA_POOL(JSArenaPool *pool, const char *name, size_t size,
|
||||
size_t align);
|
||||
size_t align, size_t *quotap);
|
||||
|
||||
/*
|
||||
* Free the arenas in pool. The user may continue to allocate from pool
|
||||
|
|
|
@ -229,6 +229,7 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
|||
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||
cx->stackLimit = (jsuword)-1;
|
||||
#endif
|
||||
cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_INIT_CLIST(&cx->threadLinks);
|
||||
js_SetContextThread(cx);
|
||||
|
@ -259,8 +260,10 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
|||
* done by js_DestroyContext).
|
||||
*/
|
||||
cx->version = JSVERSION_DEFAULT;
|
||||
JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval));
|
||||
JS_INIT_ARENA_POOL(&cx->tempPool, "temp", 1024, sizeof(jsdouble));
|
||||
JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval),
|
||||
&cx->scriptStackQuota);
|
||||
JS_INIT_ARENA_POOL(&cx->tempPool, "temp", 1024, sizeof(jsdouble),
|
||||
&cx->scriptStackQuota);
|
||||
|
||||
if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
|
||||
js_DestroyContext(cx, JSDCM_NEW_FAILED);
|
||||
|
|
|
@ -689,9 +689,12 @@ struct JSContext {
|
|||
JSPackedBool throwing; /* is there a pending exception? */
|
||||
jsval exception; /* most-recently-thrown exception */
|
||||
|
||||
/* Limit pointer for checking stack consumption during recursion. */
|
||||
/* Limit pointer for checking native stack consumption during recursion. */
|
||||
jsuword stackLimit;
|
||||
|
||||
/* Quota on the size of arenas used to compile and execute scripts. */
|
||||
size_t scriptStackQuota;
|
||||
|
||||
/* Data shared by threads in an address space. */
|
||||
JSRuntime *runtime;
|
||||
|
||||
|
|
|
@ -673,7 +673,7 @@ JS_NEW_PRINTER(JSContext *cx, const char *name, uintN indent, JSBool pretty)
|
|||
if (!jp)
|
||||
return NULL;
|
||||
INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
|
||||
JS_INIT_ARENA_POOL(&jp->pool, name, 256, 1);
|
||||
JS_INIT_ARENA_POOL(&jp->pool, name, 256, 1, &cx->scriptStackQuota);
|
||||
jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
|
||||
jp->pretty = pretty;
|
||||
jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
|
||||
|
|
|
@ -879,8 +879,10 @@ js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
|
|||
JSObject *funobj;
|
||||
JSParseNode *pn;
|
||||
|
||||
JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode));
|
||||
JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote));
|
||||
JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
|
||||
&cx->scriptStackQuota);
|
||||
JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote),
|
||||
&cx->scriptStackQuota);
|
||||
js_InitParseContext(cx, &pc);
|
||||
JS_ASSERT(!ts->parseContext);
|
||||
ts->parseContext = &pc;
|
||||
|
|
|
@ -3382,7 +3382,8 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
|
|||
gData.start = start;
|
||||
gData.skipped = 0;
|
||||
|
||||
JS_INIT_ARENA_POOL(&gData.pool, "RegExpPool", 8096, 4);
|
||||
JS_INIT_ARENA_POOL(&gData.pool, "RegExpPool", 8096, 4,
|
||||
&cx->scriptStackQuota);
|
||||
x = InitMatch(cx, &gData, re, length);
|
||||
|
||||
if (!x) {
|
||||
|
|
|
@ -1950,7 +1950,7 @@ js_InitPropertyTree(JSRuntime *rt)
|
|||
return JS_FALSE;
|
||||
}
|
||||
JS_INIT_ARENA_POOL(&rt->propertyArenaPool, "properties",
|
||||
256 * sizeof(JSScopeProperty), sizeof(void *));
|
||||
256 * sizeof(JSScopeProperty), sizeof(void *), NULL);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче