Bug 729382 - move the rest of the CallObject into ScopeObject (r=waldo)

This commit is contained in:
Luke Wagner 2012-02-23 13:58:39 -08:00
Родитель 89ae88e849
Коммит bc5c79813f
10 изменённых файлов: 391 добавлений и 410 удалений

Просмотреть файл

@ -589,7 +589,7 @@ JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fpArg)
* null returned above or in the #else * null returned above or in the #else
*/ */
if (!fp->hasCallObj() && fp->isNonEvalFunctionFrame()) if (!fp->hasCallObj() && fp->isNonEvalFunctionFrame())
return CreateFunCallObject(cx, fp); return CallObject::createForFunction(cx, fp);
return &fp->callObj(); return &fp->callObj();
} }
@ -844,10 +844,10 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
| (!shape->writable() ? JSPD_READONLY : 0) | (!shape->writable() ? JSPD_READONLY : 0)
| (!shape->configurable() ? JSPD_PERMANENT : 0); | (!shape->configurable() ? JSPD_PERMANENT : 0);
pd->spare = 0; pd->spare = 0;
if (shape->getter() == GetCallArg) { if (shape->getter() == CallObject::getArgOp) {
pd->slot = shape->shortid(); pd->slot = shape->shortid();
pd->flags |= JSPD_ARGUMENT; pd->flags |= JSPD_ARGUMENT;
} else if (shape->getter() == GetCallVar) { } else if (shape->getter() == CallObject::getVarOp) {
pd->slot = shape->shortid(); pd->slot = shape->shortid();
pd->flags |= JSPD_VARIABLE; pd->flags |= JSPD_VARIABLE;
} else { } else {

Просмотреть файл

@ -599,350 +599,6 @@ Class js::StrictArgumentsObjectClass = {
} }
}; };
namespace js {
CallObject *
CreateFunCallObject(JSContext *cx, StackFrame *fp)
{
JS_ASSERT(fp->isNonEvalFunctionFrame());
JS_ASSERT(!fp->hasCallObj());
JSObject *scopeChain = &fp->scopeChain();
JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(),
scopeChain->getPrivate() != fp);
/*
* For a named function expression Call's parent points to an environment
* object holding function's name.
*/
if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) {
scopeChain = DeclEnvObject::create(cx, fp);
if (!scopeChain)
return NULL;
if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName),
ObjectValue(fp->callee()), NULL, NULL,
JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
return NULL;
}
}
CallObject *callobj = CallObject::create(cx, fp->script(), *scopeChain, &fp->callee());
if (!callobj)
return NULL;
callobj->setStackFrame(fp);
fp->setScopeChainWithOwnCallObj(*callobj);
return callobj;
}
CallObject *
CreateEvalCallObject(JSContext *cx, StackFrame *fp)
{
CallObject *callobj = CallObject::create(cx, fp->script(), fp->scopeChain(), NULL);
if (!callobj)
return NULL;
callobj->setStackFrame(fp);
fp->setScopeChainWithOwnCallObj(*callobj);
return callobj;
}
} // namespace js
void
js_PutCallObject(StackFrame *fp)
{
CallObject &callobj = fp->callObj().asCall();
JS_ASSERT(callobj.maybeStackFrame() == fp);
JS_ASSERT_IF(fp->isEvalFrame(), fp->isStrictEvalFrame());
JS_ASSERT(fp->isEvalFrame() == callobj.isForEval());
/* Get the arguments object to snapshot fp's actual argument values. */
if (fp->hasArgsObj()) {
if (callobj.arguments().isMagic(JS_UNASSIGNED_ARGUMENTS))
callobj.setArguments(ObjectValue(fp->argsObj()));
js_PutArgsObject(fp);
}
JSScript *script = fp->script();
Bindings &bindings = script->bindings;
if (callobj.isForEval()) {
JS_ASSERT(script->strictModeCode);
JS_ASSERT(bindings.countArgs() == 0);
/* This could be optimized as below, but keep it simple for now. */
callobj.copyValues(0, NULL, bindings.countVars(), fp->slots());
} else {
JSFunction *fun = fp->fun();
JS_ASSERT(script == callobj.getCalleeFunction()->script());
JS_ASSERT(script == fun->script());
uintN n = bindings.countArgsAndVars();
if (n > 0) {
uint32_t nvars = bindings.countVars();
uint32_t nargs = bindings.countArgs();
JS_ASSERT(fun->nargs == nargs);
JS_ASSERT(nvars + nargs == n);
JSScript *script = fun->script();
if (script->usesEval
#ifdef JS_METHODJIT
|| script->debugMode
#endif
) {
callobj.copyValues(nargs, fp->formalArgs(), nvars, fp->slots());
} else {
/*
* For each arg & var that is closed over, copy it from the stack
* into the call object. We use initArg/VarUnchecked because,
* when you call a getter on a call object, js_NativeGetInline
* caches the return value in the slot, so we can't assert that
* it's undefined.
*/
uint32_t nclosed = script->nClosedArgs;
for (uint32_t i = 0; i < nclosed; i++) {
uint32_t e = script->getClosedArg(i);
#ifdef JS_GC_ZEAL
callobj.setArg(e, fp->formalArg(e));
#else
callobj.initArgUnchecked(e, fp->formalArg(e));
#endif
}
nclosed = script->nClosedVars;
for (uint32_t i = 0; i < nclosed; i++) {
uint32_t e = script->getClosedVar(i);
#ifdef JS_GC_ZEAL
callobj.setVar(e, fp->slots()[e]);
#else
callobj.initVarUnchecked(e, fp->slots()[e]);
#endif
}
}
/*
* Update the args and vars for the active call if this is an outer
* function in a script nesting.
*/
types::TypeScriptNesting *nesting = script->nesting();
if (nesting && script->isOuterFunction) {
nesting->argArray = callobj.argArray();
nesting->varArray = callobj.varArray();
}
}
/* Clear private pointers to fp, which is about to go away. */
if (js_IsNamedLambda(fun)) {
JSObject &env = callobj.enclosingScope();
JS_ASSERT(env.asDeclEnv().maybeStackFrame() == fp);
env.setPrivate(NULL);
}
}
callobj.setStackFrame(NULL);
}
namespace js {
static JSBool
GetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
CallObject &callobj = obj->asCall();
StackFrame *fp = callobj.maybeStackFrame();
if (fp && callobj.arguments().isMagic(JS_UNASSIGNED_ARGUMENTS)) {
JSObject *argsobj = js_GetArgsObject(cx, fp);
if (!argsobj)
return false;
vp->setObject(*argsobj);
} else {
/* Nested functions cannot get the 'arguments' of enclosing scopes. */
JS_ASSERT(!callobj.arguments().isMagic(JS_UNASSIGNED_ARGUMENTS));
*vp = callobj.arguments();
}
return true;
}
static JSBool
SetCallArguments(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
/* Nested functions cannot set the 'arguments' of enclosing scopes. */
JS_ASSERT(obj->asCall().maybeStackFrame());
obj->asCall().setArguments(*vp);
return true;
}
JSBool
GetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
if (StackFrame *fp = callobj.maybeStackFrame())
*vp = fp->formalArg(i);
else
*vp = callobj.arg(i);
return true;
}
JSBool
SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
if (StackFrame *fp = callobj.maybeStackFrame())
fp->formalArg(i) = *vp;
else
callobj.setArg(i, *vp);
JSFunction *fun = callobj.getCalleeFunction();
JSScript *script = fun->script();
if (!script->ensureHasTypes(cx))
return false;
TypeScript::SetArgument(cx, script, i, *vp);
return true;
}
JSBool
GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
*vp = callobj.getCallee()->toFunction()->getFlatClosureUpvar(i);
return true;
}
JSBool
SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
callobj.getCallee()->toFunction()->setFlatClosureUpvar(i, *vp);
return true;
}
JSBool
GetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
if (StackFrame *fp = callobj.maybeStackFrame())
*vp = fp->varSlot(i);
else
*vp = callobj.var(i);
return true;
}
JSBool
SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
if (StackFrame *fp = callobj.maybeStackFrame())
fp->varSlot(i) = *vp;
else
callobj.setVar(i, *vp);
JSFunction *fun = callobj.getCalleeFunction();
JSScript *script = fun->script();
if (!script->ensureHasTypes(cx))
return false;
TypeScript::SetLocal(cx, script, i, *vp);
return true;
}
} // namespace js
static JSBool
call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
{
JS_ASSERT(!obj->getProto());
if (!JSID_IS_ATOM(id))
return true;
JSObject *callee = obj->asCall().getCallee();
#ifdef DEBUG
if (callee) {
JSScript *script = callee->toFunction()->script();
JS_ASSERT(!script->bindings.hasBinding(cx, JSID_TO_ATOM(id)));
}
#endif
/*
* Resolve arguments so that we never store a particular Call object's
* arguments object reference in a Call prototype's |arguments| slot.
*
* Include JSPROP_ENUMERATE for consistency with all other Call object
* properties; see js::Bindings::add and js::Interpret's JSOP_DEFFUN
* rebinding-Call-property logic.
*/
if (callee && id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
if (!DefineNativeProperty(cx, obj, id, UndefinedValue(),
GetCallArguments, SetCallArguments,
JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE,
0, 0, DNP_DONT_PURGE)) {
return false;
}
*objp = obj;
return true;
}
/* Control flow reaches here only if id was not resolved. */
return true;
}
static void
call_trace(JSTracer *trc, JSObject *obj)
{
JS_ASSERT(obj->isCall());
/* Mark any generator frame, as for arguments objects. */
#if JS_HAS_GENERATORS
StackFrame *fp = (StackFrame *) obj->getPrivate();
if (fp && fp->isFloatingGenerator())
MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object");
#endif
}
JS_PUBLIC_DATA(Class) js::CallClass = {
"Call",
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS) |
JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
(JSResolveOp)call_resolve,
NULL, /* convert: Leave it NULL so we notice if calls ever escape */
NULL, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
call_trace
};
bool bool
StackFrame::getValidCalleeObject(JSContext *cx, Value *vp) StackFrame::getValidCalleeObject(JSContext *cx, Value *vp)
{ {

Просмотреть файл

@ -362,30 +362,6 @@ js_PutCallObject(js::StackFrame *fp);
namespace js { namespace js {
CallObject *
CreateFunCallObject(JSContext *cx, StackFrame *fp);
CallObject *
CreateEvalCallObject(JSContext *cx, StackFrame *fp);
extern JSBool
GetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
GetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp);
extern JSBool
SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp);
extern JSBool
SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp);
/* /*
* Function extended with reserved slots for use by various kinds of functions. * Function extended with reserved slots for use by various kinds of functions.
* Most functions do not have these extensions, but enough are that efficient * Most functions do not have these extensions, but enough are that efficient

Просмотреть файл

@ -160,7 +160,7 @@ js::GetScopeChain(JSContext *cx, StackFrame *fp)
JSObject *limitBlock, *limitClone; JSObject *limitBlock, *limitClone;
if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj()) { if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj()) {
JS_ASSERT_IF(fp->scopeChain().isClonedBlock(), fp->scopeChain().getPrivate() != fp); JS_ASSERT_IF(fp->scopeChain().isClonedBlock(), fp->scopeChain().getPrivate() != fp);
if (!CreateFunCallObject(cx, fp)) if (!CallObject::createForFunction(cx, fp))
return NULL; return NULL;
/* We know we must clone everything on blockChain. */ /* We know we must clone everything on blockChain. */
@ -646,7 +646,7 @@ js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const V
/* Give strict mode eval its own fresh lexical environment. */ /* Give strict mode eval its own fresh lexical environment. */
StackFrame *fp = efg.fp(); StackFrame *fp = efg.fp();
if (fp->isStrictEvalFrame() && !CreateEvalCallObject(cx, fp)) if (fp->isStrictEvalFrame() && !CallObject::createForStrictEval(cx, fp))
return false; return false;
Probes::startExecution(cx, script); Probes::startExecution(cx, script);

Просмотреть файл

@ -98,9 +98,9 @@ Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const
if (indexp) if (indexp)
*indexp = shape->shortid(); *indexp = shape->shortid();
if (shape->getter() == GetCallArg) if (shape->getter() == CallObject::getArgOp)
return ARGUMENT; return ARGUMENT;
if (shape->getter() == GetCallUpvar) if (shape->getter() == CallObject::getUpvarOp)
return UPVAR; return UPVAR;
return shape->writable() ? VARIABLE : CONSTANT; return shape->writable() ? VARIABLE : CONSTANT;
@ -128,13 +128,13 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
JS_ASSERT(nvars == 0); JS_ASSERT(nvars == 0);
JS_ASSERT(nupvars == 0); JS_ASSERT(nupvars == 0);
indexp = &nargs; indexp = &nargs;
getter = GetCallArg; getter = CallObject::getArgOp;
setter = SetCallArg; setter = CallObject::setArgOp;
slot += nargs; slot += nargs;
} else if (kind == UPVAR) { } else if (kind == UPVAR) {
indexp = &nupvars; indexp = &nupvars;
getter = GetCallUpvar; getter = CallObject::getUpvarOp;
setter = SetCallUpvar; setter = CallObject::setUpvarOp;
slot = lastBinding->maybeSlot(); slot = lastBinding->maybeSlot();
attrs |= JSPROP_SHARED; attrs |= JSPROP_SHARED;
} else { } else {
@ -142,8 +142,8 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
JS_ASSERT(nupvars == 0); JS_ASSERT(nupvars == 0);
indexp = &nvars; indexp = &nvars;
getter = GetCallVar; getter = CallObject::getVarOp;
setter = SetCallVar; setter = CallObject::setVarOp;
if (kind == CONSTANT) if (kind == CONSTANT)
attrs |= JSPROP_READONLY; attrs |= JSPROP_READONLY;
slot += nargs + nvars; slot += nargs + nvars;
@ -248,9 +248,9 @@ Bindings::getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp)
const Shape &shape = r.front(); const Shape &shape = r.front();
uintN index = uint16_t(shape.shortid()); uintN index = uint16_t(shape.shortid());
if (shape.getter() == GetCallArg) { if (shape.getter() == CallObject::getArgOp) {
JS_ASSERT(index < nargs); JS_ASSERT(index < nargs);
} else if (shape.getter() == GetCallUpvar) { } else if (shape.getter() == CallObject::getUpvarOp) {
JS_ASSERT(index < nupvars); JS_ASSERT(index < nupvars);
index += nargs + nvars; index += nargs + nvars;
} else { } else {
@ -262,7 +262,7 @@ Bindings::getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp)
names[index] = JSID_TO_ATOM(shape.propid()); names[index] = JSID_TO_ATOM(shape.propid());
} else { } else {
JS_ASSERT(JSID_IS_INT(shape.propid())); JS_ASSERT(JSID_IS_INT(shape.propid()));
JS_ASSERT(shape.getter() == GetCallArg); JS_ASSERT(shape.getter() == CallObject::getArgOp);
names[index] = NULL; names[index] = NULL;
} }
} }
@ -282,7 +282,7 @@ Bindings::lastArgument() const
const js::Shape *shape = lastVariable(); const js::Shape *shape = lastVariable();
if (nvars > 0) { if (nvars > 0) {
while (shape->previous() && shape->getter() != GetCallArg) while (shape->previous() && shape->getter() != CallObject::getArgOp)
shape = shape->previous(); shape = shape->previous();
} }
return shape; return shape;
@ -295,7 +295,7 @@ Bindings::lastVariable() const
const js::Shape *shape = lastUpvar(); const js::Shape *shape = lastUpvar();
if (nupvars > 0) { if (nupvars > 0) {
while (shape->getter() == GetCallUpvar) while (shape->getter() == CallObject::getUpvarOp)
shape = shape->previous(); shape = shape->previous();
} }
return shape; return shape;

