зеркало из https://github.com/mozilla/gecko-dev.git
Don't build a stack frame to execute empty scripts (516827, r=igor).
This commit is contained in:
Родитель
515b46d86c
Коммит
7bbe5bcd01
|
@ -53,3 +53,40 @@ BEGIN_TEST(testXDR_bug506491)
|
|||
return true;
|
||||
}
|
||||
END_TEST(testXDR_bug506491)
|
||||
|
||||
BEGIN_TEST(testXDR_bug516827)
|
||||
{
|
||||
// compile an empty script
|
||||
JSScript *script = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__);
|
||||
CHECK(script);
|
||||
JSObject *scrobj = JS_NewScriptObject(cx, script);
|
||||
CHECK(scrobj);
|
||||
jsvalRoot v(cx, OBJECT_TO_JSVAL(scrobj));
|
||||
|
||||
// freeze
|
||||
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
|
||||
CHECK(w);
|
||||
CHECK(JS_XDRScript(w, &script));
|
||||
uint32 nbytes;
|
||||
void *p = JS_XDRMemGetData(w, &nbytes);
|
||||
CHECK(p);
|
||||
void *frozen = malloc(nbytes);
|
||||
CHECK(frozen);
|
||||
memcpy(frozen, p, nbytes);
|
||||
JS_XDRDestroy(w);
|
||||
|
||||
// thaw
|
||||
script = NULL;
|
||||
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
|
||||
JS_XDRMemSetData(r, frozen, nbytes);
|
||||
CHECK(JS_XDRScript(r, &script));
|
||||
JS_XDRDestroy(r); // this frees `frozen`
|
||||
scrobj = JS_NewScriptObject(cx, script);
|
||||
CHECK(scrobj);
|
||||
v = OBJECT_TO_JSVAL(scrobj);
|
||||
|
||||
// execute with null result meaning no result wanted
|
||||
CHECK(JS_ExecuteScript(cx, global, script, NULL));
|
||||
return true;
|
||||
}
|
||||
END_TEST(testXDR_bug516827)
|
||||
|
|
|
@ -4612,7 +4612,7 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj,
|
|||
JSScript *script;
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
|
||||
tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT;
|
||||
script = JSCompiler::compileScript(cx, obj, NULL, principals, tcflags,
|
||||
chars, length, NULL, filename, lineno);
|
||||
LAST_FRAME_CHECKS(cx, script);
|
||||
|
|
|
@ -745,6 +745,12 @@ struct JSRuntime {
|
|||
double lengthSquaredSum;
|
||||
double strdepLengthSum;
|
||||
double strdepLengthSquaredSum;
|
||||
|
||||
/* Script instrumentation. */
|
||||
jsrefcount liveScripts;
|
||||
jsrefcount totalScripts;
|
||||
jsrefcount liveEmptyScripts;
|
||||
jsrefcount totalEmptyScripts;
|
||||
#endif /* DEBUG || JS_DUMP_PROPTREE_STATS */
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
|
|
|
@ -144,6 +144,12 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
|
|||
JSRuntime *rt;
|
||||
uint32 sample;
|
||||
|
||||
if (script == JSScript::emptyScript()) {
|
||||
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
|
||||
NULL, JSMSG_READ_ONLY, "empty script");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JS_ASSERT((JSOp) *pc != JSOP_TRAP);
|
||||
junk = NULL;
|
||||
rt = cx->runtime;
|
||||
|
|
|
@ -272,7 +272,15 @@ struct JSTreeContext { /* tree context for semantic checks */
|
|||
* between assignment-like and declaration-like destructuring
|
||||
* patterns, and why they need to be treated differently.
|
||||
*/
|
||||
#define TCF_DECL_DESTRUCTURING 0x10000
|
||||
#define TCF_DECL_DESTRUCTURING 0x10000
|
||||
|
||||
/*
|
||||
* A request flag passed to JSCompiler::compileScript and then down via
|
||||
* JSCodeGenerator to js_NewScriptFromCG, from script_compile_sub and any
|
||||
* kindred functions that need to make mutable scripts (even empty ones;
|
||||
* i.e., they can't share the const JSScript::emptyScript() singleton).
|
||||
*/
|
||||
#define TCF_NEED_MUTABLE_SCRIPT 0x20000
|
||||
|
||||
/*
|
||||
* Sticky deoptimization flags to propagate from FunctionBody.
|
||||
|
|
|
@ -1668,15 +1668,17 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
|||
js_FreezeLocalNames(cx, fun);
|
||||
}
|
||||
|
||||
if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
|
||||
if (!js_XDRScript(xdr, &fun->u.i.script, false, NULL))
|
||||
goto bad;
|
||||
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
*objp = FUN_OBJECT(fun);
|
||||
if (fun->u.i.script != JSScript::emptyScript()) {
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
fun->u.i.script->owner = NULL;
|
||||
fun->u.i.script->owner = NULL;
|
||||
#endif
|
||||
js_CallNewScriptHook(cx, fun->u.i.script, fun);
|
||||
js_CallNewScriptHook(cx, fun->u.i.script, fun);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -2352,14 +2354,7 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj)
|
|||
fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
fun->u.i.script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0);
|
||||
if (!fun->u.i.script)
|
||||
return NULL;
|
||||
fun->u.i.script->code[0] = JSOP_STOP;
|
||||
*fun->u.i.script->notes() = SRC_NULL;
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
fun->u.i.script->owner = NULL;
|
||||
#endif
|
||||
fun->u.i.script = JSScript::emptyScript();
|
||||
return proto;
|
||||
}
|
||||
|
||||
|
|
|
@ -1185,6 +1185,17 @@ have_fun:
|
|||
native = NULL;
|
||||
script = fun->u.i.script;
|
||||
JS_ASSERT(script);
|
||||
|
||||
if (script->isEmpty()) {
|
||||
if (flags & JSINVOKE_CONSTRUCT) {
|
||||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
|
||||
*vp = vp[1];
|
||||
} else {
|
||||
*vp = JSVAL_VOID;
|
||||
}
|
||||
ok = JS_TRUE;
|
||||
goto out2;
|
||||
}
|
||||
} else {
|
||||
native = fun->u.n.native;
|
||||
script = NULL;
|
||||
|
@ -1474,6 +1485,12 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
|||
JSObject *obj, *tmp;
|
||||
JSBool ok;
|
||||
|
||||
if (script->isEmpty()) {
|
||||
if (result)
|
||||
*result = JSVAL_VOID;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
js_LeaveTrace(cx);
|
||||
|
||||
#ifdef INCLUDE_MOZILLA_DTRACE
|
||||
|
@ -2795,7 +2812,8 @@ js_Interpret(JSContext *cx)
|
|||
/* Set registerized frame pointer and derived script pointer. */
|
||||
fp = cx->fp;
|
||||
script = fp->script;
|
||||
JS_ASSERT(script->length != 0);
|
||||
JS_ASSERT(!script->isEmpty());
|
||||
JS_ASSERT(script->length > 1);
|
||||
|
||||
/* Count of JS function calls that nest in this C js_Interpret frame. */
|
||||
inlineCallCount = 0;
|
||||
|
|
|
@ -1426,8 +1426,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
* Get the prior (cache-filling) eval's saved caller function.
|
||||
* See JSCompiler::compileScript in jsparse.cpp.
|
||||
*/
|
||||
JSFunction *fun;
|
||||
fun = script->getFunction(0);
|
||||
JSFunction *fun = script->getFunction(0);
|
||||
|
||||
if (fun == caller->fun) {
|
||||
/*
|
||||
|
@ -1446,6 +1445,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
*/
|
||||
JSObjectArray *objarray = script->objects();
|
||||
int i = 1;
|
||||
|
||||
if (objarray->length == 1) {
|
||||
if (script->regexpsOffset != 0) {
|
||||
objarray = script->regexps();
|
||||
|
@ -1483,7 +1483,8 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
callerFrame = (staticLevel != 0) ? caller : NULL;
|
||||
if (!script) {
|
||||
script = JSCompiler::compileScript(cx, scopeobj, callerFrame,
|
||||
principals, TCF_COMPILE_N_GO,
|
||||
principals,
|
||||
TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT,
|
||||
str->chars(), str->length(),
|
||||
NULL, file, line, str, staticLevel);
|
||||
if (!script) {
|
||||
|
|
|
@ -2028,12 +2028,17 @@ BEGIN_CASE(JSOP_NEW)
|
|||
}
|
||||
rval = vp[1];
|
||||
obj2 = js_NewObject(cx, &js_ObjectClass,
|
||||
JSVAL_IS_OBJECT(rval)
|
||||
? JSVAL_TO_OBJECT(rval)
|
||||
: NULL,
|
||||
JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL,
|
||||
OBJ_GET_PARENT(cx, obj));
|
||||
if (!obj2)
|
||||
goto error;
|
||||
|
||||
if (fun->u.i.script->isEmpty()) {
|
||||
*vp = OBJECT_TO_JSVAL(obj2);
|
||||
regs.sp = vp + 1;
|
||||
goto end_new;
|
||||
}
|
||||
|
||||
vp[1] = OBJECT_TO_JSVAL(obj2);
|
||||
flags = JSFRAME_CONSTRUCTING;
|
||||
goto inline_call;
|
||||
|
@ -2045,6 +2050,8 @@ BEGIN_CASE(JSOP_NEW)
|
|||
regs.sp = vp + 1;
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
TRACE_0(NativeCallComplete);
|
||||
|
||||
end_new:
|
||||
END_CASE(JSOP_NEW)
|
||||
|
||||
BEGIN_CASE(JSOP_CALL)
|
||||
|
@ -2071,6 +2078,14 @@ BEGIN_CASE(JSOP_APPLY)
|
|||
JSInlineFrame *newifp;
|
||||
JSInterpreterHook hook;
|
||||
|
||||
script = fun->u.i.script;
|
||||
if (script->isEmpty()) {
|
||||
script = fp->script;
|
||||
*vp = JSVAL_VOID;
|
||||
regs.sp = vp + 1;
|
||||
goto end_call;
|
||||
}
|
||||
|
||||
/* Restrict recursion of lightweight functions. */
|
||||
if (inlineCallCount >= JS_MAX_INLINE_CALL_COUNT) {
|
||||
js_ReportOverRecursed(cx);
|
||||
|
@ -2078,9 +2093,7 @@ BEGIN_CASE(JSOP_APPLY)
|
|||
}
|
||||
|
||||
/* Compute the total number of stack slots needed by fun. */
|
||||
nframeslots = JS_HOWMANY(sizeof(JSInlineFrame),
|
||||
sizeof(jsval));
|
||||
script = fun->u.i.script;
|
||||
nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval));
|
||||
atoms = script->atomMap.vector;
|
||||
nbytes = (nframeslots + script->nslots) * sizeof(jsval);
|
||||
|
||||
|
|
|
@ -801,7 +801,7 @@ JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *cal
|
|||
void *sbrk(ptrdiff_t), *before = sbrk(0);
|
||||
#endif
|
||||
|
||||
JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL)));
|
||||
JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT)));
|
||||
|
||||
/*
|
||||
* The scripted callerFrame can only be given for compile-and-go scripts
|
||||
|
|
|
@ -207,7 +207,6 @@ script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
const char *file;
|
||||
uintN line;
|
||||
JSPrincipals *principals;
|
||||
uint32 tcflags;
|
||||
jsint execDepth;
|
||||
|
||||
/* Make sure obj is a Script object. */
|
||||
|
@ -263,8 +262,8 @@ script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
* match the compile-time. TCF_COMPILE_N_GO is tested in jsemit.c and
|
||||
* jsparse.c to optimize based on identity of run- and compile-time scope.
|
||||
*/
|
||||
tcflags = 0;
|
||||
script = JSCompiler::compileScript(cx, scopeobj, NULL, principals, tcflags,
|
||||
script = JSCompiler::compileScript(cx, scopeobj, NULL, principals,
|
||||
TCF_NEED_MUTABLE_SCRIPT,
|
||||
str->chars(), str->length(),
|
||||
NULL, file, line);
|
||||
if (!script)
|
||||
|
@ -408,10 +407,20 @@ script_exec(JSContext *cx, uintN argc, jsval *vp)
|
|||
|
||||
#endif /* JS_HAS_SCRIPT_OBJECT */
|
||||
|
||||
static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL};
|
||||
|
||||
/* static */ const JSScript JSScript::emptyScriptConst = {
|
||||
const_cast<jsbytecode*>(emptyScriptCode),
|
||||
1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 1, 0, 0,
|
||||
const_cast<jsbytecode*>(emptyScriptCode),
|
||||
{0, NULL}, NULL, 0, 0, 0, NULL
|
||||
};
|
||||
|
||||
#if JS_HAS_XDR
|
||||
|
||||
JSBool
|
||||
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
|
||||
JSBool *hasMagic)
|
||||
{
|
||||
JSContext *cx;
|
||||
JSScript *script, *oldscript;
|
||||
|
@ -450,8 +459,32 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
|||
if (hasMagic)
|
||||
*hasMagic = JS_TRUE;
|
||||
|
||||
/*
|
||||
* Since the shortest possible script has JSOP_STOP as its only bytecode,
|
||||
* encode only the length 1 for the emptyScript singleton, and return the
|
||||
* emptyScript instead of a new script when decoding a script of length 1.
|
||||
*/
|
||||
if (xdr->mode == JSXDR_ENCODE)
|
||||
length = (script == JSScript::emptyScript()) ? 1 : script->length;
|
||||
if (!JS_XDRUint32(xdr, &length))
|
||||
return JS_FALSE;
|
||||
JS_ASSERT(length != 0);
|
||||
if (length == 1) {
|
||||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
JS_ASSERT(*scriptp == JSScript::emptyScript());
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/* Decoding: check whether we need a mutable empty script. */
|
||||
if (cx->debugHooks->newScriptHook)
|
||||
needMutableScript = true;
|
||||
if (!needMutableScript) {
|
||||
*scriptp = JSScript::emptyScript();
|
||||
return JS_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
length = script->length;
|
||||
prologLength = script->main - script->code;
|
||||
JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN);
|
||||
version = (uint32)script->version | (script->nfixed << 16);
|
||||
|
@ -477,8 +510,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
|||
ntrynotes = script->trynotes()->length;
|
||||
}
|
||||
|
||||
if (!JS_XDRUint32(xdr, &length))
|
||||
return JS_FALSE;
|
||||
if (!JS_XDRUint32(xdr, &prologLength))
|
||||
return JS_FALSE;
|
||||
if (!JS_XDRUint32(xdr, &version))
|
||||
|
@ -604,7 +635,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
|||
JS_ASSERT(clasp == &js_FunctionClass ||
|
||||
clasp == &js_BlockClass);
|
||||
isBlock = (clasp == &js_BlockClass) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
if (!JS_XDRUint32(xdr, &isBlock))
|
||||
goto error;
|
||||
if (isBlock == 0) {
|
||||
|
@ -706,7 +737,7 @@ script_freeze(JSContext *cx, uintN argc, jsval *vp)
|
|||
return JS_FALSE;
|
||||
|
||||
/* write */
|
||||
ok = js_XDRScript(xdr, &script, &hasMagic);
|
||||
ok = js_XDRScript(xdr, &script, false, &hasMagic);
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (!hasMagic) {
|
||||
|
@ -797,7 +828,7 @@ script_thaw(JSContext *cx, uintN argc, jsval *vp)
|
|||
JS_XDRMemSetData(xdr, buf, len);
|
||||
|
||||
/* XXXbe should magic mismatch be error, or false return value? */
|
||||
ok = js_XDRScript(xdr, &script, &hasMagic);
|
||||
ok = js_XDRScript(xdr, &script, true, &hasMagic);
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (!hasMagic) {
|
||||
|
@ -1369,6 +1400,16 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
|
|||
JSScript *script;
|
||||
uint8 *cursor;
|
||||
|
||||
if (length == 1) {
|
||||
JS_ASSERT(nsrcnotes == 1);
|
||||
JS_ASSERT(natoms == 0);
|
||||
JS_ASSERT(nobjects == 0);
|
||||
JS_ASSERT(nupvars == 0);
|
||||
JS_ASSERT(nregexps == 0);
|
||||
JS_ASSERT(ntrynotes == 0);
|
||||
return JSScript::emptyScript();
|
||||
}
|
||||
|
||||
size = sizeof(JSScript) +
|
||||
sizeof(JSAtom *) * natoms +
|
||||
length * sizeof(jsbytecode) +
|
||||
|
@ -1485,6 +1526,41 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
|||
|
||||
mainLength = CG_OFFSET(cg);
|
||||
prologLength = CG_PROLOG_OFFSET(cg);
|
||||
|
||||
if (!cx->debugHooks->newScriptHook &&
|
||||
!(cg->flags & TCF_NEED_MUTABLE_SCRIPT) &&
|
||||
prologLength + mainLength <= 3) {
|
||||
/*
|
||||
* Check very short scripts to see whether they are "empty" and return
|
||||
* the const empty-script singleton if so. We are deliberately flexible
|
||||
* about whether JSOP_TRACE is in the prolog.
|
||||
*/
|
||||
jsbytecode *pc = prologLength ? CG_PROLOG_BASE(cg) : CG_BASE(cg);
|
||||
|
||||
if (JSOp(*pc) == JSOP_TRACE) {
|
||||
++pc;
|
||||
if (pc == CG_PROLOG_BASE(cg) + prologLength)
|
||||
pc = CG_BASE(cg);
|
||||
}
|
||||
if ((cg->flags & TCF_NO_SCRIPT_RVAL) && JSOp(*pc) == JSOP_FALSE)
|
||||
++pc;
|
||||
if (JSOp(*pc) == JSOP_STOP) {
|
||||
JSScript *empty = JSScript::emptyScript();
|
||||
|
||||
if (cg->flags & TCF_IN_FUNCTION) {
|
||||
fun = cg->fun;
|
||||
JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
|
||||
js_FreezeLocalNames(cx, fun);
|
||||
fun->u.i.nupvars = 0;
|
||||
fun->u.i.script = empty;
|
||||
}
|
||||
|
||||
JS_RUNTIME_METER(cx->runtime, liveEmptyScripts);
|
||||
JS_RUNTIME_METER(cx->runtime, totalEmptyScripts);
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
|
||||
script = js_NewScript(cx, prologLength + mainLength, nsrcnotes,
|
||||
cg->atomList.count, cg->objectList.length,
|
||||
|
@ -1552,8 +1628,10 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
|||
if (cg->flags & TCF_IN_FUNCTION) {
|
||||
fun = cg->fun;
|
||||
JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
|
||||
JS_ASSERT_IF(script->upvarsOffset != 0,
|
||||
script->upvars()->length == fun->u.i.nupvars);
|
||||
if (script->upvarsOffset != 0)
|
||||
JS_ASSERT(script->upvars()->length == fun->u.i.nupvars);
|
||||
else
|
||||
fun->u.i.nupvars = 0;
|
||||
|
||||
js_FreezeLocalNames(cx, fun);
|
||||
fun->u.i.script = script;
|
||||
|
@ -1566,6 +1644,8 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
|||
|
||||
/* Tell the debugger about this compiled script. */
|
||||
js_CallNewScriptHook(cx, script, fun);
|
||||
JS_RUNTIME_METER(cx->runtime, liveScripts);
|
||||
JS_RUNTIME_METER(cx->runtime, totalScripts);
|
||||
return script;
|
||||
|
||||
bad:
|
||||
|
@ -1576,6 +1656,8 @@ bad:
|
|||
JS_FRIEND_API(void)
|
||||
js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
|
||||
{
|
||||
JS_ASSERT(script != JSScript::emptyScript());
|
||||
|
||||
JSNewScriptHook hook;
|
||||
|
||||
hook = cx->debugHooks->newScriptHook;
|
||||
|
@ -1590,6 +1672,8 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
|
|||
JS_FRIEND_API(void)
|
||||
js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
|
||||
{
|
||||
JS_ASSERT(script != JSScript::emptyScript());
|
||||
|
||||
JSDestroyScriptHook hook;
|
||||
|
||||
hook = cx->debugHooks->destroyScriptHook;
|
||||
|
@ -1600,6 +1684,11 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
|
|||
void
|
||||
js_DestroyScript(JSContext *cx, JSScript *script)
|
||||
{
|
||||
if (script == JSScript::emptyScript()) {
|
||||
JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
|
||||
return;
|
||||
}
|
||||
|
||||
js_CallDestroyScriptHook(cx, script);
|
||||
JS_ClearScriptTraps(cx, script);
|
||||
|
||||
|
@ -1654,6 +1743,8 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
|||
#endif
|
||||
|
||||
cx->free(script);
|
||||
|
||||
JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -172,6 +172,33 @@ struct JSScript {
|
|||
inline JSFunction *getFunction(size_t index);
|
||||
|
||||
inline JSObject *getRegExp(size_t index);
|
||||
|
||||
/*
|
||||
* The isEmpty method tells whether this script has code that computes any
|
||||
* result (not return value, result AKA normal completion value) other than
|
||||
* JSVAL_VOID, or any other effects. It has a fast path for the case where
|
||||
* |this| is the emptyScript singleton, but it also checks this->length and
|
||||
* this->code, to handle debugger-generated mutable empty scripts.
|
||||
*/
|
||||
inline bool isEmpty() const;
|
||||
|
||||
/*
|
||||
* Accessor for the emptyScriptConst singleton, to consolidate const_cast.
|
||||
* See the private member declaration.
|
||||
*/
|
||||
static JSScript *emptyScript() {
|
||||
return const_cast<JSScript *>(&emptyScriptConst);
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
* Use const to put this in read-only memory if possible. We are stuck with
|
||||
* non-const JSScript * and jsbytecode * by legacy code (back in the 1990s,
|
||||
* const wasn't supported correctly on all target platforms). The debugger
|
||||
* does mutate bytecode, and script->u.object may be set after construction
|
||||
* in some cases, so making JSScript pointers const will be "hard".
|
||||
*/
|
||||
static const JSScript emptyScriptConst;
|
||||
};
|
||||
|
||||
#define SHARP_NSLOTS 2 /* [#array, #depth] slots if the script
|
||||
|
@ -317,11 +344,19 @@ js_GetOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
|
|||
* If magic is null, js_XDRScript returns false on bad magic number errors,
|
||||
* which it reports.
|
||||
*
|
||||
* NB: callers must call js_CallNewScriptHook after successful JSXDR_DECODE
|
||||
* and subsequent set-up of owning function or script object, if any.
|
||||
* NB: after a successful JSXDR_DECODE, and provided that *scriptp is not the
|
||||
* JSScript::emptyScript() immutable singleton, js_XDRScript callers must do
|
||||
* any required subsequent set-up of owning function or script object and then
|
||||
* call js_CallNewScriptHook.
|
||||
*
|
||||
* If the caller requires a mutable empty script (for debugging or u.object
|
||||
* ownership setting), pass true for needMutableScript. Otherwise pass false.
|
||||
* Call js_CallNewScriptHook only with a mutable script, i.e. never with the
|
||||
* JSScript::emptyScript() singleton.
|
||||
*/
|
||||
extern JSBool
|
||||
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic);
|
||||
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
|
||||
JSBool *hasMagic);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#define jsscriptinlines_h___
|
||||
|
||||
#include "jsfun.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsregexp.h"
|
||||
#include "jsscript.h"
|
||||
|
||||
|
@ -66,4 +67,23 @@ JSScript::getRegExp(size_t index)
|
|||
return obj;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScript::isEmpty() const
|
||||
{
|
||||
if (this == emptyScript())
|
||||
return true;
|
||||
|
||||
if (length <= 3) {
|
||||
jsbytecode *pc = code;
|
||||
|
||||
if (JSOp(*pc) == JSOP_TRACE)
|
||||
++pc;
|
||||
if (noScriptRval && JSOp(*pc) == JSOP_FALSE)
|
||||
++pc;
|
||||
if (JSOp(*pc) == JSOP_STOP)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* jsscriptinlines_h___ */
|
||||
|
|
|
@ -12032,6 +12032,20 @@ TraceRecorder::guardArguments(JSObject *obj, LIns* obj_ins, unsigned *depthp)
|
|||
JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, bool constructing)
|
||||
{
|
||||
/*
|
||||
* The function's identity (JSFunction and therefore JSScript) is guarded,
|
||||
* so we can optimize for the empty script singleton right away. No need to
|
||||
* worry about crossing globals or relocating argv, even, in this case!
|
||||
*
|
||||
* Note that the interpreter shortcuts empty-script call and construct too,
|
||||
* and does not call any TR::record_*CallComplete hook.
|
||||
*/
|
||||
if (fun->u.i.script->isEmpty()) {
|
||||
LIns* rval_ins = constructing ? stack(-1 - argc) : INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID));
|
||||
stack(-2 - argc, rval_ins);
|
||||
return RECORD_CONTINUE;
|
||||
}
|
||||
|
||||
if (JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(fval)) != globalObj)
|
||||
RETURN_STOP("JSOP_CALL or JSOP_NEW crosses global scopes");
|
||||
|
||||
|
|
|
@ -681,7 +681,7 @@ js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp)
|
|||
JS_PUBLIC_API(JSBool)
|
||||
JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
||||
{
|
||||
if (!js_XDRScript(xdr, scriptp, NULL))
|
||||
if (!js_XDRScript(xdr, scriptp, true, NULL))
|
||||
return JS_FALSE;
|
||||
if (xdr->mode == JSXDR_DECODE)
|
||||
js_CallNewScriptHook(xdr->cx, *scriptp, NULL);
|
||||
|
|
|
@ -193,7 +193,8 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
|
|||
#define JSXDR_MAGIC_SCRIPT_7 0xdead0007
|
||||
#define JSXDR_MAGIC_SCRIPT_8 0xdead0008
|
||||
#define JSXDR_MAGIC_SCRIPT_9 0xdead0009
|
||||
#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_9
|
||||
#define JSXDR_MAGIC_SCRIPT_10 0xdead000a
|
||||
#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_10
|
||||
|
||||
/*
|
||||
* Bytecode version number. Increment the subtrahend whenever JS bytecode
|
||||
|
@ -204,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
|
|||
* before deserialization of bytecode. If the saved version does not match
|
||||
* the current version, abort deserialization and invalidate the file.
|
||||
*/
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 55)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 56)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
|
Загрузка…
Ссылка в новой задаче