diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 4d63eb029738..377e4df119bb 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1850,7 +1850,7 @@ js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp) while ((script = *listp) != NULL) { *listp = script->u.nextToGC; script->u.nextToGC = NULL; - js_DestroyScriptFromGC(cx, script); + js_DestroyCachedScript(cx, script); } } } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 68c30377387c..af3294f9ecda 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1249,9 +1249,23 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame JSScript *script = NULL; JSScript **bucket = EvalCacheHash(cx, linearStr); - if (evalType == DIRECT_EVAL && caller->isFunctionFrame() && !caller->isEvalFrame()) + if (evalType == DIRECT_EVAL && caller->isFunctionFrame() && !caller->isEvalFrame()) { script = EvalCacheLookup(cx, linearStr, caller, staticLevel, principals, scopeobj, bucket); + /* + * Although the eval cache keeps a script alive from the perspective of + * the JS engine, from a jsdbgapi user's perspective each eval() + * creates and destroys a script. This hides implementation details and + * allows jsdbgapi clients to avoid calling JS_GetScriptObject after a + * script has been returned to the eval cache, which is invalid since + * script->u.object aliases script->u.nextToGC. + */ + if (script) { + js_CallNewScriptHook(cx, script, NULL); + MUST_FLOW_THROUGH("destroy"); + } + } + /* * We can't have a callerFrame (down in js::Execute's terms) if we're in * global code (or if we're an indirect eval). @@ -1280,6 +1294,9 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame cx->runtime->atomState.evalAtom) && Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp); + MUST_FLOW_LABEL(destroy); + js_CallDestroyScriptHook(cx, script); + script->u.nextToGC = *bucket; *bucket = script; #ifdef CHECK_SCRIPT_OWNER diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 5e98121ffa72..ee87f893d953 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1561,7 +1561,7 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) } } -JS_FRIEND_API(void) +void js_CallDestroyScriptHook(JSContext *cx, JSScript *script) { JSDestroyScriptHook hook; @@ -1569,6 +1569,7 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script) hook = cx->debugHooks->destroyScriptHook; if (hook) hook(cx, script, cx->debugHooks->destroyScriptHookData); + JS_ClearScriptTraps(cx, script); } static void @@ -1581,9 +1582,6 @@ DestroyScript(JSContext *cx, JSScript *script) JS_RUNTIME_UNMETER(cx->runtime, liveScripts); #endif - js_CallDestroyScriptHook(cx, script); - JS_ClearScriptTraps(cx, script); - if (script->principals) JSPRINCIPALS_DROP(cx, script->principals); @@ -1646,11 +1644,20 @@ void js_DestroyScript(JSContext *cx, JSScript *script) { JS_ASSERT(!cx->runtime->gcRunning); + js_CallDestroyScriptHook(cx, script); DestroyScript(cx, script); } void js_DestroyScriptFromGC(JSContext *cx, JSScript *script) +{ + JS_ASSERT(cx->runtime->gcRunning); + js_CallDestroyScriptHook(cx, script); + DestroyScript(cx, script); +} + +void +js_DestroyCachedScript(JSContext *cx, JSScript *script) { JS_ASSERT(cx->runtime->gcRunning); DestroyScript(cx, script); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index ed5a3e19d0e4..6ab671483dbb 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -649,7 +649,7 @@ js_SweepScriptFilenames(JSRuntime *rt); extern JS_FRIEND_API(void) js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); -extern JS_FRIEND_API(void) +extern void js_CallDestroyScriptHook(JSContext *cx, JSScript *script); /* @@ -659,13 +659,18 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script); extern void js_DestroyScript(JSContext *cx, JSScript *script); -/* - * If data is not null, it indicates that the script could been accessed only - * from that thread. - */ extern void js_DestroyScriptFromGC(JSContext *cx, JSScript *script); +/* + * Script objects may be cached and reused, in which case their JSD-visible + * lifetimes may be shorter than their actual lifetimes. Destroy one such + * script for real as part of a GC pass. From JSD's point of view, the script + * is already dead. + */ +extern void +js_DestroyCachedScript(JSContext *cx, JSScript *script); + extern void js_TraceScript(JSTracer *trc, JSScript *script);