Просмотреть файл

@ -408,7 +408,7 @@ class SetPropCompiler : public PICStubCompiler
Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg); Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg);
{ {
Address addr(pic.shapeReg, shape->setterOp() == SetCallArg Address addr(pic.shapeReg, shape->setterOp() == CallObject::setArgOp
? StackFrame::offsetOfFormalArg(fun, slot) ? StackFrame::offsetOfFormalArg(fun, slot)
: StackFrame::offsetOfFixed(slot)); : StackFrame::offsetOfFixed(slot));
masm.storeValue(pic.u.vr, addr); masm.storeValue(pic.u.vr, addr);
@ -417,7 +417,7 @@ class SetPropCompiler : public PICStubCompiler
escapedFrame.linkTo(masm.label(), &masm); escapedFrame.linkTo(masm.label(), &masm);
{ {
if (shape->setterOp() == SetCallVar) if (shape->setterOp() == CallObject::setVarOp)
slot += fun->nargs; slot += fun->nargs;
slot += CallObject::RESERVED_SLOTS; slot += CallObject::RESERVED_SLOTS;
@ -655,8 +655,8 @@ class SetPropCompiler : public PICStubCompiler
} else { } else {
if (shape->hasSetterValue()) if (shape->hasSetterValue())
return disable("scripted setter"); return disable("scripted setter");
if (shape->setterOp() != SetCallArg && if (shape->setterOp() != CallObject::setArgOp &&
shape->setterOp() != SetCallVar) { shape->setterOp() != CallObject::setVarOp) {
return disable("setter"); return disable("setter");
} }
JS_ASSERT(obj->isCall()); JS_ASSERT(obj->isCall());
@ -679,7 +679,7 @@ class SetPropCompiler : public PICStubCompiler
return error(); return error();
{ {
types::AutoEnterTypeInference enter(cx); types::AutoEnterTypeInference enter(cx);
if (shape->setterOp() == SetCallArg) if (shape->setterOp() == CallObject::setArgOp)
pic.rhsTypes->addSubset(cx, types::TypeScript::ArgTypes(script, slot)); pic.rhsTypes->addSubset(cx, types::TypeScript::ArgTypes(script, slot));
else else
pic.rhsTypes->addSubset(cx, types::TypeScript::LocalTypes(script, slot)); pic.rhsTypes->addSubset(cx, types::TypeScript::LocalTypes(script, slot));
@ -1548,9 +1548,9 @@ class ScopeNameCompiler : public PICStubCompiler
CallObjPropKind kind; CallObjPropKind kind;
const Shape *shape = getprop.shape; const Shape *shape = getprop.shape;
if (shape->getterOp() == GetCallArg) { if (shape->getterOp() == CallObject::getArgOp) {
kind = ARG; kind = ARG;
} else if (shape->getterOp() == GetCallVar) { } else if (shape->getterOp() == CallObject::getVarOp) {
kind = VAR; kind = VAR;
} else { } else {
return disable("unhandled callobj sprop getter"); return disable("unhandled callobj sprop getter");

Просмотреть файл

@ -2494,7 +2494,7 @@ static Env *
Frame_GetEnv(JSContext *cx, StackFrame *fp) Frame_GetEnv(JSContext *cx, StackFrame *fp)
{ {
assertSameCompartment(cx, fp); assertSameCompartment(cx, fp);
if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj() && !CreateFunCallObject(cx, fp)) if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj() && !CallObject::createForFunction(cx, fp))
return NULL; return NULL;
return GetScopeChain(cx, fp); return GetScopeChain(cx, fp);
} }

Просмотреть файл

@ -23,6 +23,7 @@
* *
* Contributor(s): * Contributor(s):
* Paul Biggar <pbiggar@mozilla.com> (original author) * Paul Biggar <pbiggar@mozilla.com> (original author)
* Luke Wagner <luke@mozilla.com>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"), * either of the GNU General Public License Version 2 or later (the "GPL"),
@ -56,6 +57,100 @@
using namespace js; using namespace js;
using namespace js::types; using namespace js::types;
void
js_PutCallObject(StackFrame *fp)
{
CallObject &callobj = fp->callObj().asCall();
JS_ASSERT(callobj.maybeStackFrame() == fp);
JS_ASSERT_IF(fp->isEvalFrame(), fp->isStrictEvalFrame());
JS_ASSERT(fp->isEvalFrame() == callobj.isForEval());
/* Get the arguments object to snapshot fp's actual argument values. */
if (fp->hasArgsObj()) {
if (callobj.arguments().isMagic(JS_UNASSIGNED_ARGUMENTS))
callobj.setArguments(ObjectValue(fp->argsObj()));
js_PutArgsObject(fp);
}
JSScript *script = fp->script();
Bindings &bindings = script->bindings;
if (callobj.isForEval()) {
JS_ASSERT(script->strictModeCode);
JS_ASSERT(bindings.countArgs() == 0);
/* This could be optimized as below, but keep it simple for now. */
callobj.copyValues(0, NULL, bindings.countVars(), fp->slots());
} else {
JSFunction *fun = fp->fun();
JS_ASSERT(script == callobj.getCalleeFunction()->script());
JS_ASSERT(script == fun->script());
uintN n = bindings.countArgsAndVars();
if (n > 0) {
uint32_t nvars = bindings.countVars();
uint32_t nargs = bindings.countArgs();
JS_ASSERT(fun->nargs == nargs);
JS_ASSERT(nvars + nargs == n);
JSScript *script = fun->script();
if (script->usesEval
#ifdef JS_METHODJIT
|| script->debugMode
#endif
) {
callobj.copyValues(nargs, fp->formalArgs(), nvars, fp->slots());
} else {
/*
* For each arg & var that is closed over, copy it from the stack
* into the call object. We use initArg/VarUnchecked because,
* when you call a getter on a call object, js_NativeGetInline
* caches the return value in the slot, so we can't assert that
* it's undefined.
*/
uint32_t nclosed = script->nClosedArgs;
for (uint32_t i = 0; i < nclosed; i++) {
uint32_t e = script->getClosedArg(i);
#ifdef JS_GC_ZEAL
callobj.setArg(e, fp->formalArg(e));
#else
callobj.initArgUnchecked(e, fp->formalArg(e));
#endif
}
nclosed = script->nClosedVars;
for (uint32_t i = 0; i < nclosed; i++) {
uint32_t e = script->getClosedVar(i);
#ifdef JS_GC_ZEAL
callobj.setVar(e, fp->slots()[e]);
#else
callobj.initVarUnchecked(e, fp->slots()[e]);
#endif
}
}
/*
* Update the args and vars for the active call if this is an outer
* function in a script nesting.
*/
types::TypeScriptNesting *nesting = script->nesting();
if (nesting && script->isOuterFunction) {
nesting->argArray = callobj.argArray();
nesting->varArray = callobj.varArray();
}
}
/* Clear private pointers to fp, which is about to go away. */
if (js_IsNamedLambda(fun)) {
JSObject &env = callobj.enclosingScope();
JS_ASSERT(env.asDeclEnv().maybeStackFrame() == fp);
env.setPrivate(NULL);
}
}
callobj.setStackFrame(NULL);
}
/* /*
* Construct a call object for the given bindings. If this is a call object * Construct a call object for the given bindings. If this is a call object
* for a function invocation, callee should be the function being called. * for a function invocation, callee should be the function being called.
@ -126,6 +221,248 @@ CallObject::create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObjec
return &obj->asCall(); return &obj->asCall();
} }
CallObject *
CallObject::createForFunction(JSContext *cx, StackFrame *fp)
{
JS_ASSERT(fp->isNonEvalFunctionFrame());
JS_ASSERT(!fp->hasCallObj());
JSObject *scopeChain = &fp->scopeChain();
JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(),
scopeChain->getPrivate() != fp);
/*
* For a named function expression Call's parent points to an environment
* object holding function's name.
*/
if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) {
scopeChain = DeclEnvObject::create(cx, fp);
if (!scopeChain)
return NULL;
if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName),
ObjectValue(fp->callee()), NULL, NULL,
JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
return NULL;
}
}
CallObject *callobj = create(cx, fp->script(), *scopeChain, &fp->callee());
if (!callobj)
return NULL;
callobj->setStackFrame(fp);
fp->setScopeChainWithOwnCallObj(*callobj);
return callobj;
}
CallObject *
CallObject::createForStrictEval(JSContext *cx, StackFrame *fp)
{
CallObject *callobj = create(cx, fp->script(), fp->scopeChain(), NULL);
if (!callobj)
return NULL;
callobj->setStackFrame(fp);
fp->setScopeChainWithOwnCallObj(*callobj);
return callobj;
}
JSBool
CallObject::getArgumentsOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
CallObject &callobj = obj->asCall();
StackFrame *fp = callobj.maybeStackFrame();
if (fp && callobj.arguments().isMagic(JS_UNASSIGNED_ARGUMENTS)) {
JSObject *argsobj = js_GetArgsObject(cx, fp);
if (!argsobj)
return false;
vp->setObject(*argsobj);
} else {
/* Nested functions cannot get the 'arguments' of enclosing scopes. */
JS_ASSERT(!callobj.arguments().isMagic(JS_UNASSIGNED_ARGUMENTS));
*vp = callobj.arguments();
}
return true;
}
JSBool
CallObject::setArgumentsOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
/* Nested functions cannot set the 'arguments' of enclosing scopes. */
JS_ASSERT(obj->asCall().maybeStackFrame());
obj->asCall().setArguments(*vp);
return true;
}
JSBool
CallObject::getArgOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
if (StackFrame *fp = callobj.maybeStackFrame())
*vp = fp->formalArg(i);
else
*vp = callobj.arg(i);
return true;
}
JSBool
CallObject::setArgOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
if (StackFrame *fp = callobj.maybeStackFrame())
fp->formalArg(i) = *vp;
else
callobj.setArg(i, *vp);
JSFunction *fun = callobj.getCalleeFunction();
JSScript *script = fun->script();
if (!script->ensureHasTypes(cx))
return false;
TypeScript::SetArgument(cx, script, i, *vp);
return true;
}
JSBool
CallObject::getUpvarOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
*vp = callobj.getCallee()->toFunction()->getFlatClosureUpvar(i);
return true;
}
JSBool
CallObject::setUpvarOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
callobj.getCallee()->toFunction()->setFlatClosureUpvar(i, *vp);
return true;
}
JSBool
CallObject::getVarOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
if (StackFrame *fp = callobj.maybeStackFrame())
*vp = fp->varSlot(i);
else
*vp = callobj.var(i);
return true;
}
JSBool
CallObject::setVarOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16_t) JSID_TO_INT(id);
if (StackFrame *fp = callobj.maybeStackFrame())
fp->varSlot(i) = *vp;
else
callobj.setVar(i, *vp);
JSFunction *fun = callobj.getCalleeFunction();
JSScript *script = fun->script();
if (!script->ensureHasTypes(cx))
return false;
TypeScript::SetLocal(cx, script, i, *vp);
return true;
}
static JSBool
call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
{
JS_ASSERT(!obj->getProto());
if (!JSID_IS_ATOM(id))
return true;
JSObject *callee = obj->asCall().getCallee();
#ifdef DEBUG
if (callee) {
JSScript *script = callee->toFunction()->script();
JS_ASSERT(!script->bindings.hasBinding(cx, JSID_TO_ATOM(id)));
}
#endif
/*
* Resolve arguments so that we never store a particular Call object's
* arguments object reference in a Call prototype's |arguments| slot.
*
* Include JSPROP_ENUMERATE for consistency with all other Call object
* properties; see js::Bindings::add and js::Interpret's JSOP_DEFFUN
* rebinding-Call-property logic.
*/
if (callee && id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
if (!DefineNativeProperty(cx, obj, id, UndefinedValue(),
CallObject::getArgumentsOp, CallObject::setArgumentsOp,
JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE,
0, 0, DNP_DONT_PURGE)) {
return false;
}
*objp = obj;
return true;
}
/* Control flow reaches here only if id was not resolved. */
return true;
}
static void
call_trace(JSTracer *trc, JSObject *obj)
{
JS_ASSERT(obj->isCall());
/* Mark any generator frame, as for arguments objects. */
#if JS_HAS_GENERATORS
StackFrame *fp = (StackFrame *) obj->getPrivate();
if (fp && fp->isFloatingGenerator())
MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object");
#endif
}
JS_PUBLIC_DATA(Class) js::CallClass = {
"Call",
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS) |
JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
(JSResolveOp)call_resolve,
NULL, /* convert: Leave it NULL so we notice if calls ever escape */
NULL, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
call_trace
};
Class js::DeclEnvClass = { Class js::DeclEnvClass = {
js_Object_str, js_Object_str,
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_PRIVATE |

Просмотреть файл

@ -103,7 +103,7 @@ class ScopeObject : public JSObject
* The stack frame for this scope object, if the frame is still active. * The stack frame for this scope object, if the frame is still active.
* Note: these members may not be called for a StaticBlockObject. * Note: these members may not be called for a StaticBlockObject.
*/ */
inline js::StackFrame *maybeStackFrame() const; inline StackFrame *maybeStackFrame() const;
inline void setStackFrame(StackFrame *frame); inline void setStackFrame(StackFrame *frame);
/* For jit access. */ /* For jit access. */
@ -115,11 +115,14 @@ class CallObject : public ScopeObject
static const uint32_t CALLEE_SLOT = 1; static const uint32_t CALLEE_SLOT = 1;
static const uint32_t ARGUMENTS_SLOT = 2; static const uint32_t ARGUMENTS_SLOT = 2;
static CallObject *
create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee);
public: public:
static const uint32_t RESERVED_SLOTS = 3; static const uint32_t RESERVED_SLOTS = 3;
static CallObject * static CallObject *createForFunction(JSContext *cx, StackFrame *fp);
create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee); static CallObject *createForStrictEval(JSContext *cx, StackFrame *fp);
/* True if this is for a strict mode eval frame or for a function call. */ /* True if this is for a strict mode eval frame or for a function call. */
inline bool isForEval() const; inline bool isForEval() const;
@ -138,28 +141,37 @@ class CallObject : public ScopeObject
* 1. js_PutCallObject is called in a frame which hasArgsObj * 1. js_PutCallObject is called in a frame which hasArgsObj
* 2. the script assigns to 'arguments' * 2. the script assigns to 'arguments'
*/ */
inline const js::Value &arguments() const; inline const Value &arguments() const;
inline void setArguments(const js::Value &v); inline void setArguments(const Value &v);
/* Returns the formal argument at the given index. */ /* Returns the formal argument at the given index. */
inline const js::Value &arg(uintN i) const; inline const Value &arg(uintN i) const;
inline void setArg(uintN i, const js::Value &v); inline void setArg(uintN i, const Value &v);
inline void initArgUnchecked(uintN i, const js::Value &v); inline void initArgUnchecked(uintN i, const Value &v);
/* Returns the variable at the given index. */ /* Returns the variable at the given index. */
inline const js::Value &var(uintN i) const; inline const Value &var(uintN i) const;
inline void setVar(uintN i, const js::Value &v); inline void setVar(uintN i, const Value &v);
inline void initVarUnchecked(uintN i, const js::Value &v); inline void initVarUnchecked(uintN i, const Value &v);
/* /*
* Get the actual arrays of arguments and variables. Only call if type * Get the actual arrays of arguments and variables. Only call if type
* inference is enabled, where we ensure that call object variables are in * inference is enabled, where we ensure that call object variables are in
* contiguous slots (see NewCallObject). * contiguous slots (see NewCallObject).
*/ */
inline js::HeapSlotArray argArray(); inline HeapSlotArray argArray();
inline js::HeapSlotArray varArray(); inline HeapSlotArray varArray();
inline void copyValues(uintN nargs, Value *argv, uintN nvars, Value *slots); inline void copyValues(uintN nargs, Value *argv, uintN nvars, Value *slots);
static JSBool getArgumentsOp(JSContext *cx, JSObject *obj, jsid id, Value *vp);
static JSBool setArgumentsOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
static JSBool getArgOp(JSContext *cx, JSObject *obj, jsid id, Value *vp);
static JSBool getVarOp(JSContext *cx, JSObject *obj, jsid id, Value *vp);
static JSBool getUpvarOp(JSContext *cx, JSObject *obj, jsid id, Value *vp);
static JSBool setArgOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
static JSBool setVarOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
static JSBool setUpvarOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
}; };
class DeclEnvObject : public ScopeObject class DeclEnvObject : public ScopeObject
@ -220,7 +232,7 @@ class BlockObject : public NestedScopeObject
class StaticBlockObject : public BlockObject class StaticBlockObject : public BlockObject
{ {
/* These ScopeObject operations are not valid on a static block object. */ /* These ScopeObject operations are not valid on a static block object. */
js::StackFrame *maybeStackFrame() const; StackFrame *maybeStackFrame() const;
void setStackFrame(StackFrame *frame); void setStackFrame(StackFrame *frame);
public: public:
@ -239,7 +251,7 @@ class StaticBlockObject : public BlockObject
Definition *maybeDefinitionParseNode(unsigned i); Definition *maybeDefinitionParseNode(unsigned i);
void poisonDefinitionParseNode(unsigned i); void poisonDefinitionParseNode(unsigned i);
const js::Shape *addVar(JSContext *cx, jsid id, intN index, bool *redeclared); const Shape *addVar(JSContext *cx, jsid id, intN index, bool *redeclared);
}; };
class ClonedBlockObject : public BlockObject class ClonedBlockObject : public BlockObject

Просмотреть файл

@ -370,7 +370,7 @@ StackFrame::functionPrologue(JSContext *cx)
JSFunction *fun = this->fun(); JSFunction *fun = this->fun();
if (fun->isHeavyweight()) { if (fun->isHeavyweight()) {
if (!CreateFunCallObject(cx, this)) if (!CallObject::createForFunction(cx, this))
return false; return false;
} else { } else {
/* Force instantiation of the scope chain, for JIT frames. */ /* Force instantiation of the scope chain, for JIT frames. */