From bc5c79813f7b7472cb31036ed944c70ba555dfde Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Thu, 23 Feb 2012 13:58:39 -0800 Subject: [PATCH] Bug 729382 - move the rest of the CallObject into ScopeObject (r=waldo) --- js/src/jsdbgapi.cpp | 6 +- js/src/jsfun.cpp | 344 ------------------------------------ js/src/jsfun.h | 24 --- js/src/jsinterp.cpp | 4 +- js/src/jsscript.cpp | 26 +-- js/src/methodjit/PolyIC.cpp | 14 +- js/src/vm/Debugger.cpp | 2 +- js/src/vm/ScopeObject.cpp | 337 +++++++++++++++++++++++++++++++++++ js/src/vm/ScopeObject.h | 42 +++-- js/src/vm/Stack-inl.h | 2 +- 10 files changed, 391 insertions(+), 410 deletions(-) diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 142dd00ff7be..bd131b69164d 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -589,7 +589,7 @@ JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fpArg) * null returned above or in the #else */ if (!fp->hasCallObj() && fp->isNonEvalFunctionFrame()) - return CreateFunCallObject(cx, fp); + return CallObject::createForFunction(cx, fp); return &fp->callObj(); } @@ -844,10 +844,10 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, | (!shape->writable() ? JSPD_READONLY : 0) | (!shape->configurable() ? JSPD_PERMANENT : 0); pd->spare = 0; - if (shape->getter() == GetCallArg) { + if (shape->getter() == CallObject::getArgOp) { pd->slot = shape->shortid(); pd->flags |= JSPD_ARGUMENT; - } else if (shape->getter() == GetCallVar) { + } else if (shape->getter() == CallObject::getVarOp) { pd->slot = shape->shortid(); pd->flags |= JSPD_VARIABLE; } else { diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 2a35f3af461c..6aee93210da8 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -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 StackFrame::getValidCalleeObject(JSContext *cx, Value *vp) { diff --git a/js/src/jsfun.h b/js/src/jsfun.h index d79fcf2028d0..4b00ba8659b3 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -362,30 +362,6 @@ js_PutCallObject(js::StackFrame *fp); 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. * Most functions do not have these extensions, but enough are that efficient diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 9b9b34cd3a97..a79fcabaecbf 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -160,7 +160,7 @@ js::GetScopeChain(JSContext *cx, StackFrame *fp) JSObject *limitBlock, *limitClone; if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj()) { JS_ASSERT_IF(fp->scopeChain().isClonedBlock(), fp->scopeChain().getPrivate() != fp); - if (!CreateFunCallObject(cx, fp)) + if (!CallObject::createForFunction(cx, fp)) return NULL; /* 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. */ StackFrame *fp = efg.fp(); - if (fp->isStrictEvalFrame() && !CreateEvalCallObject(cx, fp)) + if (fp->isStrictEvalFrame() && !CallObject::createForStrictEval(cx, fp)) return false; Probes::startExecution(cx, script); diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index d4d890233dbb..cc21ef86637b 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -98,9 +98,9 @@ Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const if (indexp) *indexp = shape->shortid(); - if (shape->getter() == GetCallArg) + if (shape->getter() == CallObject::getArgOp) return ARGUMENT; - if (shape->getter() == GetCallUpvar) + if (shape->getter() == CallObject::getUpvarOp) return UPVAR; return shape->writable() ? VARIABLE : CONSTANT; @@ -128,13 +128,13 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind) JS_ASSERT(nvars == 0); JS_ASSERT(nupvars == 0); indexp = &nargs; - getter = GetCallArg; - setter = SetCallArg; + getter = CallObject::getArgOp; + setter = CallObject::setArgOp; slot += nargs; } else if (kind == UPVAR) { indexp = &nupvars; - getter = GetCallUpvar; - setter = SetCallUpvar; + getter = CallObject::getUpvarOp; + setter = CallObject::setUpvarOp; slot = lastBinding->maybeSlot(); attrs |= JSPROP_SHARED; } else { @@ -142,8 +142,8 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind) JS_ASSERT(nupvars == 0); indexp = &nvars; - getter = GetCallVar; - setter = SetCallVar; + getter = CallObject::getVarOp; + setter = CallObject::setVarOp; if (kind == CONSTANT) attrs |= JSPROP_READONLY; slot += nargs + nvars; @@ -248,9 +248,9 @@ Bindings::getLocalNameArray(JSContext *cx, Vector *namesp) const Shape &shape = r.front(); uintN index = uint16_t(shape.shortid()); - if (shape.getter() == GetCallArg) { + if (shape.getter() == CallObject::getArgOp) { JS_ASSERT(index < nargs); - } else if (shape.getter() == GetCallUpvar) { + } else if (shape.getter() == CallObject::getUpvarOp) { JS_ASSERT(index < nupvars); index += nargs + nvars; } else { @@ -262,7 +262,7 @@ Bindings::getLocalNameArray(JSContext *cx, Vector *namesp) names[index] = JSID_TO_ATOM(shape.propid()); } else { JS_ASSERT(JSID_IS_INT(shape.propid())); - JS_ASSERT(shape.getter() == GetCallArg); + JS_ASSERT(shape.getter() == CallObject::getArgOp); names[index] = NULL; } } @@ -282,7 +282,7 @@ Bindings::lastArgument() const const js::Shape *shape = lastVariable(); if (nvars > 0) { - while (shape->previous() && shape->getter() != GetCallArg) + while (shape->previous() && shape->getter() != CallObject::getArgOp) shape = shape->previous(); } return shape; @@ -295,7 +295,7 @@ Bindings::lastVariable() const const js::Shape *shape = lastUpvar(); if (nupvars > 0) { - while (shape->getter() == GetCallUpvar) + while (shape->getter() == CallObject::getUpvarOp) shape = shape->previous(); } return shape; diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 2d68ea294c62..34dd2dbe7ddc 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -408,7 +408,7 @@ class SetPropCompiler : public PICStubCompiler 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::offsetOfFixed(slot)); masm.storeValue(pic.u.vr, addr); @@ -417,7 +417,7 @@ class SetPropCompiler : public PICStubCompiler escapedFrame.linkTo(masm.label(), &masm); { - if (shape->setterOp() == SetCallVar) + if (shape->setterOp() == CallObject::setVarOp) slot += fun->nargs; slot += CallObject::RESERVED_SLOTS; @@ -655,8 +655,8 @@ class SetPropCompiler : public PICStubCompiler } else { if (shape->hasSetterValue()) return disable("scripted setter"); - if (shape->setterOp() != SetCallArg && - shape->setterOp() != SetCallVar) { + if (shape->setterOp() != CallObject::setArgOp && + shape->setterOp() != CallObject::setVarOp) { return disable("setter"); } JS_ASSERT(obj->isCall()); @@ -679,7 +679,7 @@ class SetPropCompiler : public PICStubCompiler return error(); { types::AutoEnterTypeInference enter(cx); - if (shape->setterOp() == SetCallArg) + if (shape->setterOp() == CallObject::setArgOp) pic.rhsTypes->addSubset(cx, types::TypeScript::ArgTypes(script, slot)); else pic.rhsTypes->addSubset(cx, types::TypeScript::LocalTypes(script, slot)); @@ -1548,9 +1548,9 @@ class ScopeNameCompiler : public PICStubCompiler CallObjPropKind kind; const Shape *shape = getprop.shape; - if (shape->getterOp() == GetCallArg) { + if (shape->getterOp() == CallObject::getArgOp) { kind = ARG; - } else if (shape->getterOp() == GetCallVar) { + } else if (shape->getterOp() == CallObject::getVarOp) { kind = VAR; } else { return disable("unhandled callobj sprop getter"); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index a22ad02424bf..934c05995bc2 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -2494,7 +2494,7 @@ static Env * Frame_GetEnv(JSContext *cx, StackFrame *fp) { assertSameCompartment(cx, fp); - if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj() && !CreateFunCallObject(cx, fp)) + if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj() && !CallObject::createForFunction(cx, fp)) return NULL; return GetScopeChain(cx, fp); } diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index fa9a03effb03..f9886d793c63 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -23,6 +23,7 @@ * * Contributor(s): * Paul Biggar (original author) + * Luke Wagner * * 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"), @@ -56,6 +57,100 @@ using namespace js; 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 * 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(); } +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 = { js_Object_str, JSCLASS_HAS_PRIVATE | diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index fe9495c87501..c613ec2599ee 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -103,7 +103,7 @@ class ScopeObject : public JSObject * The stack frame for this scope object, if the frame is still active. * Note: these members may not be called for a StaticBlockObject. */ - inline js::StackFrame *maybeStackFrame() const; + inline StackFrame *maybeStackFrame() const; inline void setStackFrame(StackFrame *frame); /* For jit access. */ @@ -115,11 +115,14 @@ class CallObject : public ScopeObject static const uint32_t CALLEE_SLOT = 1; static const uint32_t ARGUMENTS_SLOT = 2; + static CallObject * + create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee); + public: static const uint32_t RESERVED_SLOTS = 3; - static CallObject * - create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee); + static CallObject *createForFunction(JSContext *cx, StackFrame *fp); + static CallObject *createForStrictEval(JSContext *cx, StackFrame *fp); /* True if this is for a strict mode eval frame or for a function call. */ inline bool isForEval() const; @@ -138,28 +141,37 @@ class CallObject : public ScopeObject * 1. js_PutCallObject is called in a frame which hasArgsObj * 2. the script assigns to 'arguments' */ - inline const js::Value &arguments() const; - inline void setArguments(const js::Value &v); + inline const Value &arguments() const; + inline void setArguments(const Value &v); /* Returns the formal argument at the given index. */ - inline const js::Value &arg(uintN i) const; - inline void setArg(uintN i, const js::Value &v); - inline void initArgUnchecked(uintN i, const js::Value &v); + inline const Value &arg(uintN i) const; + inline void setArg(uintN i, const Value &v); + inline void initArgUnchecked(uintN i, const Value &v); /* Returns the variable at the given index. */ - inline const js::Value &var(uintN i) const; - inline void setVar(uintN i, const js::Value &v); - inline void initVarUnchecked(uintN i, const js::Value &v); + inline const Value &var(uintN i) const; + inline void setVar(uintN i, const Value &v); + inline void initVarUnchecked(uintN i, const Value &v); /* * Get the actual arrays of arguments and variables. Only call if type * inference is enabled, where we ensure that call object variables are in * contiguous slots (see NewCallObject). */ - inline js::HeapSlotArray argArray(); - inline js::HeapSlotArray varArray(); + inline HeapSlotArray argArray(); + inline HeapSlotArray varArray(); 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 @@ -220,7 +232,7 @@ class BlockObject : public NestedScopeObject class StaticBlockObject : public BlockObject { /* These ScopeObject operations are not valid on a static block object. */ - js::StackFrame *maybeStackFrame() const; + StackFrame *maybeStackFrame() const; void setStackFrame(StackFrame *frame); public: @@ -239,7 +251,7 @@ class StaticBlockObject : public BlockObject Definition *maybeDefinitionParseNode(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 diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 973a99696403..7310dfc95dcd 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -370,7 +370,7 @@ StackFrame::functionPrologue(JSContext *cx) JSFunction *fun = this->fun(); if (fun->isHeavyweight()) { - if (!CreateFunCallObject(cx, this)) + if (!CallObject::createForFunction(cx, this)) return false; } else { /* Force instantiation of the scope chain, for JIT frames. */