зеркало из https://github.com/mozilla/gecko-dev.git
Bug 494235: wrap escaping optimized closures for the debugger API (r=igor/mrbkap).
This commit is contained in:
Родитель
6e088c83e0
Коммит
3442a335eb
|
@ -949,6 +949,11 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
|||
0, /* JSOP_LAMBDA_FC */
|
||||
0, /* JSOP_OBJTOP */
|
||||
0, /* JSOP_LOOP */
|
||||
0, /* JSOP_GETUPVAR_DBG */
|
||||
0, /* JSOP_CALLUPVAR_DBG */
|
||||
0, /* JSOP_DEFFUN_DBGFC */
|
||||
0, /* JSOP_DEFLOCALFUN_DBGFC */
|
||||
0, /* JSOP_LAMBDA_DBGFC */
|
||||
};
|
||||
#define JSOP_IS_IMACOP(x) (0 \
|
||||
|| x == JSOP_BITOR \
|
||||
|
|
|
@ -192,7 +192,7 @@ MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 109, 0, JSEXN_SYNTAXERR, "missing ; before
|
|||
MSG_DEF(JSMSG_NO_RETURN_VALUE, 110, 1, JSEXN_TYPEERR, "function {0} does not always return a value")
|
||||
MSG_DEF(JSMSG_DUPLICATE_FORMAL, 111, 1, JSEXN_TYPEERR, "duplicate formal argument {0}")
|
||||
MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?{0}")
|
||||
MSG_DEF(JSMSG_UNUSED113, 113, 0, JSEXN_NONE, "unused113")
|
||||
MSG_DEF(JSMSG_OPTIMIZED_CLOSURE_LEAK, 113, 0, JSEXN_INTERNALERR, "cannot access optimized closure")
|
||||
MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 114, 0, JSEXN_SYNTAXERR, "more than one switch default")
|
||||
MSG_DEF(JSMSG_TOO_MANY_CASES, 115, 0, JSEXN_INTERNALERR, "too many switch cases")
|
||||
MSG_DEF(JSMSG_BAD_SWITCH, 116, 0, JSEXN_SYNTAXERR, "invalid switch statement")
|
||||
|
@ -309,6 +309,4 @@ MSG_DEF(JSMSG_EVAL_ARITY, 226, 0, JSEXN_TYPEERR, "eval accepts only
|
|||
MSG_DEF(JSMSG_MISSING_FUN_ARG, 227, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
|
||||
MSG_DEF(JSMSG_JSON_BAD_PARSE, 228, 0, JSEXN_SYNTAXERR, "JSON.parse")
|
||||
MSG_DEF(JSMSG_JSON_BAD_STRINGIFY, 229, 0, JSEXN_ERR, "JSON.stringify")
|
||||
|
||||
|
||||
|
||||
MSG_DEF(JSMSG_XDR_CLOSURE_WRAPPER, 230, 1, JSEXN_INTERNALERR, "can't XDR closure wrapper for function {0}")
|
||||
|
|
|
@ -646,11 +646,7 @@ JS_TypeOfValue(JSContext *cx, jsval v)
|
|||
type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */
|
||||
obj = JSVAL_TO_OBJECT(v);
|
||||
if (obj) {
|
||||
JSObject *wrapped;
|
||||
|
||||
wrapped = js_GetWrappedObject(cx, obj);
|
||||
if (wrapped)
|
||||
obj = wrapped;
|
||||
obj = js_GetWrappedObject(cx, obj);
|
||||
|
||||
ops = obj->map->ops;
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
|
@ -1362,11 +1358,11 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj)
|
|||
}
|
||||
|
||||
#define CLASP(name) (&js_##name##Class)
|
||||
#define EXT_CLASP(name) (&js_##name##Class.base)
|
||||
#define XCLASP(name) (&js_##name##Class.base)
|
||||
#define EAGER_ATOM(name) ATOM_OFFSET(name), NULL
|
||||
#define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL
|
||||
#define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name)
|
||||
#define EAGER_ATOM_AND_EXT_CLASP(name) EAGER_CLASS_ATOM(name), EXT_CLASP(name)
|
||||
#define EAGER_ATOM_AND_XCLASP(name) EAGER_CLASS_ATOM(name), XCLASP(name)
|
||||
#define LAZY_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str
|
||||
|
||||
typedef struct JSStdName {
|
||||
|
@ -1415,8 +1411,8 @@ static JSStdName standard_class_atoms[] = {
|
|||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
{js_InitXMLClass, EAGER_ATOM_AND_CLASP(XML)},
|
||||
{js_InitNamespaceClass, EAGER_ATOM_AND_EXT_CLASP(Namespace)},
|
||||
{js_InitQNameClass, EAGER_ATOM_AND_EXT_CLASP(QName)},
|
||||
{js_InitNamespaceClass, EAGER_ATOM_AND_XCLASP(Namespace)},
|
||||
{js_InitQNameClass, EAGER_ATOM_AND_XCLASP(QName)},
|
||||
#endif
|
||||
#if JS_HAS_FILE_OBJECT
|
||||
{js_InitFileClass, EAGER_ATOM_AND_CLASP(File)},
|
||||
|
|
|
@ -684,7 +684,6 @@ js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
jsval userid;
|
||||
|
||||
funobj = JSVAL_TO_OBJECT(argv[-2]);
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass);
|
||||
wrapper = GET_FUNCTION_PRIVATE(cx, funobj);
|
||||
userid = ATOM_KEY(wrapper->atom);
|
||||
*rval = argv[0];
|
||||
|
@ -1156,7 +1155,7 @@ JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
|
|||
if (!fp->fun)
|
||||
return NULL;
|
||||
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, fp->callee) == &js_FunctionClass);
|
||||
JS_ASSERT(HAS_FUNCTION_CLASS(fp->callee));
|
||||
JS_ASSERT(OBJ_GET_PRIVATE(cx, fp->callee) == fp->fun);
|
||||
return fp->callee;
|
||||
}
|
||||
|
@ -1261,11 +1260,28 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
|
|||
TCF_PUT_STATIC_LEVEL(JS_DISPLAY_SIZE),
|
||||
chars, length, NULL,
|
||||
filename, lineno);
|
||||
|
||||
if (!script)
|
||||
return JS_FALSE;
|
||||
|
||||
JSStackFrame *displayCopy[JS_DISPLAY_SIZE];
|
||||
if (cx->fp != fp) {
|
||||
memcpy(displayCopy, cx->display, sizeof displayCopy);
|
||||
|
||||
/* This API requires an active fp on cx, so fp2 can't go null here. */
|
||||
for (JSStackFrame *fp2 = cx->fp; fp2 != fp; fp2 = fp2->down) {
|
||||
if (fp2->displaySave) {
|
||||
JS_ASSERT(fp2->script->staticLevel < JS_DISPLAY_SIZE);
|
||||
cx->display[fp2->script->staticLevel] = fp2->displaySave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL,
|
||||
rval);
|
||||
|
||||
if (cx->fp != fp)
|
||||
memcpy(cx->display, displayCopy, sizeof cx->display);
|
||||
js_DestroyScript(cx, script);
|
||||
return ok;
|
||||
}
|
||||
|
|
|
@ -2127,6 +2127,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
JS_ASSERT(cg->flags & TCF_IN_FUNCTION);
|
||||
JS_ASSERT(cg->lexdeps.lookup(atom));
|
||||
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
|
||||
JS_ASSERT(cg->fun->u.i.skipmin <= skip);
|
||||
|
||||
/*
|
||||
* If op is a mutating opcode, this upvar's static level is too big to
|
||||
|
|
312
js/src/jsfun.cpp
312
js/src/jsfun.cpp
|
@ -245,7 +245,19 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
|
|||
* We must be in a function activation; the function must be lightweight
|
||||
* or else fp must have a variable object.
|
||||
*/
|
||||
JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj));
|
||||
JSFunction *fun = fp->fun;
|
||||
JS_ASSERT(fun && (!(fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj));
|
||||
|
||||
/*
|
||||
* Unlike FUN_ESCAPE_HAZARD(fun), we test here only for null closures, not
|
||||
* flat closures -- flat ones are inherently escaping, so arguments.callee
|
||||
* references are fine.
|
||||
*/
|
||||
if (FUN_NULL_CLOSURE(fun) && fun->u.i.skipmin != 0) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_OPTIMIZED_CLOSURE_LEAK);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* Skip eval and debugger frames. */
|
||||
while (fp->flags & JSFRAME_SPECIAL)
|
||||
|
@ -365,6 +377,175 @@ args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JS_REQUIRES_STACK JSObject *
|
||||
WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunction *fun)
|
||||
{
|
||||
JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun);
|
||||
JS_ASSERT(FUN_OPTIMIZED_CLOSURE(fun));
|
||||
JS_ASSERT(!fun->u.i.wrapper);
|
||||
|
||||
/*
|
||||
* We do not attempt to reify Call and Block objects on demand for outer
|
||||
* scopes. This could be done (see the "v8" patch in bug 494235) but it is
|
||||
* fragile in the face of ongoing compile-time optimization. Instead, the
|
||||
* _DBG* opcodes used by wrappers created here must cope with unresolved
|
||||
* upvars and throw them as reference errors. Caveat debuggers!
|
||||
*/
|
||||
JSObject *scopeChain = js_GetScopeChain(cx, fp);
|
||||
if (!scopeChain)
|
||||
return NULL;
|
||||
|
||||
JSObject *wfunobj = js_NewObjectWithGivenProto(cx, &js_FunctionClass,
|
||||
funobj, scopeChain, 0);
|
||||
if (!wfunobj)
|
||||
return NULL;
|
||||
JSAutoTempValueRooter tvr(cx, wfunobj);
|
||||
|
||||
JSFunction *wfun = (JSFunction *) wfunobj;
|
||||
wfunobj->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(wfun);
|
||||
wfun->nargs = 0;
|
||||
wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT;
|
||||
wfun->u.i.nvars = 0;
|
||||
wfun->u.i.nupvars = 0;
|
||||
wfun->u.i.skipmin = fun->u.i.skipmin;
|
||||
wfun->u.i.wrapper = true;
|
||||
wfun->u.i.script = NULL;
|
||||
wfun->u.i.names.taggedAtom = NULL;
|
||||
wfun->atom = fun->atom;
|
||||
|
||||
if (fun->hasLocalNames()) {
|
||||
void *mark = JS_ARENA_MARK(&cx->tempPool);
|
||||
jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
|
||||
if (!names)
|
||||
return NULL;
|
||||
|
||||
JSBool ok = true;
|
||||
for (uintN i = 0, n = fun->countLocalNames(); i != n; i++) {
|
||||
jsuword name = names[i];
|
||||
JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(name);
|
||||
JSLocalKind localKind = (i < fun->nargs)
|
||||
? JSLOCAL_ARG
|
||||
: (i < fun->countArgsAndVars())
|
||||
? (JS_LOCAL_NAME_IS_CONST(name)
|
||||
? JSLOCAL_CONST
|
||||
: JSLOCAL_VAR)
|
||||
: JSLOCAL_UPVAR;
|
||||
|
||||
ok = js_AddLocal(cx, wfun, atom, localKind);
|
||||
if (!ok)
|
||||
break;
|
||||
}
|
||||
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
JS_ASSERT(wfun->nargs == fun->nargs);
|
||||
JS_ASSERT(wfun->u.i.nvars == fun->u.i.nvars);
|
||||
JS_ASSERT(wfun->u.i.nupvars == fun->u.i.nupvars);
|
||||
js_FreezeLocalNames(cx, wfun);
|
||||
}
|
||||
|
||||
JSScript *script = fun->u.i.script;
|
||||
jssrcnote *snbase = SCRIPT_NOTES(script);
|
||||
jssrcnote *sn = snbase;
|
||||
while (!SN_IS_TERMINATOR(sn))
|
||||
sn = SN_NEXT(sn);
|
||||
uintN nsrcnotes = (sn - snbase) + 1;
|
||||
|
||||
/* NB: GC must not occur before wscript is homed in wfun->u.i.script. */
|
||||
JSScript *wscript = js_NewScript(cx, script->length, nsrcnotes,
|
||||
script->atomMap.length,
|
||||
(script->objectsOffset != 0)
|
||||
? JS_SCRIPT_OBJECTS(script)->length
|
||||
: 0,
|
||||
fun->u.i.nupvars,
|
||||
(script->regexpsOffset != 0)
|
||||
? JS_SCRIPT_REGEXPS(script)->length
|
||||
: 0,
|
||||
(script->trynotesOffset != 0)
|
||||
? JS_SCRIPT_TRYNOTES(script)->length
|
||||
: 0);
|
||||
if (!wscript)
|
||||
return NULL;
|
||||
|
||||
memcpy(wscript->code, script->code, script->length);
|
||||
wscript->main = wscript->code + (script->main - script->code);
|
||||
|
||||
memcpy(SCRIPT_NOTES(wscript), snbase, nsrcnotes);
|
||||
memcpy(wscript->atomMap.vector, script->atomMap.vector,
|
||||
wscript->atomMap.length * sizeof(JSAtom *));
|
||||
if (script->objectsOffset != 0) {
|
||||
memcpy(JS_SCRIPT_OBJECTS(wscript)->vector, JS_SCRIPT_OBJECTS(script)->vector,
|
||||
JS_SCRIPT_OBJECTS(wscript)->length * sizeof(JSObject *));
|
||||
}
|
||||
if (script->regexpsOffset != 0) {
|
||||
memcpy(JS_SCRIPT_REGEXPS(wscript)->vector, JS_SCRIPT_REGEXPS(script)->vector,
|
||||
JS_SCRIPT_REGEXPS(wscript)->length * sizeof(JSObject *));
|
||||
}
|
||||
if (script->trynotesOffset != 0) {
|
||||
memcpy(JS_SCRIPT_TRYNOTES(wscript)->vector, JS_SCRIPT_TRYNOTES(script)->vector,
|
||||
JS_SCRIPT_TRYNOTES(wscript)->length * sizeof(JSTryNote));
|
||||
}
|
||||
|
||||
if (wfun->u.i.nupvars != 0) {
|
||||
JS_ASSERT(wfun->u.i.nupvars == JS_SCRIPT_UPVARS(wscript)->length);
|
||||
memcpy(JS_SCRIPT_UPVARS(wscript)->vector, JS_SCRIPT_UPVARS(script)->vector,
|
||||
wfun->u.i.nupvars * sizeof(uint32));
|
||||
}
|
||||
|
||||
jsbytecode *pc = wscript->code;
|
||||
while (*pc != JSOP_STOP) {
|
||||
/* XYZZYbe should copy JSOP_TRAP? */
|
||||
JSOp op = js_GetOpcode(cx, wscript, pc);
|
||||
const JSCodeSpec *cs = &js_CodeSpec[op];
|
||||
ptrdiff_t oplen = cs->length;
|
||||
if (oplen < 0)
|
||||
oplen = js_GetVariableBytecodeLength(pc);
|
||||
|
||||
/*
|
||||
* Rewrite JSOP_{GET,CALL}DSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
|
||||
* case where fun is an escaping flat closure. This works because the
|
||||
* UPVAR and DSLOT ops by design have the same format: an upvar index
|
||||
* immediate operand.
|
||||
*/
|
||||
switch (op) {
|
||||
case JSOP_GETUPVAR: *pc = JSOP_GETUPVAR_DBG; break;
|
||||
case JSOP_CALLUPVAR: *pc = JSOP_CALLUPVAR_DBG; break;
|
||||
case JSOP_GETDSLOT: *pc = JSOP_GETUPVAR_DBG; break;
|
||||
case JSOP_CALLDSLOT: *pc = JSOP_CALLUPVAR_DBG; break;
|
||||
case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break;
|
||||
case JSOP_DEFLOCALFUN_FC: *pc = JSOP_DEFLOCALFUN_DBGFC; break;
|
||||
case JSOP_LAMBDA_FC: *pc = JSOP_LAMBDA_DBGFC; break;
|
||||
default:;
|
||||
}
|
||||
pc += oplen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flag the wrapper's script as hazardous, because it could host debugger
|
||||
* or indirect eval calls that leak closures. Wrappers thus transitively
|
||||
* wrap all contained closures that escape.
|
||||
*/
|
||||
wscript->flags = script->flags | JSSF_ESCAPE_HAZARD;
|
||||
wscript->version = script->version;
|
||||
wscript->nfixed = script->nfixed;
|
||||
wscript->filename = script->filename;
|
||||
wscript->lineno = script->lineno;
|
||||
wscript->nslots = script->nslots;
|
||||
wscript->staticLevel = script->staticLevel;
|
||||
wscript->principals = script->principals;
|
||||
if (wscript->principals)
|
||||
JSPRINCIPALS_HOLD(cx, wscript->principals);
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
wscript->owner = script->owner;
|
||||
#endif
|
||||
|
||||
/* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
|
||||
FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
|
||||
wfun->u.i.script = wscript;
|
||||
return wfunobj;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
{
|
||||
|
@ -379,6 +560,13 @@ args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|||
return JS_TRUE;
|
||||
JS_ASSERT(fp->argsobj);
|
||||
|
||||
/*
|
||||
* If this function or one in it needs upvars that reach above it in the
|
||||
* scope chain, it must not be a null closure (it could be a flat closure,
|
||||
* or an unoptimized closure -- the latter not necessarily heavyweight).
|
||||
*/
|
||||
JS_ASSERT_IF(fp->fun->u.i.skipmin != 0, !FUN_NULL_CLOSURE(fp->fun));
|
||||
|
||||
slot = JSVAL_TO_INT(id);
|
||||
switch (slot) {
|
||||
case ARGS_CALLEE:
|
||||
|
@ -590,14 +778,62 @@ JSClass js_ArgumentsClass = {
|
|||
#define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2)
|
||||
#define CALL_CLASS_FIXED_RESERVED_SLOTS 2
|
||||
|
||||
/*
|
||||
* A Declarative Environment object stores its active JSStackFrame pointer in
|
||||
* its private slot, just as Call and Arguments objects do.
|
||||
*/
|
||||
JSClass js_DeclEnvClass = {
|
||||
js_Object_str,
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
||||
JSCLASS_NO_OPTIONAL_MEMBERS
|
||||
};
|
||||
|
||||
static JS_REQUIRES_STACK JSBool
|
||||
CheckForEscapingClosure(JSContext *cx, JSObject *obj, jsval *vp)
|
||||
{
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass ||
|
||||
STOBJ_GET_CLASS(obj) == &js_DeclEnvClass);
|
||||
|
||||
if (cx->fp && cx->fp->script && (cx->fp->script->flags & JSSF_ESCAPE_HAZARD)) {
|
||||
jsval v = *vp;
|
||||
|
||||
if (VALUE_IS_FUNCTION(cx, v)) {
|
||||
JSObject *funobj = JSVAL_TO_OBJECT(v);
|
||||
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
|
||||
|
||||
/*
|
||||
* Any escaping null or flat closure that reaches above itself or
|
||||
* contains nested functions that reach above it must be wrapped.
|
||||
* We can wrap only when this Call or Declarative Environment obj
|
||||
* still has an active stack frame associated with it.
|
||||
*/
|
||||
if (FUN_ESCAPE_HAZARD(fun)) {
|
||||
JSStackFrame *fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
|
||||
if (fp) {
|
||||
JSObject *wrapper = WrapEscapingClosure(cx, fp, funobj, fun);
|
||||
if (!wrapper)
|
||||
return false;
|
||||
*vp = OBJECT_TO_JSVAL(wrapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_OPTIMIZED_CLOSURE_LEAK);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static JS_REQUIRES_STACK JSBool
|
||||
CalleeGetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
{
|
||||
return CheckForEscapingClosure(cx, obj, vp);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_GetCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
|
@ -628,11 +864,13 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
|
|||
fp->scopeChain, 0);
|
||||
if (!env)
|
||||
return NULL;
|
||||
env->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp);
|
||||
|
||||
/* Root env before js_DefineNativeProperty (-> JSClass.addProperty). */
|
||||
fp->scopeChain = env;
|
||||
if (!js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName),
|
||||
OBJECT_TO_JSVAL(fp->callee), NULL, NULL,
|
||||
OBJECT_TO_JSVAL(fp->callee),
|
||||
CalleeGetter, NULL,
|
||||
JSPROP_PERMANENT | JSPROP_READONLY,
|
||||
0, 0, NULL)) {
|
||||
return NULL;
|
||||
|
@ -725,10 +963,18 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
|||
}
|
||||
|
||||
/*
|
||||
* Clear the private pointer to fp, which is about to go away (js_Invoke).
|
||||
* Clear private pointers to fp, which is about to go away (js_Invoke).
|
||||
* Do this last because js_GetProperty calls above need to follow the
|
||||
* private slot to find fp.
|
||||
* call object's private slot to find fp.
|
||||
*/
|
||||
if ((fun->flags & JSFUN_LAMBDA) && fun->atom) {
|
||||
JSObject *env = STOBJ_GET_PARENT(callobj);
|
||||
|
||||
JS_ASSERT(STOBJ_GET_CLASS(env) == &js_DeclEnvClass);
|
||||
JS_ASSERT(STOBJ_GET_PRIVATE(env) == fp);
|
||||
JS_SetPrivate(cx, env, NULL);
|
||||
}
|
||||
|
||||
JS_SetPrivate(cx, callobj, NULL);
|
||||
fp->callobj = NULL;
|
||||
return ok;
|
||||
|
@ -888,7 +1134,10 @@ SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|||
JSBool
|
||||
js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||
{
|
||||
return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE);
|
||||
if (!CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE))
|
||||
return JS_FALSE;
|
||||
|
||||
return CheckForEscapingClosure(cx, obj, vp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -1099,10 +1348,20 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|||
break;
|
||||
|
||||
case FUN_CALLER:
|
||||
if (fp && fp->down && fp->down->fun)
|
||||
if (fp && fp->down && fp->down->fun) {
|
||||
/*
|
||||
* See the equivalent assertion in args_getProperty. Here we assert
|
||||
* against a closure leak that goes through foo.caller. Wrapping an
|
||||
* escaping optimized closure guarantees that its function cannot
|
||||
* match the unwrapped stack frame's function (fp->fun) in the loop
|
||||
* commented "Find fun's top-most activation record" above.
|
||||
*/
|
||||
JS_ASSERT_IF(fp->down->fun->u.i.skipmin != 0,
|
||||
!FUN_NULL_CLOSURE(fp->down->fun));
|
||||
*vp = OBJECT_TO_JSVAL(fp->down->callee);
|
||||
else
|
||||
} else {
|
||||
*vp = JSVAL_NULL;
|
||||
}
|
||||
if (!JSVAL_IS_PRIMITIVE(*vp)) {
|
||||
callbacks = JS_GetSecurityCallbacks(cx);
|
||||
if (callbacks && callbacks->checkObjectAccess) {
|
||||
|
@ -1272,10 +1531,12 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
|||
{
|
||||
JSContext *cx;
|
||||
JSFunction *fun;
|
||||
uint32 nullAtom; /* flag to indicate if fun->atom is NULL */
|
||||
uint32 firstword; /* flag telling whether fun->atom is non-null,
|
||||
plus for fun->u.i.skipmin, fun->u.i.wrapper,
|
||||
and 14 bits reserved for future use */
|
||||
uintN nargs, nvars, nupvars, n;
|
||||
uint32 localsword; /* word to xdr argument and variable counts */
|
||||
uint32 flagsword; /* word to xdr upvars count and fun->flags */
|
||||
uint32 localsword; /* word for argument and variable counts */
|
||||
uint32 flagsword; /* word for fun->u.i.nupvars and fun->flags */
|
||||
JSTempValueRooter tvr;
|
||||
JSBool ok;
|
||||
|
||||
|
@ -1288,7 +1549,14 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
|||
JS_GetFunctionName(fun));
|
||||
return JS_FALSE;
|
||||
}
|
||||
nullAtom = !fun->atom;
|
||||
if (fun->u.i.wrapper) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_XDR_CLOSURE_WRAPPER,
|
||||
JS_GetFunctionName(fun));
|
||||
return JS_FALSE;
|
||||
}
|
||||
JS_ASSERT((fun->u.i.wrapper & ~1U) == 0);
|
||||
firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom;
|
||||
nargs = fun->nargs;
|
||||
nvars = fun->u.i.nvars;
|
||||
nupvars = fun->u.i.nupvars;
|
||||
|
@ -1310,9 +1578,9 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
|||
JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr);
|
||||
ok = JS_TRUE;
|
||||
|
||||
if (!JS_XDRUint32(xdr, &nullAtom))
|
||||
if (!JS_XDRUint32(xdr, &firstword))
|
||||
goto bad;
|
||||
if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom))
|
||||
if ((firstword & 1U) && !js_XDRStringAtom(xdr, &fun->atom))
|
||||
goto bad;
|
||||
if (!JS_XDRUint32(xdr, &localsword) ||
|
||||
!JS_XDRUint32(xdr, &flagsword)) {
|
||||
|
@ -1325,6 +1593,8 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
|||
JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
|
||||
nupvars = flagsword >> 16;
|
||||
fun->flags = uint16(flagsword);
|
||||
fun->u.i.skipmin = uint16(firstword >> 2);
|
||||
fun->u.i.wrapper = (firstword >> 1) & 1;
|
||||
}
|
||||
|
||||
/* do arguments and local vars */
|
||||
|
@ -2134,6 +2404,8 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
|
|||
JS_ASSERT(nargs == 0);
|
||||
fun->u.i.nvars = 0;
|
||||
fun->u.i.nupvars = 0;
|
||||
fun->u.i.skipmin = 0;
|
||||
fun->u.i.wrapper = false;
|
||||
fun->u.i.script = NULL;
|
||||
#ifdef DEBUG
|
||||
fun->u.i.names.taggedAtom = 0;
|
||||
|
@ -2210,6 +2482,8 @@ JSObject *
|
|||
js_NewFlatClosure(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
JSObject *closure = js_AllocFlatClosure(cx, fun, cx->fp->scopeChain);
|
||||
if (!closure)
|
||||
return NULL;
|
||||
|
||||
JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script);
|
||||
JS_ASSERT(uva->length <= size_t(closure->dslots[-1]));
|
||||
|
@ -2221,6 +2495,16 @@ js_NewFlatClosure(JSContext *cx, JSFunction *fun)
|
|||
return closure;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
JS_ASSERT(cx->fp->script->flags & JSSF_ESCAPE_HAZARD);
|
||||
JS_ASSERT(cx->fp->fun->flags & JSFUN_HEAVYWEIGHT);
|
||||
JS_ASSERT(!FUN_OPTIMIZED_CLOSURE(cx->fp->fun));
|
||||
|
||||
return WrapEscapingClosure(cx, cx->fp, FUN_OBJECT(fun), fun);
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
|
||||
uintN nargs, uintN attrs)
|
||||
|
|
|
@ -113,6 +113,8 @@ typedef union JSLocalNames {
|
|||
#define FUN_INTERPRETED(fun) (FUN_KIND(fun) >= JSFUN_INTERPRETED)
|
||||
#define FUN_FLAT_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_FLAT_CLOSURE)
|
||||
#define FUN_NULL_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_NULL_CLOSURE)
|
||||
#define FUN_OPTIMIZED_CLOSURE(fun) (FUN_KIND(fun) > JSFUN_INTERPRETED)
|
||||
#define FUN_ESCAPE_HAZARD(fun) (FUN_OPTIMIZED_CLOSURE(fun) && (fun)->u.i.skipmin != 0)
|
||||
#define FUN_SLOW_NATIVE(fun) (!FUN_INTERPRETED(fun) && !((fun)->flags & JSFUN_FAST_NATIVE))
|
||||
#define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL)
|
||||
#define FUN_NATIVE(fun) (FUN_SLOW_NATIVE(fun) ? (fun)->u.n.native : NULL)
|
||||
|
@ -147,6 +149,15 @@ struct JSFunction {
|
|||
uint16 nvars; /* number of local variables */
|
||||
uint16 nupvars; /* number of upvars (computable from script
|
||||
but here for faster access) */
|
||||
uint16 skipmin; /* net skip amount up (toward zero) from
|
||||
script->staticLevel to nearest upvar,
|
||||
including upvars in nested functions */
|
||||
JSPackedBool wrapper; /* true if this function is a wrapper that
|
||||
rewrites bytecode optimized for a function
|
||||
judged non-escaping by the compiler, which
|
||||
then escaped via the debugger or a rogue
|
||||
indirect eval; if true, then this function
|
||||
object's proto is the wrapped object */
|
||||
JSScript *script; /* interpreted bytecode descriptor or null */
|
||||
JSLocalNames names; /* argument and variable names */
|
||||
} i;
|
||||
|
@ -231,6 +242,9 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent);
|
|||
extern JS_REQUIRES_STACK JSObject *
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JS_REQUIRES_STACK JSObject *
|
||||
js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JSFunction *
|
||||
js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
|
||||
uintN nargs, uintN flags);
|
||||
|
@ -265,7 +279,7 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp);
|
|||
extern JSBool
|
||||
js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
|
||||
|
||||
extern JSBool
|
||||
extern JS_REQUIRES_STACK JSBool
|
||||
js_GetCallVar(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
|
||||
|
||||
extern JSBool
|
||||
|
|
|
@ -2589,11 +2589,19 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
|
|||
JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETDSLOT_LENGTH == JSOP_CALLDSLOT_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
|
||||
|
||||
/*
|
||||
* Same for debuggable flat closures defined at top level in another function
|
||||
* or program fragment.
|
||||
*/
|
||||
JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH);
|
||||
|
||||
/*
|
||||
* Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
|
||||
* remain distinct for the decompiler.
|
||||
|
@ -5733,6 +5741,43 @@ js_Interpret(JSContext *cx)
|
|||
}
|
||||
END_CASE(JSOP_GETUPVAR)
|
||||
|
||||
BEGIN_CASE(JSOP_GETUPVAR_DBG)
|
||||
BEGIN_CASE(JSOP_CALLUPVAR_DBG)
|
||||
fun = fp->fun;
|
||||
JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
|
||||
JS_ASSERT(fun->u.i.wrapper);
|
||||
|
||||
/* Scope for tempPool mark and local names allocation in it. */
|
||||
{
|
||||
void *mark = JS_ARENA_MARK(&cx->tempPool);
|
||||
jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
|
||||
if (!names)
|
||||
goto error;
|
||||
|
||||
index = fun->countArgsAndVars() + GET_UINT16(regs.pc);
|
||||
atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
|
||||
id = ATOM_TO_JSID(atom);
|
||||
|
||||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
if (!ok)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!prop)
|
||||
goto atom_not_defined;
|
||||
|
||||
/* Minimize footprint with generic code instead of NATIVE_GET. */
|
||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||
vp = regs.sp;
|
||||
PUSH_OPND(JSVAL_NULL);
|
||||
if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
|
||||
goto error;
|
||||
|
||||
if (op == JSOP_CALLUPVAR_DBG)
|
||||
PUSH_OPND(JSVAL_NULL);
|
||||
END_CASE(JSOP_GETUPVAR_DBG)
|
||||
|
||||
BEGIN_CASE(JSOP_GETDSLOT)
|
||||
BEGIN_CASE(JSOP_CALLDSLOT)
|
||||
obj = fp->callee;
|
||||
|
@ -6018,9 +6063,12 @@ js_Interpret(JSContext *cx)
|
|||
END_CASE(JSOP_DEFFUN)
|
||||
|
||||
BEGIN_CASE(JSOP_DEFFUN_FC)
|
||||
BEGIN_CASE(JSOP_DEFFUN_DBGFC)
|
||||
LOAD_FUNCTION(0);
|
||||
|
||||
obj = js_NewFlatClosure(cx, fun);
|
||||
obj = (op == JSOP_DEFFUN_FC)
|
||||
? js_NewFlatClosure(cx, fun)
|
||||
: js_NewDebuggableFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
rval = OBJECT_TO_JSVAL(obj);
|
||||
|
@ -6117,6 +6165,17 @@ js_Interpret(JSContext *cx)
|
|||
fp->slots[slot] = OBJECT_TO_JSVAL(obj);
|
||||
END_CASE(JSOP_DEFLOCALFUN_FC)
|
||||
|
||||
BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
|
||||
LOAD_FUNCTION(SLOTNO_LEN);
|
||||
|
||||
obj = js_NewDebuggableFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
slot = GET_SLOTNO(regs.pc);
|
||||
fp->slots[slot] = OBJECT_TO_JSVAL(obj);
|
||||
END_CASE(JSOP_DEFLOCALFUN_DBGFC)
|
||||
|
||||
BEGIN_CASE(JSOP_LAMBDA)
|
||||
/* Load the specified function object literal. */
|
||||
LOAD_FUNCTION(0);
|
||||
|
@ -6152,6 +6211,16 @@ js_Interpret(JSContext *cx)
|
|||
PUSH_OPND(OBJECT_TO_JSVAL(obj));
|
||||
END_CASE(JSOP_LAMBDA_FC)
|
||||
|
||||
BEGIN_CASE(JSOP_LAMBDA_DBGFC)
|
||||
LOAD_FUNCTION(0);
|
||||
|
||||
obj = js_NewDebuggableFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
PUSH_OPND(OBJECT_TO_JSVAL(obj));
|
||||
END_CASE(JSOP_LAMBDA_DBGFC)
|
||||
|
||||
BEGIN_CASE(JSOP_CALLEE)
|
||||
PUSH_OPND(OBJECT_TO_JSVAL(fp->callee));
|
||||
END_CASE(JSOP_CALLEE)
|
||||
|
|
|
@ -2804,6 +2804,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
|
||||
case JSOP_GETUPVAR:
|
||||
case JSOP_CALLUPVAR:
|
||||
case JSOP_GETUPVAR_DBG:
|
||||
case JSOP_CALLUPVAR_DBG:
|
||||
case JSOP_GETDSLOT:
|
||||
case JSOP_CALLDSLOT:
|
||||
{
|
||||
|
@ -3974,6 +3976,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
|
||||
case JSOP_LAMBDA:
|
||||
case JSOP_LAMBDA_FC:
|
||||
case JSOP_LAMBDA_DBGFC:
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (sn && SN_TYPE(sn) == SRC_GENEXP) {
|
||||
|
@ -4339,6 +4342,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
|
||||
case JSOP_DEFFUN:
|
||||
case JSOP_DEFFUN_FC:
|
||||
case JSOP_DEFFUN_DBGFC:
|
||||
LOAD_FUNCTION(0);
|
||||
todo = -2;
|
||||
goto do_function;
|
||||
|
|
|
@ -580,3 +580,12 @@ OPDEF(JSOP_LAMBDA_FC, 226,"lambda_fc", NULL, 3, 0, 1, 19, JOF_OBJECT
|
|||
OPDEF(JSOP_OBJTOP, 227,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16)
|
||||
|
||||
OPDEF(JSOP_LOOP, 228, "loop", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Debugger versions of JSOP_{GET,CALL}UPVAR.
|
||||
*/
|
||||
OPDEF(JSOP_GETUPVAR_DBG, 229,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
|
||||
OPDEF(JSOP_CALLUPVAR_DBG, 230,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
|
||||
OPDEF(JSOP_DEFFUN_DBGFC, 231,"deffun_dbgfc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING)
|
||||
OPDEF(JSOP_DEFLOCALFUN_DBGFC,232,"deflocalfun_dbgfc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING)
|
||||
OPDEF(JSOP_LAMBDA_DBGFC, 233,"lambda_dbgfc", NULL, 3, 0, 1, 19, JOF_OBJECT)
|
||||
|
|
|
@ -819,7 +819,7 @@ JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *cal
|
|||
/* Null script early in case of error, to reduce our code footprint. */
|
||||
script = NULL;
|
||||
|
||||
cg.flags |= (uint16) tcflags;
|
||||
cg.flags |= uint16(tcflags);
|
||||
cg.scopeChain = scopeChain;
|
||||
if (!SetStaticLevel(&cg, TCF_GET_STATIC_LEVEL(tcflags)))
|
||||
goto out;
|
||||
|
@ -1688,12 +1688,21 @@ JSCompiler::analyzeFunctions(JSFunctionBox *funbox, uint16& tcflags)
|
|||
* process is potentially exponential in the number of functions, but generally
|
||||
* not so complex. But it can't be done during a single recursive traversal of
|
||||
* the funbox tree, so we must use a work queue.
|
||||
*
|
||||
* Return the minimal "skipmin" for funbox and its siblings. This is the delta
|
||||
* between the static level of the bodies of funbox and its peers (which must
|
||||
* be funbox->level + 1), and the static level of the nearest upvar among all
|
||||
* the upvars contained by funbox and its peers. If there are no upvars, return
|
||||
* FREE_STATIC_LEVEL. Thus this function never returns 0.
|
||||
*/
|
||||
static void
|
||||
static uintN
|
||||
FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
|
||||
{
|
||||
uintN allskipmin = FREE_STATIC_LEVEL;
|
||||
|
||||
do {
|
||||
JSParseNode *fn = funbox->node;
|
||||
JSFunction *fun = (JSFunction *) funbox->object;
|
||||
int fnlevel = level;
|
||||
|
||||
/*
|
||||
|
@ -1709,35 +1718,78 @@ FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
|
|||
kid->node->setFunArg();
|
||||
}
|
||||
|
||||
if (fn->isFunArg()) {
|
||||
queue->push(funbox);
|
||||
fnlevel = int(funbox->level);
|
||||
} else {
|
||||
JSParseNode *pn = fn->pn_body;
|
||||
/*
|
||||
* Compute in skipmin the least distance from fun's static level up to
|
||||
* an upvar, whether used directly by fun, or indirectly by a function
|
||||
* nested in fun.
|
||||
*/
|
||||
uintN skipmin = FREE_STATIC_LEVEL;
|
||||
JSParseNode *pn = fn->pn_body;
|
||||
|
||||
if (pn->pn_type == TOK_UPVARS) {
|
||||
JSAtomList upvars(pn->pn_names);
|
||||
JS_ASSERT(upvars.count != 0);
|
||||
if (pn->pn_type == TOK_UPVARS) {
|
||||
JSAtomList upvars(pn->pn_names);
|
||||
JS_ASSERT(upvars.count != 0);
|
||||
|
||||
JSAtomListIterator iter(&upvars);
|
||||
JSAtomListElement *ale;
|
||||
JSAtomListIterator iter(&upvars);
|
||||
JSAtomListElement *ale;
|
||||
|
||||
while ((ale = iter()) != NULL) {
|
||||
JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
|
||||
while ((ale = iter()) != NULL) {
|
||||
JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
|
||||
|
||||
if (!lexdep->isFreeVar() && int(lexdep->frameLevel()) <= fnlevel) {
|
||||
if (!lexdep->isFreeVar()) {
|
||||
uintN upvarLevel = lexdep->frameLevel();
|
||||
|
||||
if (int(upvarLevel) <= fnlevel)
|
||||
fn->setFunArg();
|
||||
queue->push(funbox);
|
||||
fnlevel = int(funbox->level);
|
||||
break;
|
||||
}
|
||||
|
||||
uintN skip = (funbox->level + 1) - upvarLevel;
|
||||
if (skip < skipmin)
|
||||
skipmin = skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (funbox->kids)
|
||||
FindFunArgs(funbox->kids, fnlevel, queue);
|
||||
/*
|
||||
* If this function escapes, whether directly (the parser detects such
|
||||
* escapes) or indirectly (because this non-escaping function uses an
|
||||
* upvar that reaches across an outer function boundary where the outer
|
||||
* function escapes), enqueue it for further analysis, and bump fnlevel
|
||||
* to trap any non-escaping children.
|
||||
*/
|
||||
if (fn->isFunArg()) {
|
||||
queue->push(funbox);
|
||||
fnlevel = int(funbox->level);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now process the current function's children, and recalibrate their
|
||||
* cumulative skipmin to be relative to the current static level.
|
||||
*/
|
||||
if (funbox->kids) {
|
||||
uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
|
||||
|
||||
JS_ASSERT(kidskipmin != 0);
|
||||
if (kidskipmin != FREE_STATIC_LEVEL) {
|
||||
--kidskipmin;
|
||||
if (kidskipmin != 0 && kidskipmin < skipmin)
|
||||
skipmin = kidskipmin;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, after we've traversed all of the current function's kids,
|
||||
* minimize fun's skipmin against our accumulated skipmin. Do likewise
|
||||
* with allskipmin, but minimize across funbox and all of its siblings,
|
||||
* to compute our return value.
|
||||
*/
|
||||
if (skipmin != FREE_STATIC_LEVEL) {
|
||||
fun->u.i.skipmin = skipmin;
|
||||
if (skipmin < allskipmin)
|
||||
allskipmin = skipmin;
|
||||
}
|
||||
} while ((funbox = funbox->siblings) != NULL);
|
||||
|
||||
return allskipmin;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1767,13 +1819,14 @@ JSCompiler::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
|
|||
!lexdep->isFunArg() &&
|
||||
lexdep->kind() == JSDefinition::FUNCTION) {
|
||||
/*
|
||||
* Mark this formerly-Algol-like function as a funarg,
|
||||
* since it is referenced from a funarg and can no longer
|
||||
* use JSOP_{GET,CALL}UPVAR to access upvars.
|
||||
* Mark this formerly-Algol-like function as an escaping
|
||||
* function (i.e., as a funarg), because it is used from a
|
||||
* funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
|
||||
* access upvars.
|
||||
*
|
||||
* Progress is guaranteed since we set PND_FUNARG here,
|
||||
* which suppresses revisiting this function (namely the
|
||||
* !lexdep->isFunArg() test just above).
|
||||
* Progress is guaranteed because we set the funarg flag
|
||||
* here, which suppresses revisiting this function (thanks
|
||||
* to the !lexdep->isFunArg() test just above).
|
||||
*/
|
||||
lexdep->setFunArg();
|
||||
|
||||
|
|
|
@ -1530,6 +1530,8 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
|||
cg->regexpList.finish(JS_SCRIPT_REGEXPS(script));
|
||||
if (cg->flags & TCF_NO_SCRIPT_RVAL)
|
||||
script->flags |= JSSF_NO_SCRIPT_RVAL;
|
||||
if ((cg->flags & TCF_COMPILE_N_GO) && cg->compiler->callerFrame)
|
||||
script->flags |= JSSF_ESCAPE_HAZARD;
|
||||
|
||||
if (cg->upvarList.count != 0) {
|
||||
JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
|
||||
|
|
|
@ -135,6 +135,9 @@ struct JSScript {
|
|||
#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 */
|
||||
#define JSSF_ESCAPE_HAZARD 0x04 /* script (including functions) was
|
||||
created by dynamically scoped eval
|
||||
or debugger eval-in-frame API */
|
||||
|
||||
static JS_INLINE uintN
|
||||
StackDepth(JSScript *script)
|
||||
|
|
|
@ -10797,6 +10797,19 @@ TraceRecorder::record_JSOP_LOOP()
|
|||
return JSRS_CONTINUE;
|
||||
}
|
||||
|
||||
#define DBG_STUB(OP) \
|
||||
JS_REQUIRES_STACK JSRecordingStatus \
|
||||
TraceRecorder::record_##OP() \
|
||||
{ \
|
||||
ABORT_TRACE("can't trace " #OP); \
|
||||
}
|
||||
|
||||
DBG_STUB(JSOP_GETUPVAR_DBG)
|
||||
DBG_STUB(JSOP_CALLUPVAR_DBG)
|
||||
DBG_STUB(JSOP_DEFFUN_DBGFC)
|
||||
DBG_STUB(JSOP_DEFLOCALFUN_DBGFC)
|
||||
DBG_STUB(JSOP_LAMBDA_DBGFC)
|
||||
|
||||
#ifdef JS_JIT_SPEW
|
||||
/* Prints information about entry typemaps and unstable exits for all peers at a PC */
|
||||
void
|
||||
|
|
|
@ -204,7 +204,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 - 48)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 49)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
|
Загрузка…
Ссылка в новой задаче