author | Luke Wagner <luke@mozilla.com> |
Thu, 23 Feb 2012 13:58:39 -0800 | |
changeset 87895 | d485f2378abe54f7e1acdff0fbb79c9888978c77 |
parent 87894 | bd71047c9b4d53b5e8ead4cfe63b6e5267b02fe7 |
child 87896 | 43766cfb6d30600beaf695a9f652a8befd4631fe |
push id | 6561 |
push user | lwagner@mozilla.com |
push date | Tue, 28 Feb 2012 08:34:43 +0000 |
treeherder | mozilla-inbound@d485f2378abe [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | waldo |
bugs | 729382 |
milestone | 13.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/jsdbgapi.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsfun.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsfun.h | file | annotate | diff | comparison | revisions | |
js/src/jsinterp.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsscript.cpp | file | annotate | diff | comparison | revisions | |
js/src/methodjit/PolyIC.cpp | file | annotate | diff | comparison | revisions | |
js/src/vm/Debugger.cpp | file | annotate | diff | comparison | revisions | |
js/src/vm/ScopeObject.cpp | file | annotate | diff | comparison | revisions | |
js/src/vm/ScopeObject.h | file | annotate | diff | comparison | revisions | |
js/src/vm/Stack-inl.h | file | annotate | diff | comparison | revisions |
--- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -584,17 +584,17 @@ JS_GetFrameCallObject(JSContext *cx, JSS if (!ac.enter()) return NULL; /* * XXX ill-defined: null return here means error was reported, unlike a * null returned above or in the #else */ if (!fp->hasCallObj() && fp->isNonEvalFunctionFrame()) - return CreateFunCallObject(cx, fp); + return CallObject::createForFunction(cx, fp); return &fp->callObj(); } JS_PUBLIC_API(JSBool) JS_GetFrameThis(JSContext *cx, JSStackFrame *fpArg, jsval *thisv) { StackFrame *fp = Valueify(fpArg); if (fp->isDummyFrame()) @@ -839,20 +839,20 @@ JS_GetPropertyDesc(JSContext *cx, JSObje if (wasThrowing) cx->setPendingException(lastException); pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0) | (!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 { pd->slot = 0; } pd->alias = JSVAL_VOID; return JS_TRUE;
--- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -594,360 +594,16 @@ Class js::StrictArgumentsObjectClass = { NULL, /* outerObject */ NULL, /* innerObject */ JS_ElementIteratorStub, NULL, /* unused */ false, /* isWrappedNative */ } }; -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) { if (!isFunctionFrame()) { vp->setNull(); return true; }
--- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -357,40 +357,16 @@ js_ValueToCallableObject(JSContext *cx, extern void js_ReportIsNotFunction(JSContext *cx, const js::Value *vp, uintN flags); extern void 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 * storage is required (no malloc'ed reserved slots). */ class FunctionExtended : public JSFunction { friend struct JSFunction;
--- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -155,17 +155,17 @@ js::GetScopeChain(JSContext *cx, StackFr * make sure there's a call object at the current head of the scope chain, * if this frame is a call frame. * * Also, identify the innermost compiler-allocated block we needn't clone. */ 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. */ limitBlock = limitClone = NULL; } else { /* * scopeChain includes all blocks whose static scope we're within that * have already been cloned. Find the innermost such block. Its @@ -641,17 +641,17 @@ js::ExecuteKernel(JSContext *cx, JSScrip if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg)) return false; if (!script->ensureRanAnalysis(cx, &scopeChain)) return false; /* 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); TypeScript::SetThis(cx, script, fp->thisValue()); AutoPreserveEnumerators preserve(cx); JSBool ok = RunScript(cx, script, fp);
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -93,19 +93,19 @@ Bindings::lookup(JSContext *cx, JSAtom * Shape **spp; Shape *shape = Shape::search(cx, lastBinding, ATOM_TO_JSID(name), &spp); if (!shape) return NONE; 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; } bool Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind) { @@ -123,32 +123,32 @@ Bindings::add(JSContext *cx, JSAtom *nam PropertyOp getter; StrictPropertyOp setter; uint32_t slot = CallObject::RESERVED_SLOTS; if (kind == ARGUMENT) { 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 { JS_ASSERT(kind == VARIABLE || kind == CONSTANT); 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; } if (*indexp == BINDING_COUNT_LIMIT) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, (kind == ARGUMENT) @@ -243,31 +243,31 @@ Bindings::getLocalNameArray(JSContext *c for (uintN i = 0; i < n; i++) names[i] = POISON; #endif for (Shape::Range r = lastBinding->all(); !r.empty(); r.popFront()) { 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 { JS_ASSERT(index < nvars); index += nargs; } if (JSID_IS_ATOM(shape.propid())) { 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; } } #ifdef DEBUG for (uintN i = 0; i < n; i++) JS_ASSERT(names[i] != POISON); #endif @@ -277,30 +277,30 @@ Bindings::getLocalNameArray(JSContext *c const Shape * Bindings::lastArgument() const { JS_ASSERT(lastBinding); 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; } const Shape * Bindings::lastVariable() const { JS_ASSERT(lastBinding); const js::Shape *shape = lastUpvar(); if (nupvars > 0) { - while (shape->getter() == GetCallUpvar) + while (shape->getter() == CallObject::getUpvarOp) shape = shape->previous(); } return shape; } const Shape * Bindings::lastUpvar() const {
--- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -403,26 +403,26 @@ class SetPropCompiler : public PICStubCo JSFunction *fun = obj->asCall().getCalleeFunction(); uint16_t slot = uint16_t(shape->shortid()); /* Guard that the call object has a frame. */ masm.loadObjPrivate(pic.objReg, pic.shapeReg, obj->numFixedSlots()); 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); skipOver = masm.jump(); } escapedFrame.linkTo(masm.label(), &masm); { - if (shape->setterOp() == SetCallVar) + if (shape->setterOp() == CallObject::setVarOp) slot += fun->nargs; slot += CallObject::RESERVED_SLOTS; Address address = masm.objPropAddress(obj, pic.objReg, slot); masm.storeValue(pic.u.vr, address); } @@ -650,18 +650,18 @@ class SetPropCompiler : public PICStubCo if (shape->hasDefaultSetter()) { if (!shape->hasSlot()) return disable("invalid slot"); if (pic.typeMonitored && !updateMonitoredTypes()) return Lookup_Uncacheable; } 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()); if (pic.typeMonitored) { /* * Update the types of the locals/args in the script according * to the possible RHS types of the assignment. Note that the * shape guards we have performed do not by themselves @@ -674,17 +674,17 @@ class SetPropCompiler : public PICStubCo RecompilationMonitor monitor(cx); JSFunction *fun = obj->asCall().getCalleeFunction(); JSScript *script = fun->script(); uint16_t slot = uint16_t(shape->shortid()); if (!script->ensureHasTypes(cx)) 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)); } if (monitor.recompiled()) return Lookup_Uncacheable; } } @@ -1543,19 +1543,19 @@ class ScopeNameCompiler : public PICStub if (pic.kind == ic::PICInfo::NAME) masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg); JS_ASSERT(obj == getprop.holder); JS_ASSERT(getprop.holder != &scopeChain->global()); 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"); } LookupStatus status = walkScopeChain(masm, fails); if (status != Lookup_Cacheable) return status;
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -2489,17 +2489,17 @@ DebuggerFrame_getType(JSContext *cx, uin : cx->runtime->atomState.callAtom); return true; } 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); } static JSBool DebuggerFrame_getEnvironment(JSContext *cx, uintN argc, Value *vp) { THIS_FRAME_OWNER(cx, argc, vp, "get environment", args, thisobj, fp, dbg);
--- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -18,16 +18,17 @@ * * The Initial Developer of the Original Code is * the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * 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 * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -51,16 +52,110 @@ #include "jsatominlines.h" #include "jsobjinlines.h" #include "ScopeObject-inl.h" 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. * Otherwise it must be a call object for eval of strict mode code, and callee * must be null. */ CallObject * CallObject::create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee) @@ -121,16 +216,258 @@ CallObject::create(JSContext *cx, JSScri * its Call should have its own shape; see BaseShape::extensibleParents. */ if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx)) return NULL; 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 | JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */
--- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -98,33 +98,36 @@ class ScopeObject : public JSObject */ inline JSObject &enclosingScope() const; inline bool setEnclosingScope(JSContext *cx, JSObject &obj); /* * 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. */ static inline size_t offsetOfEnclosingScope(); }; 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; /* * The callee function if this CallObject was created for a function * invocation, or null if it was created for a strict mode eval frame. */ @@ -133,38 +136,47 @@ class CallObject : public ScopeObject inline void setCallee(JSObject *callee); /* * When a call object is created, CallObject::arguments has the value * MagicValue(JS_UNASSIGNED_ARGUMENTS). This value is overwritten if: * 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 { public: static const uint32_t RESERVED_SLOTS = 1; static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2; @@ -215,17 +227,17 @@ class BlockObject : public NestedScopeOb protected: /* Blocks contain an object slot for each slot i: 0 <= i < slotCount. */ inline HeapSlot &slotValue(unsigned i); }; 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: static StaticBlockObject *create(JSContext *cx); inline StaticBlockObject *enclosingBlock() const; inline void setEnclosingBlock(StaticBlockObject *blockObj); @@ -234,17 +246,17 @@ class StaticBlockObject : public BlockOb /* * Frontend compilation temporarily uses the object's slots to link * a let var to its associated Definition parse node. */ void setDefinitionParseNode(unsigned i, Definition *def); 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 { public: static ClonedBlockObject *create(JSContext *cx, StaticBlockObject &block, StackFrame *fp); /* The static block from which this block was cloned. */
--- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -365,17 +365,17 @@ StackFrame::maintainNestingState() const inline bool StackFrame::functionPrologue(JSContext *cx) { JS_ASSERT(isNonEvalFunctionFrame()); 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. */ scopeChain(); } if (script()->nesting()) { JS_ASSERT(maintainNestingState());