зеркало из https://github.com/mozilla/pjs.git
Bug 516255 - Eagerly copy initial parameter values into the arguments object when a function's parameters might be mutated, and rely on normal resolution behavior in the remaining cases when parameters are never modified. r=dmandelin
This commit is contained in:
Родитель
580ef13257
Коммит
e841d02d4c
|
@ -2821,7 +2821,9 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
|||
return JS_FALSE;
|
||||
if (left->pn_op == JSOP_ARGUMENTS &&
|
||||
JSDOUBLE_IS_INT32(next->pn_dval, &slot) &&
|
||||
(jsuint)slot < JS_BIT(16)) {
|
||||
jsuint(slot) < JS_BIT(16) &&
|
||||
(!cg->inStrictMode() ||
|
||||
(!cg->mutatesParameter() && !cg->callsEval()))) {
|
||||
/*
|
||||
* arguments[i]() requires arguments object as "this".
|
||||
* Check that we never generates list for that usage.
|
||||
|
@ -2895,7 +2897,9 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
|||
return JS_FALSE;
|
||||
if (left->pn_op == JSOP_ARGUMENTS &&
|
||||
JSDOUBLE_IS_INT32(right->pn_dval, &slot) &&
|
||||
(jsuint)slot < JS_BIT(16)) {
|
||||
jsuint(slot) < JS_BIT(16) &&
|
||||
(!cg->inStrictMode() ||
|
||||
(!cg->mutatesParameter() && !cg->callsEval()))) {
|
||||
left->pn_offset = right->pn_offset = top;
|
||||
EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot);
|
||||
return JS_TRUE;
|
||||
|
@ -3547,6 +3551,13 @@ js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (cg->needsEagerArguments()) {
|
||||
CG_SWITCH_TO_PROLOG(cg);
|
||||
if (js_Emit1(cx, cg, JSOP_ARGUMENTS) < 0 || js_Emit1(cx, cg, JSOP_POP) < 0)
|
||||
return false;
|
||||
CG_SWITCH_TO_MAIN(cg);
|
||||
}
|
||||
|
||||
if (cg->flags & TCF_FUN_UNBRAND_THIS) {
|
||||
if (js_Emit1(cx, cg, JSOP_UNBRANDTHIS) < 0)
|
||||
return false;
|
||||
|
|
|
@ -197,6 +197,7 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
|
|||
static void
|
||||
PutArguments(JSContext *cx, JSObject *argsobj, Value *args)
|
||||
{
|
||||
JS_ASSERT(argsobj->isNormalArguments());
|
||||
uint32 argc = argsobj->getArgsInitialLength();
|
||||
for (uint32 i = 0; i != argc; ++i) {
|
||||
if (!argsobj->getArgsElement(i).isMagic(JS_ARGS_HOLE))
|
||||
|
@ -229,8 +230,22 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
|
|||
if (!argsobj)
|
||||
return argsobj;
|
||||
|
||||
/* Link the new object to fp so it can get actual argument values. */
|
||||
argsobj->setPrivate(fp);
|
||||
/*
|
||||
* Strict mode functions have arguments which copy the initial parameter
|
||||
* values. It is the caller's responsibility to get the arguments object
|
||||
* before any parameters are modified! (The emitter ensures this by
|
||||
* synthesizing an arguments access at the start of any strict mode
|
||||
* function which contains an assignment to a parameter or which calls
|
||||
* eval.) Non-strict mode arguments use the frame pointer to retrieve
|
||||
* up-to-date parameter values.
|
||||
*/
|
||||
if (argsobj->isStrictArguments()) {
|
||||
JS_ASSERT_IF(fp->argc > 0, argsobj->dslots[-1].toPrivateUint32() >= fp->argc);
|
||||
memcpy(argsobj->dslots, fp->argv, fp->argc * sizeof(Value));
|
||||
} else {
|
||||
argsobj->setPrivate(fp);
|
||||
}
|
||||
|
||||
fp->setArgsObj(argsobj);
|
||||
return argsobj;
|
||||
}
|
||||
|
@ -239,9 +254,13 @@ void
|
|||
js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
JSObject *argsobj = fp->getArgsObj();
|
||||
JS_ASSERT(argsobj->getPrivate() == fp);
|
||||
PutArguments(cx, argsobj, fp->argv);
|
||||
argsobj->setPrivate(NULL);
|
||||
if (argsobj->isNormalArguments()) {
|
||||
JS_ASSERT(argsobj->getPrivate() == fp);
|
||||
PutArguments(cx, argsobj, fp->argv);
|
||||
argsobj->setPrivate(NULL);
|
||||
} else {
|
||||
JS_ASSERT(!argsobj->getPrivate());
|
||||
}
|
||||
fp->setArgsObj(NULL);
|
||||
}
|
||||
|
||||
|
@ -256,7 +275,17 @@ js_Arguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
|
|||
JSObject *argsobj = NewArguments(cx, parent, argc, callee);
|
||||
if (!argsobj)
|
||||
return NULL;
|
||||
argsobj->setPrivate(JS_ARGUMENT_OBJECT_ON_TRACE);
|
||||
|
||||
if (callee->getFunctionPrivate()->inStrictMode()) {
|
||||
/*
|
||||
* Strict mode callers must copy arguments into the created arguments
|
||||
* object.
|
||||
*/
|
||||
JS_ASSERT(!argsobj->getPrivate());
|
||||
} else {
|
||||
argsobj->setPrivate(JS_ARGUMENT_OBJECT_ON_TRACE);
|
||||
}
|
||||
|
||||
return argsobj;
|
||||
}
|
||||
#endif
|
||||
|
@ -268,6 +297,7 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_Arguments, CONTEXT, OBJECT, UINT32, OBJE
|
|||
JSBool JS_FASTCALL
|
||||
js_PutArguments(JSContext *cx, JSObject *argsobj, Value *args)
|
||||
{
|
||||
JS_ASSERT(argsobj->isNormalArguments());
|
||||
JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENT_OBJECT_ON_TRACE);
|
||||
PutArguments(cx, argsobj, args);
|
||||
argsobj->setPrivate(NULL);
|
||||
|
@ -637,14 +667,9 @@ StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||
*/
|
||||
uintN arg = uintN(JSID_TO_INT(id));
|
||||
if (arg < obj->getArgsInitialLength()) {
|
||||
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
|
||||
if (fp) {
|
||||
*vp = fp->argv[arg];
|
||||
} else {
|
||||
const Value &v = obj->getArgsElement(arg);
|
||||
if (!v.isMagic(JS_ARGS_HOLE))
|
||||
*vp = v;
|
||||
}
|
||||
const Value &v = obj->getArgsElement(arg);
|
||||
if (!v.isMagic(JS_ARGS_HOLE))
|
||||
*vp = v;
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
|
||||
|
|
|
@ -488,6 +488,16 @@ js_GetArgsValue(JSContext *cx, JSStackFrame *fp, js::Value *vp);
|
|||
extern JSBool
|
||||
js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, js::Value *vp);
|
||||
|
||||
/*
|
||||
* Get the arguments object for the given frame. If the frame is strict mode
|
||||
* code, its current arguments will be copied into the arguments object.
|
||||
*
|
||||
* NB: Callers *must* get the arguments object before any parameters are
|
||||
* mutated when the frame is strict mode code! The emitter ensures this
|
||||
* occurs for strict mode functions containing syntax which might mutate a
|
||||
* named parameter by synthesizing an arguments access at the start of the
|
||||
* function.
|
||||
*/
|
||||
extern JSObject *
|
||||
js_GetArgsObject(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
|
|
|
@ -1131,8 +1131,7 @@ CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
|||
bool
|
||||
CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs)
|
||||
{
|
||||
if (tc->needStrictChecks() &&
|
||||
lhs->pn_type == TOK_NAME) {
|
||||
if (tc->needStrictChecks() && lhs->pn_type == TOK_NAME) {
|
||||
JSAtom *atom = lhs->pn_atom;
|
||||
JSAtomState *atomState = &cx->runtime->atomState;
|
||||
if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
|
||||
|
@ -2821,8 +2820,8 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
|||
JSFunction *fun = (JSFunction *) funbox->object;
|
||||
|
||||
/* Now parse formal argument list and compute fun->nargs. */
|
||||
JSParseNode *list = NULL;
|
||||
if (!functionArguments(funtc, funbox, fun, &list))
|
||||
JSParseNode *prolog = NULL;
|
||||
if (!functionArguments(funtc, funbox, fun, &prolog))
|
||||
return NULL;
|
||||
|
||||
if (type == GETTER && fun->nargs > 0) {
|
||||
|
@ -2866,6 +2865,24 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
|||
#endif
|
||||
pn->pn_pos.end = tokenStream.currentToken().pos.end;
|
||||
|
||||
/*
|
||||
* Strict mode functions' arguments objects copy initial parameter values.
|
||||
* We create arguments objects lazily -- but that doesn't work for strict
|
||||
* mode functions where a parameter might be modified and arguments might
|
||||
* be accessed. For such functions we synthesize an access to arguments to
|
||||
* initialize it with the original parameter values.
|
||||
*/
|
||||
if (funtc.inStrictMode()) {
|
||||
/*
|
||||
* Fruit of the poisonous tree: eval forces eager arguments
|
||||
* creation in (strict mode) parent functions.
|
||||
*/
|
||||
if (outertc->inFunction() && outertc->inStrictMode()) {
|
||||
if (funtc.callsEval())
|
||||
outertc->noteCallsEval();
|
||||
}
|
||||
}
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
/*
|
||||
* If there were destructuring formal parameters, prepend the initializing
|
||||
|
@ -2874,7 +2891,7 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
|||
* parameter destructuring code without bracing the decompilation of the
|
||||
* function body's lexical scope.
|
||||
*/
|
||||
if (list) {
|
||||
if (prolog) {
|
||||
if (body->pn_arity != PN_LIST) {
|
||||
JSParseNode *block;
|
||||
|
||||
|
@ -2894,7 +2911,7 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
|||
|
||||
item->pn_type = TOK_SEMI;
|
||||
item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
|
||||
item->pn_kid = list;
|
||||
item->pn_kid = prolog;
|
||||
item->pn_next = body->pn_head;
|
||||
body->pn_head = item;
|
||||
if (body->pn_tail == &body->pn_head)
|
||||
|
@ -5663,15 +5680,6 @@ Parser::statement()
|
|||
return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
NoteArgumentsUse(JSTreeContext *tc)
|
||||
{
|
||||
JS_ASSERT(tc->inFunction());
|
||||
tc->flags |= TCF_FUN_USES_ARGUMENTS;
|
||||
if (tc->funbox)
|
||||
tc->funbox->node->pn_dflags |= PND_FUNARG;
|
||||
}
|
||||
|
||||
JSParseNode *
|
||||
Parser::variables(bool inLetHead)
|
||||
{
|
||||
|
@ -5837,7 +5845,7 @@ Parser::variables(bool inLetHead)
|
|||
|
||||
if (tc->inFunction() &&
|
||||
atom == context->runtime->atomState.argumentsAtom) {
|
||||
NoteArgumentsUse(tc);
|
||||
tc->noteArgumentsUse();
|
||||
if (!let)
|
||||
tc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
}
|
||||
|
@ -8188,7 +8196,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
|
|||
* a reference of the form foo.arguments, which ancient code may
|
||||
* still use instead of arguments (more hate).
|
||||
*/
|
||||
NoteArgumentsUse(tc);
|
||||
tc->noteArgumentsUse();
|
||||
|
||||
/*
|
||||
* Bind early to JSOP_ARGUMENTS to relieve later code from having
|
||||
|
|
|
@ -458,9 +458,9 @@ public:
|
|||
#define PNX_DESTRUCT 0x200 /* destructuring special cases:
|
||||
1. shorthand syntax used, at present
|
||||
object destructuring ({x,y}) only;
|
||||
2. the first child of function body
|
||||
is code evaluating destructuring
|
||||
arguments */
|
||||
2. code evaluating destructuring
|
||||
arguments occurs before function
|
||||
body */
|
||||
#define PNX_HOLEY 0x400 /* array initialiser has holes */
|
||||
|
||||
uintN frameLevel() const {
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
|
||||
#include "jsatominlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
#include "jsfuninlines.h"
|
||||
#include "jspropertycacheinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
|
@ -3449,8 +3450,10 @@ FlushNativeStackFrame(JSContext* cx, unsigned callDepth, const JSValueType* mp,
|
|||
for (; n != 0; fp = fp->down) {
|
||||
--n;
|
||||
if (fp->argv) {
|
||||
if (fp->hasArgsObj() && fp->getArgsObj()->getPrivate() == JS_ARGUMENT_OBJECT_ON_TRACE)
|
||||
if (fp->hasArgsObj() && fp->getArgsObj()->getPrivate() == JS_ARGUMENT_OBJECT_ON_TRACE) {
|
||||
JS_ASSERT(fp->getArgsObj()->isNormalArguments());
|
||||
fp->getArgsObj()->setPrivate(fp);
|
||||
}
|
||||
|
||||
JS_ASSERT(fp->argv[-1].isObjectOrNull());
|
||||
JS_ASSERT(fp->callee()->isFunction());
|
||||
|
@ -10289,7 +10292,7 @@ TraceRecorder::clearCurrentFrameSlotsFromTracker(Tracker& which)
|
|||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::putActivationObjects()
|
||||
{
|
||||
bool have_args = cx->fp->hasArgsObj() && cx->fp->argc;
|
||||
bool have_args = cx->fp->hasArgsObj() && !cx->fp->getArgsObj()->isStrictArguments() && cx->fp->argc > 0;
|
||||
bool have_call = cx->fp->hasFunction() &&
|
||||
JSFUN_HEAVYWEIGHT_TEST(cx->fp->getFunction()->flags) &&
|
||||
cx->fp->getFunction()->countArgsAndVars();
|
||||
|
@ -10645,7 +10648,7 @@ TraceRecorder::record_JSOP_IFNE()
|
|||
}
|
||||
|
||||
LIns*
|
||||
TraceRecorder::newArguments(LIns* callee_ins)
|
||||
TraceRecorder::newArguments(LIns* callee_ins, bool strict)
|
||||
{
|
||||
LIns* global_ins = INS_CONSTOBJ(globalObj);
|
||||
LIns* argc_ins = INS_CONST(cx->fp->argc);
|
||||
|
@ -10653,6 +10656,15 @@ TraceRecorder::newArguments(LIns* callee_ins)
|
|||
LIns* args[] = { callee_ins, argc_ins, global_ins, cx_ins };
|
||||
LIns* call_ins = lir->insCall(&js_Arguments_ci, args);
|
||||
guard(false, lir->insEqP_0(call_ins), OOM_EXIT);
|
||||
|
||||
if (strict) {
|
||||
JSStackFrame* fp = cx->fp;
|
||||
uintN argc = fp->argc;
|
||||
LIns* argsSlots_ins = NULL;
|
||||
for (uintN i = 0; i < argc; i++)
|
||||
stobj_set_dslot(call_ins, i, argsSlots_ins, fp->argv[i], get(&fp->argv[i]));
|
||||
}
|
||||
|
||||
return call_ins;
|
||||
}
|
||||
|
||||
|
@ -10665,12 +10677,15 @@ TraceRecorder::record_JSOP_ARGUMENTS()
|
|||
if (cx->fp->flags & JSFRAME_OVERRIDE_ARGS)
|
||||
RETURN_STOP_A("Can't trace |arguments| if |arguments| is assigned to");
|
||||
|
||||
JSStackFrame* const fp = cx->fp;
|
||||
|
||||
LIns* a_ins = getFrameObjPtr(cx->fp->addressArgsObj());
|
||||
LIns* args_ins;
|
||||
LIns* callee_ins = get(&cx->fp->argv[-2]);
|
||||
LIns* callee_ins = get(&fp->argv[-2]);
|
||||
bool strict = fp->getFunction()->inStrictMode();
|
||||
if (a_ins->isImmP()) {
|
||||
// |arguments| is set to 0 by EnterFrame on this trace, so call to create it.
|
||||
args_ins = newArguments(callee_ins);
|
||||
args_ins = newArguments(callee_ins, strict);
|
||||
} else {
|
||||
// Generate LIR to create arguments only if it has not already been created.
|
||||
|
||||
|
@ -10683,7 +10698,7 @@ TraceRecorder::record_JSOP_ARGUMENTS()
|
|||
LIns* label1 = lir->ins0(LIR_label);
|
||||
br1->setTarget(label1);
|
||||
|
||||
LIns* call_ins = newArguments(callee_ins);
|
||||
LIns* call_ins = newArguments(callee_ins, strict);
|
||||
lir->insStore(call_ins, mem_ins, 0, ACCSET_OTHER);
|
||||
|
||||
LIns* label2 = lir->ins0(LIR_label);
|
||||
|
@ -10693,7 +10708,7 @@ TraceRecorder::record_JSOP_ARGUMENTS()
|
|||
}
|
||||
|
||||
stack(0, args_ins);
|
||||
setFrameObjPtr(cx->fp->addressArgsObj(), args_ins);
|
||||
setFrameObjPtr(fp->addressArgsObj(), args_ins);
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1159,7 +1159,7 @@ class TraceRecorder
|
|||
JS_REQUIRES_STACK nanojit::LIns* makeNumberInt32(nanojit::LIns* f);
|
||||
JS_REQUIRES_STACK nanojit::LIns* stringify(const Value& v);
|
||||
|
||||
JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins);
|
||||
JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins, bool strict);
|
||||
|
||||
JS_REQUIRES_STACK bool canCallImacro() const;
|
||||
JS_REQUIRES_STACK RecordingStatus callImacro(jsbytecode* imacro);
|
||||
|
|
Загрузка…
Ссылка в новой задаче