--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1178,16 +1178,20 @@ JS_GetOptions(JSContext *cx)
}
#define SYNC_OPTIONS_TO_VERSION(cx) \
JS_BEGIN_MACRO \
if ((cx)->options & JSOPTION_XML) \
(cx)->version |= JSVERSION_HAS_XML; \
else \
(cx)->version &= ~JSVERSION_HAS_XML; \
+ if ((cx)->options & JSOPTION_ANONFUNFIX) \
+ (cx)->version |= JSVERSION_ANONFUNFIX; \
+ else \
+ (cx)->version &= ~JSVERSION_ANONFUNFIX; \
JS_END_MACRO
JS_PUBLIC_API(uint32)
JS_SetOptions(JSContext *cx, uint32 options)
{
uint32 oldopts = cx->options;
cx->options = options;
SYNC_OPTIONS_TO_VERSION(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -591,18 +591,20 @@ JS_StringToVersion(const char *string);
#define JSOPTION_JIT JS_BIT(11) /* Enable JIT compilation. */
#define JSOPTION_NO_SCRIPT_RVAL JS_BIT(12) /* A promise to the compiler
that a null rval out-param
will be passed to each call
to JS_ExecuteScript. */
#define JSOPTION_UNROOTED_GLOBAL JS_BIT(13) /* The GC will not root the
- global objects leaving
- that up to the embedding. */
+ contexts' global objects
+ (see JS_GetGlobalObject),
+ leaving that up to the
+ embedding. */
extern JS_PUBLIC_API(uint32)
JS_GetOptions(JSContext *cx);
extern JS_PUBLIC_API(uint32)
JS_SetOptions(JSContext *cx, uint32 options);
extern JS_PUBLIC_API(uint32)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -163,17 +163,17 @@ js_GetCurrentThread(JSRuntime *rt)
JS_INIT_CLIST(&thread->contextList);
thread->id = js_CurrentThreadId();
thread->gcMallocBytes = 0;
#ifdef JS_TRACER
memset(&thread->traceMonitor, 0, sizeof(thread->traceMonitor));
js_InitJIT(&thread->traceMonitor);
#endif
- thread->scriptsToGC = NULL;
+ memset(thread->scriptsToGC, 0, sizeof thread->scriptsToGC);
/*
* js_SetContextThread initializes the remaining fields as necessary.
*/
}
return thread;
}
@@ -192,18 +192,21 @@ js_SetContextThread(JSContext *cx)
return JS_FALSE;
}
/*
* Clear caches on each transition from 0 to 1 context active on the
* current thread. See bug 425828.
*/
if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
- memset(&thread->gsnCache, 0, sizeof(thread->gsnCache));
- memset(&thread->propertyCache, 0, sizeof(thread->propertyCache));
+ memset(&thread->gsnCache, 0, sizeof thread->gsnCache);
+ memset(&thread->propertyCache, 0, sizeof thread->propertyCache);
+#ifdef DEBUG
+ memset(&thread->evalCacheMeter, 0, sizeof thread->evalCacheMeter);
+#endif
}
/* Assert that the previous cx->thread called JS_ClearContextThread(). */
JS_ASSERT(!cx->thread || cx->thread == thread);
if (!cx->thread)
JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
cx->thread = thread;
return JS_TRUE;
@@ -345,16 +348,59 @@ js_NewContext(JSRuntime *rt, size_t stac
if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
js_DestroyContext(cx, JSDCM_NEW_FAILED);
return NULL;
}
return cx;
}
+#if defined DEBUG && defined XP_UNIX
+# include <stdio.h>
+
+static void
+DumpEvalCacheMeter(JSContext *cx)
+{
+ struct {
+ const char *name;
+ ptrdiff_t offset;
+ } table[] = {
+#define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
+ EVAL_CACHE_METER_LIST(frob)
+#undef frob
+ };
+ JSEvalCacheMeter *ecm = &JS_CACHE_LOCUS(cx)->evalCacheMeter;
+
+ static FILE *fp;
+ if (!fp) {
+ fp = fopen("/tmp/evalcache.stats", "w");
+ if (!fp)
+ return;
+ }
+
+ fprintf(fp, "eval cache meter (%p):\n",
+#ifdef JS_THREADSAFE
+ cx->thread
+#else
+ cx->runtime
+#endif
+ );
+ for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
+ fprintf(fp, "%-8.8s %llu\n",
+ table[i].name, *(uint64 *)((uint8 *)ecm + table[i].offset));
+ }
+ fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe);
+ fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe);
+ fflush(fp);
+}
+# define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
+#else
+# define DUMP_EVAL_CACHE_METER(cx) ((void) 0)
+#endif
+
void
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
{
JSRuntime *rt;
JSContextCallback cxCallback;
JSBool last;
JSArgumentFormatMap *map;
JSLocalRootStack *lrs;
@@ -433,16 +479,17 @@ js_DestroyContext(JSContext *cx, JSDestr
* data structure.
*/
while (cx->requestDepth != 0)
JS_EndRequest(cx);
#endif
if (last) {
js_GC(cx, GC_LAST_CONTEXT);
+ DUMP_EVAL_CACHE_METER(cx);
/*
* Free the script filename table if it exists and is empty. Do this
* after the last GC to avoid finalizers tripping on free memory.
*/
if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0)
js_FinishRuntimeScriptState(rt);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -154,16 +154,41 @@ typedef struct JSTraceMonitor {
#ifdef JS_TRACER
# define JS_ON_TRACE(cx) (JS_TRACE_MONITOR(cx).onTrace)
# define JS_EXECUTING_TRACE(cx) (JS_ON_TRACE(cx) && !JS_TRACE_MONITOR(cx).recorder)
#else
# define JS_ON_TRACE(cx) JS_FALSE
# define JS_EXECUTING_TRACE(cx) JS_FALSE
#endif
+#ifdef DEBUG
+# define JS_EVAL_CACHE_METERING 1
+#endif
+
+/* Number of potentially reusable scriptsToGC to search for the eval cache. */
+#ifndef JS_EVAL_CACHE_SHIFT
+# define JS_EVAL_CACHE_SHIFT 6
+#endif
+#define JS_EVAL_CACHE_SIZE JS_BIT(JS_EVAL_CACHE_SHIFT)
+
+#ifdef JS_EVAL_CACHE_METERING
+# define EVAL_CACHE_METER_LIST(_) _(probe), _(hit), _(step), _(noscope)
+# define ID(x) x
+
+/* Have to typedef this for LiveConnect C code, which includes us. */
+typedef struct JSEvalCacheMeter {
+ uint64 EVAL_CACHE_METER_LIST(ID);
+} JSEvalCacheMeter;
+
+# undef ID
+# define DECLARE_EVAL_CACHE_METER JSEvalCacheMeter evalCacheMeter;
+#else
+# define DECLARE_EVAL_CACHE_METER /* nothing */
+#endif
+
#ifdef JS_THREADSAFE
/*
* Structure uniquely representing a thread. It holds thread-private data
* that can be accessed without a global lock.
*/
struct JSThread {
/* Linked list of all contexts active on this thread. */
@@ -190,24 +215,23 @@ struct JSThread {
/* Property cache for faster call/get/set invocation. */
JSPropertyCache propertyCache;
#ifdef JS_TRACER
/* Trace-tree JIT recorder/interpreter state. */
JSTraceMonitor traceMonitor;
#endif
- /* Lock-free list of scripts created by eval to garbage-collect. */
- JSScript *scriptsToGC;
+ /* Lock-free hashed lists of scripts created by eval to garbage-collect. */
+ JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE];
+
+ DECLARE_EVAL_CACHE_METER
};
-#define JS_GSN_CACHE(cx) ((cx)->thread->gsnCache)
-#define JS_PROPERTY_CACHE(cx) ((cx)->thread->propertyCache)
-#define JS_TRACE_MONITOR(cx) ((cx)->thread->traceMonitor)
-#define JS_SCRIPTS_TO_GC(cx) ((cx)->thread->scriptsToGC)
+#define JS_CACHE_LOCUS(cx) ((cx)->thread)
extern void
js_ThreadDestructorCB(void *ptr);
extern JSBool
js_SetContextThread(JSContext *cx);
extern void
@@ -458,23 +482,22 @@ struct JSRuntime {
JSGSNCache gsnCache;
/* Property cache for faster call/get/set invocation. */
JSPropertyCache propertyCache;
/* Trace-tree JIT recorder/interpreter state. */
JSTraceMonitor traceMonitor;
- /* Lock-free list of scripts created by eval to garbage-collect. */
- JSScript *scriptsToGC;
+ /* Lock-free hashed lists of scripts created by eval to garbage-collect. */
+ JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE];
-#define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache)
-#define JS_PROPERTY_CACHE(cx) ((cx)->runtime->propertyCache)
-#define JS_TRACE_MONITOR(cx) ((cx)->runtime->traceMonitor)
-#define JS_SCRIPTS_TO_GC(cx) ((cx)->runtime->scriptsToGC)
+ DECLARE_EVAL_CACHE_METER
+
+#define JS_CACHE_LOCUS(cx) ((cx)->runtime)
#endif
/*
* Object shape (property cache structural type) identifier generator.
*
* Type 0 stands for the empty scope, and must not be regenerated due to
* uint32 wrap-around. Since we use atomic pre-increment, the initial
* value for the first typed non-empty scope will be 1.
@@ -582,16 +605,29 @@ struct JSRuntime {
JSBasicStats lexicalScopeDepthStats;
#endif
#ifdef JS_GCMETER
JSGCStats gcStats;
#endif
};
+/* Common macros to access thread-local caches in JSThread or JSRuntime. */
+#define JS_GSN_CACHE(cx) (JS_CACHE_LOCUS(cx)->gsnCache)
+#define JS_PROPERTY_CACHE(cx) (JS_CACHE_LOCUS(cx)->propertyCache)
+#define JS_TRACE_MONITOR(cx) (JS_CACHE_LOCUS(cx)->traceMonitor)
+#define JS_SCRIPTS_TO_GC(cx) (JS_CACHE_LOCUS(cx)->scriptsToGC)
+
+#ifdef JS_EVAL_CACHE_METERING
+# define EVAL_CACHE_METER(x) (JS_CACHE_LOCUS(cx)->evalCacheMeter.x++)
+#else
+# define EVAL_CACHE_METER(x) ((void) 0)
+#endif
+#undef DECLARE_EVAL_CACHE_METER
+
#ifdef DEBUG
# define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which)
# define JS_RUNTIME_UNMETER(rt, which) JS_ATOMIC_DECREMENT(&(rt)->which)
#else
# define JS_RUNTIME_METER(rt, which) /* nothing */
# define JS_RUNTIME_UNMETER(rt, which) /* nothing */
#endif
@@ -1017,16 +1053,18 @@ class JSAutoResolveFlags
#define JS_HAS_OPTION(cx,option) (((cx)->options & (option)) != 0)
#define JS_HAS_STRICT_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_STRICT)
#define JS_HAS_WERROR_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_WERROR)
#define JS_HAS_COMPILE_N_GO_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_COMPILE_N_GO)
#define JS_HAS_ATLINE_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_ATLINE)
#define JSVERSION_MASK 0x0FFF /* see JSVersion in jspubtd.h */
#define JSVERSION_HAS_XML 0x1000 /* flag induced by XML option */
+#define JSVERSION_ANONFUNFIX 0x2000 /* see jsapi.h, the comments
+ for JSOPTION_ANONFUNFIX */
#define JSVERSION_NUMBER(cx) ((JSVersion)((cx)->version & \
JSVERSION_MASK))
#define JS_HAS_XML_OPTION(cx) ((cx)->version & JSVERSION_HAS_XML || \
JSVERSION_NUMBER(cx) >= JSVERSION_1_6)
/*
* Initialize a library-wide thread private data index, and remember that it
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3465,17 +3465,18 @@ js_GC(JSContext *cx, JSGCInvocationKind
/* Clear property and JIT oracle caches (only for cx->thread if JS_THREADSAFE). */
js_FlushPropertyCache(cx);
#ifdef JS_TRACER
js_FlushJITOracle(cx);
#endif
/* Destroy eval'ed scripts. */
- DestroyScriptsToGC(cx, &JS_SCRIPTS_TO_GC(cx));
+ for (i = 0; i < JS_ARRAY_LENGTH(JS_SCRIPTS_TO_GC(cx)); i++)
+ DestroyScriptsToGC(cx, &JS_SCRIPTS_TO_GC(cx)[i]);
#ifdef JS_THREADSAFE
/*
* Clear thread-based caches. To avoid redundant clearing we unroll the
* current thread's step.
*
* In case a JSScript wrapped within an object was finalized, we null
* acx->thread->gsnCache.script and finish the cache's hashtable. Note
@@ -3487,17 +3488,18 @@ js_GC(JSContext *cx, JSGCInvocationKind
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
if (!acx->thread || acx->thread == cx->thread)
continue;
GSN_CACHE_CLEAR(&acx->thread->gsnCache);
js_FlushPropertyCache(acx);
#ifdef JS_TRACER
js_FlushJITOracle(acx);
#endif
- DestroyScriptsToGC(cx, &acx->thread->scriptsToGC);
+ for (i = 0; i < JS_ARRAY_LENGTH(acx->thread->scriptsToGC); i++)
+ DestroyScriptsToGC(cx, &acx->thread->scriptsToGC[i]);
}
#else
/* The thread-unsafe case just has to clear the runtime's GSN cache. */
GSN_CACHE_CLEAR(&rt->gsnCache);
#endif
restart:
rt->gcNumber++;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1186,29 +1186,52 @@ js_ComputeFilename(JSContext *cx, JSStac
JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_LINENO);
*linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH);
} else {
*linenop = js_FramePCToLineNumber(cx, caller);
}
return caller->script->filename;
}
+#ifndef EVAL_CACHE_CHAIN_LIMIT
+# define EVAL_CACHE_CHAIN_LIMIT 4
+#endif
+
+static inline JSScript **
+EvalCacheHash(JSContext *cx, JSString *str)
+{
+ const jschar *s;
+ size_t n;
+ uint32 h;
+
+ JSSTRING_CHARS_AND_LENGTH(str, s, n);
+ if (n > 100)
+ n = 100;
+ for (h = 0; n; s++, n--)
+ h = JS_ROTATE_LEFT32(h, 4) ^ *s;
+
+ h *= JS_GOLDEN_RATIO;
+ h >>= 32 - JS_EVAL_CACHE_SHIFT;
+ return &JS_SCRIPTS_TO_GC(cx)[h];
+}
+
static JSBool
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSStackFrame *fp, *caller;
JSBool indirectCall;
JSObject *scopeobj;
- JSString *str;
+ uint32 tcflags;
+ JSPrincipals *principals;
const char *file;
uintN line;
- JSPrincipals *principals;
- uint32 tcflags;
+ JSString *str;
JSScript *script;
JSBool ok;
+ JSScript **bucket = NULL; /* avoid GCC warning with early decl&init */
#if JS_HAS_EVAL_THIS_SCOPE
JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
JSObject *setCallerScopeChain = NULL;
JSBool setCallerVarObj = JS_FALSE;
#endif
fp = js_GetTopStackFrame(cx);
caller = js_GetScriptedCaller(cx, fp);
@@ -1331,35 +1354,104 @@ obj_eval(JSContext *cx, JSObject *obj, u
/* Ensure we compile this eval with the right object in the scope chain. */
scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
if (!scopeobj) {
ok = JS_FALSE;
goto out;
}
- str = JSVAL_TO_STRING(argv[0]);
+ tcflags = TCF_COMPILE_N_GO;
if (caller) {
+ tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
principals = JS_EvalFramePrincipals(cx, fp, caller);
file = js_ComputeFilename(cx, caller, principals, &line);
} else {
+ principals = NULL;
file = NULL;
line = 0;
- principals = NULL;
}
- tcflags = TCF_COMPILE_N_GO;
- if (caller)
- tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
- script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
- JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
- NULL, file, line);
+ str = JSVAL_TO_STRING(argv[0]);
+ script = NULL;
+
+ /* Cache local eval scripts indexed by source qualified by scope. */
+ bucket = EvalCacheHash(cx, str);
+ if (caller->fun) {
+ uintN count = 0;
+ JSScript **scriptp = bucket;
+
+ EVAL_CACHE_METER(probe);
+ while ((script = *scriptp) != NULL) {
+ if ((script->flags & JSSF_SAVED_CALLER_FUN) &&
+ script->version == cx->version &&
+ (script->principals == principals ||
+ (principals->subsume(principals, script->principals) &&
+ script->principals->subsume(script->principals, principals)))) {
+ /*
+ * Get the prior (cache-filling) eval's saved caller function.
+ * See js_CompileScript in jsparse.cpp.
+ */
+ JSFunction *fun;
+ JS_GET_SCRIPT_FUNCTION(script, 0, fun);
+
+ if (fun == caller->fun) {
+ /*
+ * Get the source string passed for safekeeping in the
+ * atom map by the prior eval to js_CompileScript.
+ */
+ JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]);
+
+ if (src == str || js_EqualStrings(src, str)) {
+ /*
+ * Source matches, qualify by comparing scopeobj to the
+ * COMPILE_N_GO-memoized parent of the first literal
+ * function or regexp object if any. If none, then this
+ * script has no compiled-in dependencies on the prior
+ * eval's scopeobj.
+ */
+ JSObjectArray *objarray = JS_SCRIPT_OBJECTS(script);
+ int i = 1;
+ if (objarray->length == 1) {
+ if (script->regexpsOffset != 0) {
+ objarray = JS_SCRIPT_REGEXPS(script);
+ i = 0;
+ } else {
+ EVAL_CACHE_METER(noscope);
+ i = -1;
+ }
+ }
+ if (i < 0 ||
+ STOBJ_GET_PARENT(objarray->vector[i]) == scopeobj) {
+ EVAL_CACHE_METER(hit);
+ *scriptp = script->u.nextToGC;
+ script->u.nextToGC = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ if (++count == EVAL_CACHE_CHAIN_LIMIT) {
+ script = NULL;
+ break;
+ }
+ EVAL_CACHE_METER(step);
+ scriptp = &script->u.nextToGC;
+ }
+ }
+
if (!script) {
- ok = JS_FALSE;
- goto out;
+ script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
+ JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
+ NULL, file, line, str);
+ if (!script) {
+ ok = JS_FALSE;
+ goto out;
+ }
}
if (argc < 2) {
/* Execute using caller's new scope object (might be a Call object). */
if (caller)
scopeobj = caller->scopeChain;
}
@@ -1367,18 +1459,18 @@ obj_eval(JSContext *cx, JSObject *obj, u
* Belt-and-braces: check that the lesser of eval's principals and the
* caller's principals has access to scopeobj.
*/
ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
cx->runtime->atomState.evalAtom);
if (ok)
ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
- script->u.nextToGC = JS_SCRIPTS_TO_GC(cx);
- JS_SCRIPTS_TO_GC(cx) = script;
+ script->u.nextToGC = *bucket;
+ *bucket = script;
#ifdef CHECK_SCRIPT_OWNER
script->owner = NULL;
#endif
out:
#if JS_HAS_EVAL_THIS_SCOPE
/* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
if (setCallerScopeChain) {
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -2747,16 +2747,17 @@ Decompile(SprintStack *ss, jsbytecode *p
ss->sprinter.offset = GetOff(ss, top);
if (op == JSOP_LEAVEBLOCKEXPR)
todo = SprintCString(&ss->sprinter, rval);
break;
}
case JSOP_CALLUPVAR:
case JSOP_GETUPVAR:
+ JS_ASSERT(jp->script->flags & JSSF_SAVED_CALLER_FUN);
if (!jp->fun)
JS_GET_SCRIPT_FUNCTION(jp->script, 0, jp->fun);
if (!jp->localNames)
jp->localNames = js_GetLocalNameArray(cx, jp->fun, &jp->pool);
i = JS_UPVAR_LOCAL_NAME_START(jp->fun) + GET_UINT16(pc);
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -498,17 +498,18 @@ js_ParseScript(JSContext *cx, JSObject *
/*
* Compile a top-level script.
*/
extern JSScript *
js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
JSPrincipals *principals, uint32 tcflags,
const jschar *chars, size_t length,
- FILE *file, const char *filename, uintN lineno)
+ FILE *file, const char *filename, uintN lineno,
+ JSString *source)
{
JSParseContext pc;
JSArenaPool codePool, notePool;
JSCodeGenerator cg;
JSTokenType tt;
JSParseNode *pn;
uint32 scriptGlobals;
JSScript *script;
@@ -538,26 +539,46 @@ js_CompileScript(JSContext *cx, JSObject
js_InitCodeGenerator(cx, &cg, &pc, &codePool, ¬ePool,
pc.tokenStream.lineno);
MUST_FLOW_THROUGH("out");
cg.treeContext.flags |= (uint16) tcflags;
cg.treeContext.u.scopeChain = scopeChain;
cg.staticDepth = TCF_GET_STATIC_DEPTH(tcflags);
- if ((tcflags & TCF_COMPILE_N_GO) && callerFrame && callerFrame->fun) {
- /*
- * An eval script in a caller frame needs to have its enclosing function
- * captured in case it uses an upvar reference, and someone wishes to
- * decompile it while running.
- */
- JSParsedObjectBox *pob = js_NewParsedObjectBox(cx, &pc, FUN_OBJECT(callerFrame->fun));
- pob->emitLink = cg.objectList.lastPob;
- cg.objectList.lastPob = pob;
- cg.objectList.length++;
+ /*
+ * If funpob is non-null after we create the new script, callerFrame->fun
+ * was saved in the 0th object table entry.
+ */
+ JSParsedObjectBox *funpob = NULL;
+
+ if (tcflags & TCF_COMPILE_N_GO) {
+ if (source) {
+ /*
+ * Save eval program source in script->atomMap.vector[0] for the
+ * eval cache (see obj_eval in jsobj.cpp).
+ */
+ JSAtom *atom = js_AtomizeString(cx, source, 0);
+ if (!atom || !js_IndexAtom(cx, atom, &cg.atomList))
+ return NULL;
+ }
+
+ if (callerFrame && callerFrame->fun) {
+ /*
+ * An eval script in a caller frame needs to have its enclosing
+ * function captured in case it uses an upvar reference, and
+ * someone wishes to decompile it while it's running.
+ */
+ funpob = js_NewParsedObjectBox(cx, &pc, FUN_OBJECT(callerFrame->fun));
+ if (!funpob)
+ return NULL;
+ funpob->emitLink = cg.objectList.lastPob;
+ cg.objectList.lastPob = funpob;
+ cg.objectList.length++;
+ }
}
/* Inline Statements() to emit as we go to save space. */
for (;;) {
pc.tokenStream.flags |= TSF_OPERAND;
tt = js_PeekToken(cx, &pc.tokenStream);
pc.tokenStream.flags &= ~TSF_OPERAND;
if (tt <= TOK_EOF) {
@@ -642,16 +663,18 @@ js_CompileScript(JSContext *cx, JSObject
#ifdef METER_PARSENODES
printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
(char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
#endif
#ifdef JS_ARENAMETER
JS_DumpArenaStats(stdout);
#endif
script = js_NewScriptFromCG(cx, &cg);
+ if (script && funpob)
+ script->flags |= JSSF_SAVED_CALLER_FUN;
#ifdef JS_SCOPE_DEPTH_METER
if (script) {
JSObject *obj = scopeChain;
uintN depth = 1;
while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
++depth;
JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -451,17 +451,18 @@ struct JSParseContext {
*/
extern JSParseNode *
js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc);
extern JSScript *
js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
JSPrincipals *principals, uint32 tcflags,
const jschar *chars, size_t length,
- FILE *file, const char *filename, uintN lineno);
+ FILE *file, const char *filename, uintN lineno,
+ JSString *source = NULL);
extern JSBool
js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
const jschar *chars, size_t length,
const char *filename, uintN lineno);
extern JSBool
js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc,
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -125,16 +125,17 @@ struct JSScript {
} u;
#ifdef CHECK_SCRIPT_OWNER
JSThread *owner; /* for thread-safe life-cycle assertions */
#endif
};
#define JSSF_NO_SCRIPT_RVAL 0x01 /* no need for result value of last
expression statement */
+#define JSSF_SAVED_CALLER_FUN 0x02 /* object 0 is caller function */
static JS_INLINE uintN
StackDepth(JSScript *script)
{
return script->nslots - script->nfixed;
}
/* No need to store script->notes now that it is allocated right after code. */