Bug 733950 - create arguments object eagerly (r=bhackett)

--HG--
rename : js/src/jsfun.cpp => js/src/vm/ArgumentsObject.cpp
This commit is contained in:
Luke Wagner 2012-01-17 16:35:12 -08:00
parent 225350a951
commit fccfecaaff
22 changed files with 880 additions and 891 deletions

View File

@ -150,6 +150,7 @@ CPPSRCS = \
jsxml.cpp \
prmjtime.cpp \
sharkctl.cpp \
ArgumentsObject.cpp \
ScopeObject.cpp \
Debugger.cpp \
GlobalObject.cpp \

View File

@ -2743,20 +2743,6 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
bce->switchToMain();
}
/*
* Strict mode functions' arguments objects copy initial parameter values.
* We create arguments objects lazily -- but that doesn't work for strict
* mode functions where a parameter might be modified and arguments might
* be accessed. For such functions we synthesize an access to arguments to
* initialize it with the original parameter values.
*/
if (bce->needsEagerArguments()) {
bce->switchToProlog();
if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0 || Emit1(cx, bce, JSOP_POP) < 0)
return false;
bce->switchToMain();
}
return EmitTree(cx, bce, body) &&
Emit1(cx, bce, JSOP_STOP) >= 0 &&
JSScript::NewScriptFromEmitter(cx, bce);

View File

@ -162,11 +162,13 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
* other than through ARG* and LOCAL* opcodes (though arguments can still
* be indirectly read but not written through 'arguments' properties).
* All escaping locals are treated as having possible use-before-defs.
* Conservatively use 'mayNeedArgsObj' instead of 'needsArgsObj'
* (needsArgsObj requires SSA which requires escapedSlots).
*/
PodZero(escapedSlots, numSlots);
if (script->usesEval || script->usesArguments || script->compartment()->debugMode()) {
if (script->usesEval || script->mayNeedArgsObj() || script->compartment()->debugMode()) {
for (unsigned i = 0; i < nargs; i++)
escapedSlots[ArgSlot(i)] = true;
} else {
@ -201,7 +203,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
isInlineable = true;
if (script->nClosedArgs || script->nClosedVars || heavyweight ||
script->usesEval || script->usesArguments || cx->compartment->debugMode()) {
script->usesEval || script->mayNeedArgsObj() || cx->compartment->debugMode()) {
isInlineable = false;
}
@ -671,6 +673,19 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
JS_ASSERT(forwardJump == 0 && forwardCatch == 0);
ranBytecode_ = true;
/*
* Always ensure that a script's arguments usage has been analyzed before
* entering the script. This allows the functionPrologue to ensure that
* arguments are always created eagerly which simplifies interp logic.
*/
if (!script->analyzedArgsUsage()) {
if (!script->mayNeedArgsObj())
script->setNeedsArgsObj(false);
else
analyzeSSA(cx);
JS_ASSERT_IF(!failed(), script->analyzedArgsUsage());
}
}
/////////////////////////////////////////////////////////////////////
@ -1593,6 +1608,51 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
}
ranSSA_ = true;
/*
* Now that we have full SSA information for the script, analyze whether
* the arguments object is actually needed. The first pass performed by the
* frontend just looked for the 'arguments' keyword. Here, we can see how
* 'arguments' is used and optimize several cases where we can read values
* from the stack frame directly.
*/
if (script->analyzedArgsUsage())
return;
/* Ensured by analyzeBytecode. */
JS_ASSERT(script->function());
JS_ASSERT(script->mayNeedArgsObj());
JS_ASSERT(!script->usesEval);
/*
* Since let variables are not tracked, we cannot soundly perform this
* analysis in their presence.
*/
if (localsAliasStack()) {
script->setNeedsArgsObj(true);
return;
}
offset = 0;
while (offset < script->length) {
Bytecode *code = maybeCode(offset);
jsbytecode *pc = script->code + offset;
/* Ensured by NewScriptFromEmitter. */
JS_ASSERT_IF(script->strictModeCode, *pc != JSOP_SETARG);
if (code && JSOp(*pc) == JSOP_ARGUMENTS) {
Vector<SSAValue> seen(cx);
if (!followEscapingArguments(cx, SSAValue::PushedValue(offset, 0), &seen)) {
script->setNeedsArgsObj(true);
return;
}
}
offset += GetBytecodeLength(pc);
}
script->setNeedsArgsObj(false);
}
/* Get a phi node's capacity for a given length. */
@ -1876,6 +1936,74 @@ ScriptAnalysis::freezeNewValues(JSContext *cx, uint32_t offset)
cx->delete_(pending);
}
bool
ScriptAnalysis::followEscapingArguments(JSContext *cx, const SSAValue &v, Vector<SSAValue> *seen)
{
/*
* trackUseChain is false for initial values of variables, which
* cannot hold the script's arguments object.
*/
if (!trackUseChain(v))
return true;
for (unsigned i = 0; i < seen->length(); i++) {
if (v == (*seen)[i])
return true;
}
if (!seen->append(v)) {
cx->compartment->types.setPendingNukeTypes(cx);
return false;
}
SSAUseChain *use = useChain(v);
while (use) {
if (!followEscapingArguments(cx, use, seen))
return false;
use = use->next;
}
return true;
}
bool
ScriptAnalysis::followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector<SSAValue> *seen)
{
if (!use->popped)
return followEscapingArguments(cx, SSAValue::PhiValue(use->offset, use->u.phi), seen);
jsbytecode *pc = script->code + use->offset;
uint32_t which = use->u.which;
JSOp op = JSOp(*pc);
if (op == JSOP_POP || op == JSOP_POPN)
return true;
/* arguments[i] can read fp->canonicalActualArg(i) directly. */
if (op == JSOP_GETELEM && which == 1)
return true;
/* arguments.length length can read fp->numActualArgs() directly. */
if (op == JSOP_LENGTH)
return true;
/* Allow assignments to non-closed locals (but not arguments). */
if (op == JSOP_SETLOCAL) {
uint32_t slot = GetBytecodeSlot(script, pc);
if (!trackSlot(slot) || script->strictModeCode)
return false;
if (!followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen))
return false;
return followEscapingArguments(cx, SSAValue::WrittenVar(slot, use->offset), seen);
}
if (op == JSOP_GETLOCAL)
return followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen);
return false;
}
CrossSSAValue
CrossScriptSSA::foldValue(const CrossSSAValue &cv)
{

View File

@ -471,16 +471,6 @@ class Value
return JSVAL_IS_MAGIC_IMPL(data);
}
/*
* Although the Value class comment says 'magic' is a singleton type, it is
* technically possible to use the payload. This should be avoided to
* preserve the ability for the strong assertions in isMagic().
*/
JS_ALWAYS_INLINE
bool isParticularMagic(JSWhyMagic why) const {
return isMagic() && data.s.payload.why == why;
}
JS_ALWAYS_INLINE
bool isMarkable() const {
return JSVAL_IS_TRACEABLE_IMPL(data);

View File

@ -101,504 +101,6 @@ using namespace js;
using namespace js::gc;
using namespace js::types;
js::ArgumentsObject *
ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee)
{
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
JSObject *proto = callee.global().getOrCreateObjectPrototype(cx);
if (!proto)
return NULL;
RootedVarTypeObject type(cx);
type = proto->getNewType(cx);
if (!type)
return NULL;
bool strict = callee.toFunction()->inStrictMode();
Class *clasp = strict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
RootedVarShape emptyArgumentsShape(cx);
emptyArgumentsShape =
EmptyShape::getInitialShape(cx, clasp, proto,
proto->getParent(), FINALIZE_KIND,
BaseShape::INDEXED);
if (!emptyArgumentsShape)
return NULL;
ArgumentsData *data = (ArgumentsData *)
cx->malloc_(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
if (!data)
return NULL;
data->callee.init(ObjectValue(callee));
for (HeapValue *vp = data->slots; vp != data->slots + argc; vp++)
vp->init(UndefinedValue());
/* We have everything needed to fill in the object, so make the object. */
JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyArgumentsShape, type, NULL);
if (!obj)
return NULL;
ArgumentsObject &argsobj = obj->asArguments();
JS_ASSERT(UINT32_MAX > (uint64_t(argc) << PACKED_BITS_COUNT));
argsobj.initInitialLength(argc);
argsobj.initData(data);
argsobj.setStackFrame(NULL);
JS_ASSERT(argsobj.numFixedSlots() >= NormalArgumentsObject::RESERVED_SLOTS);
JS_ASSERT(argsobj.numFixedSlots() >= StrictArgumentsObject::RESERVED_SLOTS);
return &argsobj;
}
struct STATIC_SKIP_INFERENCE PutArg
{
PutArg(JSCompartment *comp, HeapValue *dst) : dst(dst), compartment(comp) {}
HeapValue *dst;
JSCompartment *compartment;
bool operator()(unsigned, Value *src) {
JS_ASSERT(dst->isMagic(JS_ARGS_HOLE) || dst->isUndefined());
if (!dst->isMagic(JS_ARGS_HOLE))
dst->set(compartment, *src);
++dst;
return true;
}
};
ArgumentsObject *
js_GetArgsObject(JSContext *cx, StackFrame *fp)
{
/*
* Arguments and Call objects are owned by the enclosing non-eval function
* frame, thus any eval frames must be skipped before testing hasArgsObj.
*/
JS_ASSERT(fp->isFunctionFrame());
while (fp->isEvalInFunction())
fp = fp->prev();
/*
* Mark all functions which have ever had arguments objects constructed,
* which will prevent lazy arguments optimizations in the method JIT.
*/
if (!fp->script()->createdArgs)
types::MarkArgumentsCreated(cx, fp->script());
/* Create an arguments object for fp only if it lacks one. */
JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj());
if (fp->hasArgsObj())
return &fp->argsObj();
ArgumentsObject *argsobj = ArgumentsObject::create(cx, fp->numActualArgs(), fp->callee());
if (!argsobj)
return argsobj;
/*
* Strict mode functions have arguments objects that copy the initial
* actual parameter values. It is the caller's responsibility to get the
* arguments object before any parameters are modified! (The emitter
* ensures this by synthesizing an arguments access at the start of any
* strict mode function that contains an assignment to a parameter, or
* that calls eval.) Non-strict mode arguments use the frame pointer to
* retrieve up-to-date parameter values.
*/
if (argsobj->isStrictArguments())
fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots));
else
argsobj->setStackFrame(fp);
fp->setArgsObj(*argsobj);
return argsobj;
}
void
js_PutArgsObject(StackFrame *fp)
{
ArgumentsObject &argsobj = fp->argsObj();
if (argsobj.isNormalArguments()) {
JS_ASSERT(argsobj.maybeStackFrame() == fp);
JSCompartment *comp = fp->scopeChain().compartment();
fp->forEachCanonicalActualArg(PutArg(comp, argsobj.data()->slots));
argsobj.setStackFrame(NULL);
} else {
JS_ASSERT(!argsobj.maybeStackFrame());
}
}
static JSBool
args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
ArgumentsObject &argsobj = obj->asArguments();
if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength())
argsobj.setElement(arg, MagicValue(JS_ARGS_HOLE));
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
argsobj.markLengthOverridden();
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
argsobj.asNormalArguments().clearCallee();
}
return true;
}
static JSBool
ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
if (!obj->isNormalArguments())
return true;
NormalArgumentsObject &argsobj = obj->asNormalArguments();
if (JSID_IS_INT(id)) {
/*
* arg can exceed the number of arguments if a script changed the
* prototype to point to another Arguments object with a bigger argc.
*/
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
JS_ASSERT(!argsobj.element(arg).isMagic(JS_ARGS_HOLE));
if (StackFrame *fp = argsobj.maybeStackFrame())
*vp = fp->canonicalActualArg(arg);
else
*vp = argsobj.element(arg);
}
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (!argsobj.hasOverriddenLength())
vp->setInt32(argsobj.initialLength());
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
const Value &v = argsobj.callee();
if (!v.isMagic(JS_ARGS_HOLE))
*vp = v;
}
return true;
}
static JSBool
ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
if (!obj->isNormalArguments())
return true;
NormalArgumentsObject &argsobj = obj->asNormalArguments();
if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
if (StackFrame *fp = argsobj.maybeStackFrame()) {
JSScript *script = fp->functionScript();
if (script->usesArguments) {
if (arg < fp->numFormalArgs())
TypeScript::SetArgument(cx, script, arg, *vp);
fp->canonicalActualArg(arg) = *vp;
}
return true;
}
}
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
}
/*
* For simplicity we use delete/define to replace the property with one
* backed by the default Object getter and setter. Note that we rely on
* args_delProperty to clear the corresponding reserved slot so the GC can
* collect its value. Note also that we must define the property instead
* of setting it in case the user has changed the prototype to an object
* that has a setter for this id.
*/
AutoValueRooter tvr(cx);
return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), false) &&
js_DefineProperty(cx, &argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
}
static JSBool
args_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
JSObject **objp)
{
*objp = NULL;
NormalArgumentsObject &argsobj = obj->asNormalArguments();
unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
if (JSID_IS_INT(id)) {
uint32_t arg = uint32_t(JSID_TO_INT(id));
if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE))
return true;
attrs |= JSPROP_ENUMERATE;
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (argsobj.hasOverriddenLength())
return true;
} else {
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
return true;
if (argsobj.callee().isMagic(JS_ARGS_HOLE))
return true;
}
Value undef = UndefinedValue();
if (!js_DefineProperty(cx, &argsobj, id, &undef, ArgGetter, ArgSetter, attrs))
return JS_FALSE;
*objp = &argsobj;
return true;
}
static JSBool
args_enumerate(JSContext *cx, JSObject *obj)
{
NormalArgumentsObject &argsobj = obj->asNormalArguments();
/*
* Trigger reflection in args_resolve using a series of js_LookupProperty
* calls.
*/
int argc = int(argsobj.initialLength());
for (int i = -2; i != argc; i++) {
jsid id = (i == -2)
? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
: (i == -1)
? ATOM_TO_JSID(cx->runtime->atomState.calleeAtom)
: INT_TO_JSID(i);
JSObject *pobj;
JSProperty *prop;
if (!js_LookupProperty(cx, &argsobj, id, &pobj, &prop))
return false;
}
return true;
}
static JSBool
StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
if (!obj->isStrictArguments())
return true;
StrictArgumentsObject &argsobj = obj->asStrictArguments();
if (JSID_IS_INT(id)) {
/*
* arg can exceed the number of arguments if a script changed the
* prototype to point to another Arguments object with a bigger argc.
*/
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
const Value &v = argsobj.element(arg);
if (!v.isMagic(JS_ARGS_HOLE))
*vp = v;
}
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
if (!argsobj.hasOverriddenLength())
vp->setInt32(argsobj.initialLength());
}
return true;
}
static JSBool
StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
if (!obj->isStrictArguments())
return true;
StrictArgumentsObject &argsobj = obj->asStrictArguments();
if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
argsobj.setElement(arg, *vp);
return true;
}
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
}
/*
* For simplicity we use delete/set to replace the property with one
* backed by the default Object getter and setter. Note that we rely on
* args_delProperty to clear the corresponding reserved slot so the GC can
* collect its value.
*/
AutoValueRooter tvr(cx);
return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), strict) &&
js_SetPropertyHelper(cx, &argsobj, id, 0, vp, strict);
}
static JSBool
strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
{
*objp = NULL;
StrictArgumentsObject &argsobj = obj->asStrictArguments();
unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
PropertyOp getter = StrictArgGetter;
StrictPropertyOp setter = StrictArgSetter;
if (JSID_IS_INT(id)) {
uint32_t arg = uint32_t(JSID_TO_INT(id));
if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE))
return true;
attrs |= JSPROP_ENUMERATE;
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (argsobj.hasOverriddenLength())
return true;
} else {
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
!JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
return true;
}
attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
getter = CastAsPropertyOp(argsobj.global().getThrowTypeError());
setter = CastAsStrictPropertyOp(argsobj.global().getThrowTypeError());
}
Value undef = UndefinedValue();
if (!js_DefineProperty(cx, &argsobj, id, &undef, getter, setter, attrs))
return false;
*objp = &argsobj;
return true;
}
static JSBool
strictargs_enumerate(JSContext *cx, JSObject *obj)
{
StrictArgumentsObject *argsobj = &obj->asStrictArguments();
/*
* Trigger reflection in strictargs_resolve using a series of
* js_LookupProperty calls.
*/
JSObject *pobj;
JSProperty *prop;
// length
if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop))
return false;
// callee
if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop))
return false;
// caller
if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop))
return false;
for (uint32_t i = 0, argc = argsobj->initialLength(); i < argc; i++) {
if (!js_LookupProperty(cx, argsobj, INT_TO_JSID(i), &pobj, &prop))
return false;
}
return true;
}
static void
args_finalize(JSContext *cx, JSObject *obj)
{
cx->free_(reinterpret_cast<void *>(obj->asArguments().data()));
}
static void
args_trace(JSTracer *trc, JSObject *obj)
{
ArgumentsObject &argsobj = obj->asArguments();
ArgumentsData *data = argsobj.data();
MarkValue(trc, &data->callee, js_callee_str);
MarkValueRange(trc, argsobj.initialLength(), data->slots, js_arguments_str);
/*
* If a generator's arguments or call object escapes, and the generator
* frame is not executing, the generator object needs to be marked because
* it is not otherwise reachable. An executing generator is rooted by its
* invocation. To distinguish the two cases (which imply different access
* paths to the generator object), we use the JSFRAME_FLOATING_GENERATOR
* flag, which is only set on the StackFrame kept in the generator object's
* JSGenerator.
*/
#if JS_HAS_GENERATORS
StackFrame *fp = argsobj.maybeStackFrame();
if (fp && fp->isFloatingGenerator())
MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object");
#endif
}
/*
* The classes below collaborate to lazily reflect and synchronize actual
* argument values, argument count, and callee function object stored in a
* StackFrame with their corresponding property values in the frame's
* arguments object.
*/
Class js::NormalArgumentsObjectClass = {
"Arguments",
JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_FOR_OF_ITERATION,
JS_PropertyStub, /* addProperty */
args_delProperty,
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
args_enumerate,
reinterpret_cast<JSResolveOp>(args_resolve),
JS_ConvertStub,
args_finalize, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
args_trace,
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
JS_ElementIteratorStub,
NULL, /* unused */
false, /* isWrappedNative */
}
};
/*
* Strict mode arguments is significantly less magical than non-strict mode
* arguments, so it is represented by a different class while sharing some
* functionality.
*/
Class js::StrictArgumentsObjectClass = {
"Arguments",
JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_FOR_OF_ITERATION,
JS_PropertyStub, /* addProperty */
args_delProperty,
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
strictargs_enumerate,
reinterpret_cast<JSResolveOp>(strictargs_resolve),
JS_ConvertStub,
args_finalize, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
args_trace,
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
JS_ElementIteratorStub,
NULL, /* unused */
false, /* isWrappedNative */
}
};
bool
StackFrame::getValidCalleeObject(JSContext *cx, Value *vp)
{
@ -763,17 +265,10 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
return false;
}
/*
* Purposefully disconnect the returned arguments object from the frame
* by always creating a new copy that does not alias formal parameters.
* This allows function-local analysis to determine that formals are
* not aliased and generally simplifies arguments objects.
*/
ArgumentsObject *argsobj = ArgumentsObject::create(cx, fp->numActualArgs(), fp->callee());
ArgumentsObject *argsobj = ArgumentsObject::createUnexpected(cx, fp);
if (!argsobj)
return false;
fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots));
*vp = ObjectValue(*argsobj);
return true;
}

View File

@ -348,19 +348,6 @@ JSFunction::toExtended() const
return static_cast<const js::FunctionExtended *>(this);
}
/*
* Get the arguments object for the given frame. If the frame is strict mode
* code, its current arguments will be copied into the arguments object.
*
* NB: Callers *must* get the arguments object before any parameters are
* mutated when the frame is strict mode code! The emitter ensures this
* occurs for strict mode functions containing syntax which might mutate a
* named parameter by synthesizing an arguments access at the start of the
* function.
*/
extern js::ArgumentsObject *
js_GetArgsObject(JSContext *cx, js::StackFrame *fp);
extern void
js_PutArgsObject(js::StackFrame *fp);

View File

@ -921,34 +921,6 @@ TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeS
add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubsetBarrier>(script, pc, target));
}
/*
* Constraint which marks a pushed ARGUMENTS value as unknown if the script has
* an arguments object created in the future.
*/
class TypeConstraintLazyArguments : public TypeConstraint
{
public:
TypeSet *target;
TypeConstraintLazyArguments(TypeSet *target)
: TypeConstraint("lazyArgs"), target(target)
{}
void newType(JSContext *cx, TypeSet *source, Type type) {}
void newObjectState(JSContext *cx, TypeObject *object, bool force)
{
if (object->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS))
target->addType(cx, Type::UnknownType());
}
};
void
TypeSet::addLazyArguments(JSContext *cx, TypeSet *target)
{
add(cx, cx->typeLifoAlloc().new_<TypeConstraintLazyArguments>(target));
}
/////////////////////////////////////////////////////////////////////
// TypeConstraint
/////////////////////////////////////////////////////////////////////
@ -1654,47 +1626,6 @@ TypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags
return false;
}
void
types::MarkArgumentsCreated(JSContext *cx, JSScript *script)
{
JS_ASSERT(!script->createdArgs);
script->createdArgs = true;
script->uninlineable = true;
MarkTypeObjectFlags(cx, script->function(),
OBJECT_FLAG_CREATED_ARGUMENTS | OBJECT_FLAG_UNINLINEABLE);
if (!script->usedLazyArgs)
return;
AutoEnterTypeInference enter(cx);
#ifdef JS_METHODJIT
mjit::ExpandInlineFrames(cx->compartment);
#endif
if (!script->ensureRanAnalysis(cx, NULL))
return;
ScriptAnalysis *analysis = script->analysis();
for (FrameRegsIter iter(cx); !iter.done(); ++iter) {
StackFrame *fp = iter.fp();
if (fp->isScriptFrame() && fp->script() == script) {
/*
* Check locals and stack slots, assignment to individual arguments
* is treated as an escape on the arguments.
*/
Value *sp = fp->base() + analysis->getCode(iter.pc()).stackDepth;
for (Value *vp = fp->slots(); vp < sp; vp++) {
if (vp->isParticularMagic(JS_LAZY_ARGUMENTS))
*vp = ObjectOrNullValue(js_GetArgsObject(cx, fp));
}
}
}
}
static inline void
ObjectStateChange(JSContext *cx, TypeObject *object, bool markingUnknown, bool force)
{
@ -2986,9 +2917,6 @@ TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
if (singleton) {
/* Make sure flags are consistent with persistent object state. */
JS_ASSERT_IF(flags & OBJECT_FLAG_CREATED_ARGUMENTS,
(flags & OBJECT_FLAG_UNINLINEABLE) &&
interpretedFunction->script()->createdArgs);
JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE,
interpretedFunction->script()->uninlineable);
JS_ASSERT_IF(flags & OBJECT_FLAG_REENTRANT_FUNCTION,
@ -3699,20 +3627,13 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
break;
}
case JSOP_ARGUMENTS: {
case JSOP_ARGUMENTS:
/* Compute a precise type only when we know the arguments won't escape. */
TypeObject *funType = script->function()->getType(cx);
if (funType->unknownProperties() || funType->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS)) {
if (script->needsArgsObj())
pushed[0].addType(cx, Type::UnknownType());
break;
}
TypeSet *types = funType->getProperty(cx, JSID_EMPTY, false);
if (!types)
break;
types->addLazyArguments(cx, &pushed[0]);
pushed[0].addType(cx, Type::LazyArgsType());
else
pushed[0].addType(cx, Type::LazyArgsType());
break;
}
case JSOP_SETPROP:
case JSOP_SETMETHOD: {
@ -4235,122 +4156,6 @@ ScriptAnalysis::analyzeTypes(JSContext *cx)
}
result = result->next;
}
if (!script->usesArguments || script->createdArgs)
return;
/*
* Do additional analysis to determine whether the arguments object in the
* script can escape.
*/
/*
* Note: don't check for strict mode code here, even though arguments
* accesses in such scripts will always be deoptimized. These scripts can
* have a JSOP_ARGUMENTS in their prologue which the usesArguments check
* above does not account for. We filter in the interpreter and JITs
* themselves.
*/
if (script->function()->isHeavyweight() || cx->compartment->debugMode() || localsAliasStack()) {
MarkArgumentsCreated(cx, script);
return;
}
offset = 0;
while (offset < script->length) {
Bytecode *code = maybeCode(offset);
jsbytecode *pc = script->code + offset;
if (code && JSOp(*pc) == JSOP_ARGUMENTS) {
Vector<SSAValue> seen(cx);
if (!followEscapingArguments(cx, SSAValue::PushedValue(offset, 0), &seen)) {
MarkArgumentsCreated(cx, script);
return;
}
}
offset += GetBytecodeLength(pc);
}
/*
* The VM is now free to use the arguments in this script lazily. If we end
* up creating an arguments object for the script in the future or regard
* the arguments as escaping, we need to walk the stack and replace lazy
* arguments objects with actual arguments objects.
*/
script->usedLazyArgs = true;
}
bool
ScriptAnalysis::followEscapingArguments(JSContext *cx, const SSAValue &v, Vector<SSAValue> *seen)
{
/*
* trackUseChain is false for initial values of variables, which
* cannot hold the script's arguments object.
*/
if (!trackUseChain(v))
return true;
for (unsigned i = 0; i < seen->length(); i++) {
if (v == (*seen)[i])
return true;
}
if (!seen->append(v)) {
cx->compartment->types.setPendingNukeTypes(cx);
return false;
}
SSAUseChain *use = useChain(v);
while (use) {
if (!followEscapingArguments(cx, use, seen))
return false;
use = use->next;
}
return true;
}
bool
ScriptAnalysis::followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector<SSAValue> *seen)
{
if (!use->popped)
return followEscapingArguments(cx, SSAValue::PhiValue(use->offset, use->u.phi), seen);
jsbytecode *pc = script->code + use->offset;
uint32_t which = use->u.which;
JSOp op = JSOp(*pc);
if (op == JSOP_POP || op == JSOP_POPN)
return true;
/* Allow GETELEM and LENGTH on arguments objects that don't escape. */
/*
* Note: if the element index is not an integer we will mark the arguments
* as escaping at the access site.
*/
if (op == JSOP_GETELEM && which == 1)
return true;
if (op == JSOP_LENGTH)
return true;
/* Allow assignments to non-closed locals (but not arguments). */
if (op == JSOP_SETLOCAL) {
uint32_t slot = GetBytecodeSlot(script, pc);
if (!trackSlot(slot))
return false;
if (!followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen))
return false;
return followEscapingArguments(cx, SSAValue::WrittenVar(slot, use->offset), seen);
}
if (op == JSOP_GETLOCAL)
return followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen);
return false;
}
bool
@ -5703,8 +5508,6 @@ JSObject::makeLazyType(JSContext *cx)
if (isFunction() && toFunction()->isInterpreted()) {
type->interpretedFunction = toFunction();
JSScript *script = type->interpretedFunction->script();
if (script->createdArgs)
type->flags |= OBJECT_FLAG_CREATED_ARGUMENTS;
if (script->uninlineable)
type->flags |= OBJECT_FLAG_UNINLINEABLE;
if (script->reentrantOuterFunction)

View File

@ -314,26 +314,23 @@ enum {
/* Whether any objects this represents are not typed arrays. */
OBJECT_FLAG_NON_TYPED_ARRAY = 0x00040000,
/* Whether any represented script has had arguments objects created. */
OBJECT_FLAG_CREATED_ARGUMENTS = 0x00080000,
/* Whether any represented script is considered uninlineable. */
OBJECT_FLAG_UNINLINEABLE = 0x00100000,
OBJECT_FLAG_UNINLINEABLE = 0x00080000,
/* Whether any objects have an equality hook. */
OBJECT_FLAG_SPECIAL_EQUALITY = 0x00200000,
OBJECT_FLAG_SPECIAL_EQUALITY = 0x00100000,
/* Whether any objects have been iterated over. */
OBJECT_FLAG_ITERATED = 0x00400000,
OBJECT_FLAG_ITERATED = 0x00200000,
/* Outer function which has been marked reentrant. */
OBJECT_FLAG_REENTRANT_FUNCTION = 0x00800000,
OBJECT_FLAG_REENTRANT_FUNCTION = 0x00400000,
/* For a global object, whether flags were set on the RegExpStatics. */
OBJECT_FLAG_REGEXP_FLAGS_SET = 0x01000000,
OBJECT_FLAG_REGEXP_FLAGS_SET = 0x00800000,
/* Flags which indicate dynamic properties of represented objects. */
OBJECT_FLAG_DYNAMIC_MASK = 0x01ff0000,
OBJECT_FLAG_DYNAMIC_MASK = 0x00ff0000,
/*
* Whether all properties of this object are considered unknown.
@ -450,7 +447,6 @@ class TypeSet
Type type, TypeSet *types = NULL);
void addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter);
void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
void addLazyArguments(JSContext *cx, TypeSet *target);
/*
* Make an type set with the specified debugging name, not embedded in
@ -908,13 +904,6 @@ struct TypeObjectEntry
};
typedef HashSet<ReadBarriered<TypeObject>, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet;
/*
* Call to mark a script's arguments as having been created, recompile any
* dependencies and walk the stack if necessary to fix any lazy arguments.
*/
extern void
MarkArgumentsCreated(JSContext *cx, JSScript *script);
/* Whether to use a new type object when calling 'new' at script/pc. */
bool
UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc);

View File

@ -479,7 +479,6 @@ bool
js::InvokeKernel(JSContext *cx, CallArgs args, MaybeConstruct construct)
{
JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX);
JS_ASSERT(!cx->compartment->activeAnalysis);
/* MaybeConstruct is a subset of InitialFrameFlags */
@ -2970,27 +2969,10 @@ END_VARLEN_CASE
}
BEGIN_CASE(JSOP_ARGUMENTS)
{
Value rval;
if (cx->typeInferenceEnabled() && !script->strictModeCode) {
if (!script->ensureRanInference(cx))
goto error;
if (script->createdArgs) {
ArgumentsObject *arguments = js_GetArgsObject(cx, regs.fp());
if (!arguments)
goto error;
rval = ObjectValue(*arguments);
} else {
rval = MagicValue(JS_LAZY_ARGUMENTS);
}
} else {
ArgumentsObject *arguments = js_GetArgsObject(cx, regs.fp());
if (!arguments)
goto error;
rval = ObjectValue(*arguments);
}
PUSH_COPY(rval);
}
if (script->needsArgsObj())
PUSH_COPY(ObjectValue(regs.fp()->argsObj()));
else
PUSH_COPY(MagicValue(JS_OPTIMIZED_ARGUMENTS));
END_CASE(JSOP_ARGUMENTS)
BEGIN_CASE(JSOP_GETARG)
@ -4055,7 +4037,6 @@ BEGIN_CASE(JSOP_GENERATOR)
JSObject *obj = js_NewGenerator(cx);
if (!obj)
goto error;
JS_ASSERT(!regs.fp()->hasCallObj() && !regs.fp()->hasArgsObj());
regs.fp()->setReturnValue(ObjectValue(*obj));
interpReturnOK = true;
if (entryFrame != regs.fp())

View File

@ -210,7 +210,7 @@ GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp
*vp = Int32Value(lval.toString()->length());
return true;
}
if (lval.isMagic(JS_LAZY_ARGUMENTS)) {
if (lval.isMagic(JS_OPTIMIZED_ARGUMENTS)) {
*vp = Int32Value(cx->fp()->numActualArgs());
return true;
}
@ -778,14 +778,8 @@ GetElementOperation(JSContext *cx, JSOp op, const Value &lref, const Value &rref
}
}
if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
if (rref.isInt32() && size_t(rref.toInt32()) < cx->regs().fp()->numActualArgs()) {
*res = cx->regs().fp()->canonicalActualArg(rref.toInt32());
return true;
}
types::MarkArgumentsCreated(cx, cx->fp()->script());
JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
}
if (lref.isMagic(JS_OPTIMIZED_ARGUMENTS))
return NormalArgumentsObject::optimizedGetElem(cx, cx->fp(), rref, res);
bool isObject = lref.isObject();
JSObject *obj = ValueToObject(cx, lref);

View File

@ -401,7 +401,8 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
SavedCallerFun,
StrictModeCode,
UsesEval,
UsesArguments,
MayNeedArgsObj,
NeedsArgsObj,
OwnFilename,
SharedFilename
};
@ -557,8 +558,16 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
scriptBits |= (1 << StrictModeCode);
if (script->usesEval)
scriptBits |= (1 << UsesEval);
if (script->usesArguments)
scriptBits |= (1 << UsesArguments);
if (script->mayNeedArgsObj()) {
scriptBits |= (1 << MayNeedArgsObj);
/*
* In some cases, the front-end calls setNeedsArgsObj when the
* script definitely needsArgsObj; preserve this information which
* would otherwise be lost.
*/
if (script->analyzedArgsUsage() && script->needsArgsObj())
scriptBits |= (1 << NeedsArgsObj);
}
if (script->filename) {
scriptBits |= (script->filename != xdr->sharedFilename)
? (1 << OwnFilename)
@ -627,8 +636,13 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
script->strictModeCode = true;
if (scriptBits & (1 << UsesEval))
script->usesEval = true;
if (scriptBits & (1 << UsesArguments))
script->usesArguments = true;
if (scriptBits & (1 << MayNeedArgsObj)) {
script->mayNeedArgsObj_ = true;
if (scriptBits & (1 << NeedsArgsObj))
script->setNeedsArgsObj(true);
} else {
JS_ASSERT(!(scriptBits & (1 << NeedsArgsObj)));
}
}
if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)))
@ -1227,11 +1241,23 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
}
if (bce->callsEval())
script->usesEval = true;
if (bce->flags & TCF_FUN_USES_ARGUMENTS)
script->usesArguments = true;
if (bce->flags & TCF_HAS_SINGLETONS)
script->hasSingletons = true;
/*
* The arguments-usage analysis in analyzeSSA only looks at
* JSOP_ARGUMENTS use. Therefore, anything else that definitely requires an
* arguments object needs to be accounted for here.
*/
if (bce->inFunction()) {
bool needsArgsObj = bce->mayOverwriteArguments() || bce->needsEagerArguments();
if (needsArgsObj || bce->usesArguments()) {
script->mayNeedArgsObj_ = true;
if (needsArgsObj)
script->setNeedsArgsObj(true);
}
}
if (bce->globalUses.length()) {
PodCopy<GlobalSlotArray::Entry>(script->globals()->vector, &bce->globalUses[0],
bce->globalUses.length());
@ -1916,3 +1942,11 @@ JSScript::markChildren(JSTracer *trc)
}
}
}
void
JSScript::setNeedsArgsObj(bool needsArgsObj)
{
JS_ASSERT(!analyzedArgsUsage_);
analyzedArgsUsage_ = true;
needsArgsObj_ = needsArgsObj;
}

View File

@ -323,11 +323,20 @@ class DebugScript
BreakpointSite *breakpoints[1];
};
/*
* NB: after a successful JSXDR_DECODE, js_XDRScript callers must do any
* required subsequent set-up of owning function or script object and then call
* js_CallNewScriptHook.
*/
extern JSBool
XDRScript(JSXDRState *xdr, JSScript **scriptp);
} /* namespace js */
static const uint32_t JS_SCRIPT_COOKIE = 0xc00cee;
struct JSScript : public js::gc::Cell {
struct JSScript : public js::gc::Cell
{
/*
* Two successively less primitive ways to make a new JSScript. The first
* does *not* call a non-null cx->runtime->newScriptHook -- only the second,
@ -347,6 +356,8 @@ struct JSScript : public js::gc::Cell {
static JSScript *NewScriptFromEmitter(JSContext *cx, js::BytecodeEmitter *bce);
friend JSBool js::XDRScript(JSXDRState *, JSScript **);
#ifdef JS_CRASH_DIAGNOSTICS
/*
* Make sure that the cookie size does not affect the GC alignment
@ -390,7 +401,6 @@ struct JSScript : public js::gc::Cell {
bool strictModeCode:1; /* code is in strict mode */
bool compileAndGo:1; /* script was compiled with TCF_COMPILE_N_GO */
bool usesEval:1; /* script uses eval() */
bool usesArguments:1; /* script uses arguments */
bool warnedAboutTwoArgumentEval:1; /* have warned about use of
obsolete eval(s, o) in
this script */
@ -403,8 +413,6 @@ struct JSScript : public js::gc::Cell {
* outer function */
bool isActiveEval:1; /* script came from eval(), and is still active */
bool isCachedEval:1; /* script came from eval(), and is in eval cache */
bool usedLazyArgs:1; /* script has used lazy arguments at some point */
bool createdArgs:1; /* script has had arguments objects created */
bool uninlineable:1; /* script is considered uninlineable by analysis */
bool reentrantOuterFunction:1; /* outer function marked reentrant */
bool typesPurged:1; /* TypeScript has been purged at some point */
@ -414,6 +422,28 @@ struct JSScript : public js::gc::Cell {
#endif
bool callDestroyHook:1;/* need to call destroy hook */
/*
* An arguments object is created for a function script (when the function
* is first called) iff script->needsArgsObj(). There are several cases
* where the 'arguments' keyword is technically used but which don't really
* need an object (e.g., 'arguments[i]', 'f.apply(null, arguments')'). This
* determination is made during script analysis which occurs lazily (right
* before a script is run). Thus, the output of the front-end is a
* conservative 'mayNeedArgsObj' which leads to further analysis in
* analyzeBytecode and analyzeSSA. To avoid the complexity of spurious
* argument objects creation, we maintain the invariant that needsArgsObj()
* is only queried after this analysis has occurred (analyzedArgsUsage()).
*/
private:
bool mayNeedArgsObj_:1;
bool analyzedArgsUsage_:1;
bool needsArgsObj_:1;
public:
bool mayNeedArgsObj() const { return mayNeedArgsObj_; }
bool analyzedArgsUsage() const { return analyzedArgsUsage_; }
bool needsArgsObj() const { JS_ASSERT(analyzedArgsUsage()); return needsArgsObj_; }
void setNeedsArgsObj(bool needsArgsObj);
uint32_t natoms; /* length of atoms array */
uint16_t nslots; /* vars plus maximum stack depth */
uint16_t staticLevel;/* static level for display maintenance */
@ -866,14 +896,6 @@ CurrentScriptFileLineOrigin(JSContext *cx, unsigned *linenop, LineOption = NOT_C
extern JSScript *
CloneScript(JSContext *cx, JSScript *script);
/*
* NB: after a successful JSXDR_DECODE, XDRScript callers must do any
* required subsequent set-up of owning function or script object and then call
* js_CallNewScriptHook.
*/
extern JSBool
XDRScript(JSXDRState *xdr, JSScript **scriptp);
}
#endif /* jsscript_h___ */

View File

@ -287,6 +287,7 @@ typedef enum JSWhyMagic
JS_SERIALIZE_NO_NODE, /* an empty subnode in the AST serializer */
JS_LAZY_ARGUMENTS, /* lazy arguments value on the stack */
JS_UNASSIGNED_ARGUMENTS, /* the initial value of callobj.arguments */
JS_OPTIMIZED_ARGUMENTS, /* optimized-away 'arguments' value */
JS_IS_CONSTRUCTING, /* magic value passed to natives to indicate construction */
JS_GENERIC_MAGIC /* for local use */
} JSWhyMagic;

View File

@ -1110,7 +1110,7 @@ mjit::Compiler::generatePrologue()
* stub for heavyweight functions (including nesting outer functions).
*/
JS_ASSERT_IF(nesting && nesting->children, script->function()->isHeavyweight());
if (script->function()->isHeavyweight()) {
if (script->function()->isHeavyweight() || script->needsArgsObj()) {
prepareStubCall(Uses(0));
INLINE_STUBCALL(stubs::FunctionFramePrologue, REJOIN_FUNCTION_PROLOGUE);
} else {
@ -1159,21 +1159,6 @@ mjit::Compiler::generatePrologue()
}
}
if (outerScript->usesArguments && !script->function()->isHeavyweight()) {
/*
* Make sure that fp->u.nactual is always coherent. This may be
* inspected directly by JIT code, and is not guaranteed to be
* correct if the UNDERFLOW and OVERFLOW flags are not set.
*/
Jump hasArgs = masm.branchTest32(Assembler::NonZero, FrameFlagsAddress(),
Imm32(StackFrame::UNDERFLOW_ARGS |
StackFrame::OVERFLOW_ARGS |
StackFrame::HAS_ARGS_OBJ));
masm.storePtr(ImmPtr((void *)(size_t) script->function()->nargs),
Address(JSFrameReg, StackFrame::offsetOfNumActual()));
hasArgs.linkTo(masm.label(), &masm);
}
j.linkTo(masm.label(), &masm);
}
@ -2254,13 +2239,11 @@ mjit::Compiler::generateMethod()
applyTricks = LazyArgsObj;
pushSyncedEntry(0);
} else if (cx->typeInferenceEnabled() && !script->strictModeCode &&
!types::TypeSet::HasObjectFlags(cx, script->function()->getType(cx),
types::OBJECT_FLAG_CREATED_ARGUMENTS)) {
frame.push(MagicValue(JS_LAZY_ARGUMENTS));
} else {
} else if (script->needsArgsObj()) {
jsop_arguments(REJOIN_FALLTHROUGH);
pushSyncedEntry(0);
} else {
frame.push(MagicValue(JS_OPTIMIZED_ARGUMENTS));
}
END_CASE(JSOP_ARGUMENTS)
@ -3823,14 +3806,16 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
*/
if (script->function()) {
types::TypeScriptNesting *nesting = script->nesting();
if (script->function()->isHeavyweight() || (nesting && nesting->children)) {
if (script->function()->isHeavyweight() || script->needsArgsObj() ||
(nesting && nesting->children))
{
prepareStubCall(Uses(fe ? 1 : 0));
INLINE_STUBCALL(stubs::FunctionFrameEpilogue, REJOIN_NONE);
} else {
/* if (hasCallObj() || hasArgsObj()) */
/* if hasCallObj() */
Jump putObjs = masm.branchTest32(Assembler::NonZero,
Address(JSFrameReg, StackFrame::offsetOfFlags()),
Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ));
Imm32(StackFrame::HAS_CALL_OBJ));
stubcc.linkExit(putObjs, Uses(frame.frameSlots()));
stubcc.leave();

View File

@ -1081,6 +1081,10 @@ ic::SplatApplyArgs(VMFrame &f)
StackFrame *fp = f.regs.fp();
unsigned n;
/* XXX: this will flip to !fp->hasArgsObj() in a later patch. */
JS_ASSERT(fp->hasArgsObj());
if (!fp->hasArgsObj()) {
/* Extract the common/fast path where there is no args obj. */
n = fp->numActualArgs();

View File

@ -1877,10 +1877,11 @@ GetPropMaybeCached(VMFrame &f, ic::PICInfo *pic, bool cached)
PropertyName *name = pic->name;
if (name == f.cx->runtime->atomState.lengthAtom) {
if (f.regs.sp[-1].isMagic(JS_LAZY_ARGUMENTS)) {
if (f.regs.sp[-1].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
f.regs.sp[-1].setInt32(f.regs.fp()->numActualArgs());
return;
} else if (!f.regs.sp[-1].isPrimitive()) {
}
if (!f.regs.sp[-1].isPrimitive()) {
JSObject *obj = &f.regs.sp[-1].toObject();
if (obj->isArray() ||
(obj->isArguments() && !obj->asArguments().hasOverriddenLength()) ||

View File

@ -1264,10 +1264,8 @@ stubs::Throw(VMFrame &f)
void JS_FASTCALL
stubs::Arguments(VMFrame &f)
{
ArgumentsObject *arguments = js_GetArgsObject(f.cx, f.fp());
if (!arguments)
THROW();
f.regs.sp[0] = ObjectValue(*arguments);
JS_ASSERT(f.fp()->script()->needsArgsObj());
f.regs.sp[0] = ObjectValue(f.fp()->argsObj());
}
JSBool JS_FASTCALL

View File

@ -0,0 +1,576 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is SpiderMonkey arguments object code.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* 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
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "jsgc.h"
#include "jsinfer.h"
#include "jsinterp.h"
#include "vm/GlobalObject.h"
#include "vm/MethodGuard.h"
#include "vm/Stack.h"
#include "jsobjinlines.h"
#include "gc/Barrier-inl.h"
#include "vm/ArgumentsObject-inl.h"
using namespace js;
using namespace js::gc;
struct PutArg
{
PutArg(JSCompartment *comp, HeapValue *dst) : dst(dst), compartment(comp) {}
HeapValue *dst;
JSCompartment *compartment;
bool operator()(unsigned, Value *src) {
JS_ASSERT(dst->isMagic(JS_ARGS_HOLE) || dst->isUndefined());
if (!dst->isMagic(JS_ARGS_HOLE))
dst->set(compartment, *src);
++dst;
return true;
}
};
void
js_PutArgsObject(StackFrame *fp)
{
ArgumentsObject &argsobj = fp->argsObj();
if (argsobj.isNormalArguments()) {
JS_ASSERT(argsobj.maybeStackFrame() == fp);
JSCompartment *comp = fp->scopeChain().compartment();
fp->forEachCanonicalActualArg(PutArg(comp, argsobj.data()->slots));
argsobj.setStackFrame(NULL);
} else {
JS_ASSERT(!argsobj.maybeStackFrame());
}
}
ArgumentsObject *
ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee)
{
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
JSObject *proto = callee.global().getOrCreateObjectPrototype(cx);
if (!proto)
return NULL;
RootedVarTypeObject type(cx);
type = proto->getNewType(cx);
if (!type)
return NULL;
bool strict = callee.toFunction()->inStrictMode();
Class *clasp = strict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
RootedVarShape emptyArgumentsShape(cx);
emptyArgumentsShape =
EmptyShape::getInitialShape(cx, clasp, proto,
proto->getParent(), FINALIZE_KIND,
BaseShape::INDEXED);
if (!emptyArgumentsShape)
return NULL;
ArgumentsData *data = (ArgumentsData *)
cx->malloc_(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
if (!data)
return NULL;
data->callee.init(ObjectValue(callee));
for (HeapValue *vp = data->slots; vp != data->slots + argc; vp++)
vp->init(UndefinedValue());
/* We have everything needed to fill in the object, so make the object. */
JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyArgumentsShape, type, NULL);
if (!obj)
return NULL;
ArgumentsObject &argsobj = obj->asArguments();
JS_ASSERT(UINT32_MAX > (uint64_t(argc) << PACKED_BITS_COUNT));
argsobj.initInitialLength(argc);
argsobj.initData(data);
argsobj.setStackFrame(NULL);
JS_ASSERT(argsobj.numFixedSlots() >= NormalArgumentsObject::RESERVED_SLOTS);
JS_ASSERT(argsobj.numFixedSlots() >= StrictArgumentsObject::RESERVED_SLOTS);
return &argsobj;
}
bool
ArgumentsObject::create(JSContext *cx, StackFrame *fp)
{
JS_ASSERT(fp->script()->needsArgsObj());
JS_ASSERT(!fp->hasCallObj());
ArgumentsObject *argsobj = ArgumentsObject::create(cx, fp->numActualArgs(), fp->callee());
if (!argsobj)
return NULL;
/*
* Strict mode functions have arguments objects that copy the initial
* actual parameter values. Non-strict mode arguments use the frame pointer
* to retrieve up-to-date parameter values.
*/
if (argsobj->isStrictArguments())
fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots));
else
argsobj->setStackFrame(fp);
fp->initArgsObj(*argsobj);
return argsobj;
}
ArgumentsObject *
ArgumentsObject::createUnexpected(JSContext *cx, StackFrame *fp)
{
ArgumentsObject *argsobj = create(cx, fp->numActualArgs(), fp->callee());
if (!argsobj)
return NULL;
fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots));
return argsobj;
}
static JSBool
args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
ArgumentsObject &argsobj = obj->asArguments();
if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength())
argsobj.setElement(arg, MagicValue(JS_ARGS_HOLE));
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
argsobj.markLengthOverridden();
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
argsobj.asNormalArguments().clearCallee();
}
return true;
}
static JSBool
ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
if (!obj->isNormalArguments())
return true;
NormalArgumentsObject &argsobj = obj->asNormalArguments();
if (JSID_IS_INT(id)) {
/*
* arg can exceed the number of arguments if a script changed the
* prototype to point to another Arguments object with a bigger argc.
*/
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
JS_ASSERT(!argsobj.element(arg).isMagic(JS_ARGS_HOLE));
if (StackFrame *fp = argsobj.maybeStackFrame())
*vp = fp->canonicalActualArg(arg);
else
*vp = argsobj.element(arg);
}
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (!argsobj.hasOverriddenLength())
vp->setInt32(argsobj.initialLength());
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
const Value &v = argsobj.callee();
if (!v.isMagic(JS_ARGS_HOLE))
*vp = v;
}
return true;
}
static JSBool
ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
if (!obj->isNormalArguments())
return true;
NormalArgumentsObject &argsobj = obj->asNormalArguments();
if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
if (StackFrame *fp = argsobj.maybeStackFrame()) {
JSScript *script = fp->functionScript();
JS_ASSERT(script->needsArgsObj());
if (arg < fp->numFormalArgs())
types::TypeScript::SetArgument(cx, script, arg, *vp);
fp->canonicalActualArg(arg) = *vp;
return true;
}
}
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
}
/*
* For simplicity we use delete/define to replace the property with one
* backed by the default Object getter and setter. Note that we rely on
* args_delProperty to clear the corresponding reserved slot so the GC can
* collect its value. Note also that we must define the property instead
* of setting it in case the user has changed the prototype to an object
* that has a setter for this id.
*/
AutoValueRooter tvr(cx);
return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), false) &&
js_DefineProperty(cx, &argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
}
static JSBool
args_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
JSObject **objp)
{
*objp = NULL;
NormalArgumentsObject &argsobj = obj->asNormalArguments();
unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
if (JSID_IS_INT(id)) {
uint32_t arg = uint32_t(JSID_TO_INT(id));
if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE))
return true;
attrs |= JSPROP_ENUMERATE;
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (argsobj.hasOverriddenLength())
return true;
} else {
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
return true;
if (argsobj.callee().isMagic(JS_ARGS_HOLE))
return true;
}
Value undef = UndefinedValue();
if (!js_DefineProperty(cx, &argsobj, id, &undef, ArgGetter, ArgSetter, attrs))
return JS_FALSE;
*objp = &argsobj;
return true;
}
bool
NormalArgumentsObject::optimizedGetElem(JSContext *cx, StackFrame *fp, const Value &elem, Value *vp)
{
JS_ASSERT(!fp->hasArgsObj());
if (elem.isInt32()) {
int32_t i = elem.toInt32();
if (i >= 0 && uint32_t(i) < fp->numActualArgs()) {
*vp = fp->canonicalActualArg(elem.toInt32());
return true;
}
}
jsid id;
if (!ValueToId(cx, elem, &id))
return false;
if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
*vp = Int32Value(fp->numActualArgs());
return true;
}
if (id == ATOM_TO_JSID(cx->runtime->atomState.calleeAtom)) {
*vp = ObjectValue(fp->callee());
return true;
}
JSObject *proto = fp->scopeChain().global().getOrCreateObjectPrototype(cx);
if (!proto)
return false;
return proto->getGeneric(cx, id, vp);
}
static JSBool
args_enumerate(JSContext *cx, JSObject *obj)
{
NormalArgumentsObject &argsobj = obj->asNormalArguments();
/*
* Trigger reflection in args_resolve using a series of js_LookupProperty
* calls.
*/
int argc = int(argsobj.initialLength());
for (int i = -2; i != argc; i++) {
jsid id = (i == -2)
? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
: (i == -1)
? ATOM_TO_JSID(cx->runtime->atomState.calleeAtom)
: INT_TO_JSID(i);
JSObject *pobj;
JSProperty *prop;
if (!js_LookupProperty(cx, &argsobj, id, &pobj, &prop))
return false;
}
return true;
}
static JSBool
StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
if (!obj->isStrictArguments())
return true;
StrictArgumentsObject &argsobj = obj->asStrictArguments();
if (JSID_IS_INT(id)) {
/*
* arg can exceed the number of arguments if a script changed the
* prototype to point to another Arguments object with a bigger argc.
*/
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
const Value &v = argsobj.element(arg);
if (!v.isMagic(JS_ARGS_HOLE))
*vp = v;
}
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
if (!argsobj.hasOverriddenLength())
vp->setInt32(argsobj.initialLength());
}
return true;
}
static JSBool
StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
if (!obj->isStrictArguments())
return true;
StrictArgumentsObject &argsobj = obj->asStrictArguments();
if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
argsobj.setElement(arg, *vp);
return true;
}
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
}
/*
* For simplicity we use delete/set to replace the property with one
* backed by the default Object getter and setter. Note that we rely on
* args_delProperty to clear the corresponding reserved slot so the GC can
* collect its value.
*/
AutoValueRooter tvr(cx);
return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), strict) &&
js_SetPropertyHelper(cx, &argsobj, id, 0, vp, strict);
}
static JSBool
strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
{
*objp = NULL;
StrictArgumentsObject &argsobj = obj->asStrictArguments();
unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
PropertyOp getter = StrictArgGetter;
StrictPropertyOp setter = StrictArgSetter;
if (JSID_IS_INT(id)) {
uint32_t arg = uint32_t(JSID_TO_INT(id));
if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE))
return true;
attrs |= JSPROP_ENUMERATE;
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (argsobj.hasOverriddenLength())
return true;
} else {
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
!JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
return true;
}
attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
getter = CastAsPropertyOp(argsobj.global().getThrowTypeError());
setter = CastAsStrictPropertyOp(argsobj.global().getThrowTypeError());
}
Value undef = UndefinedValue();
if (!js_DefineProperty(cx, &argsobj, id, &undef, getter, setter, attrs))
return false;
*objp = &argsobj;
return true;
}
static JSBool
strictargs_enumerate(JSContext *cx, JSObject *obj)
{
StrictArgumentsObject *argsobj = &obj->asStrictArguments();
/*
* Trigger reflection in strictargs_resolve using a series of
* js_LookupProperty calls.
*/
JSObject *pobj;
JSProperty *prop;
// length
if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop))
return false;
// callee
if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop))
return false;
// caller
if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop))
return false;
for (uint32_t i = 0, argc = argsobj->initialLength(); i < argc; i++) {
if (!js_LookupProperty(cx, argsobj, INT_TO_JSID(i), &pobj, &prop))
return false;
}
return true;
}
static void
args_finalize(JSContext *cx, JSObject *obj)
{
cx->free_(reinterpret_cast<void *>(obj->asArguments().data()));
}
static void
args_trace(JSTracer *trc, JSObject *obj)
{
ArgumentsObject &argsobj = obj->asArguments();
ArgumentsData *data = argsobj.data();
MarkValue(trc, &data->callee, js_callee_str);
MarkValueRange(trc, argsobj.initialLength(), data->slots, js_arguments_str);
/*
* If a generator's arguments or call object escapes, and the generator
* frame is not executing, the generator object needs to be marked because
* it is not otherwise reachable. An executing generator is rooted by its
* invocation. To distinguish the two cases (which imply different access
* paths to the generator object), we use the JSFRAME_FLOATING_GENERATOR
* flag, which is only set on the StackFrame kept in the generator object's
* JSGenerator.
*/
#if JS_HAS_GENERATORS
StackFrame *fp = argsobj.maybeStackFrame();
if (fp && fp->isFloatingGenerator())
MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object");
#endif
}
/*
* The classes below collaborate to lazily reflect and synchronize actual
* argument values, argument count, and callee function object stored in a
* StackFrame with their corresponding property values in the frame's
* arguments object.
*/
Class js::NormalArgumentsObjectClass = {
"Arguments",
JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_FOR_OF_ITERATION,
JS_PropertyStub, /* addProperty */
args_delProperty,
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
args_enumerate,
reinterpret_cast<JSResolveOp>(args_resolve),
JS_ConvertStub,
args_finalize, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
args_trace,
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
JS_ElementIteratorStub,
NULL, /* unused */
false, /* isWrappedNative */
}
};
/*
* Strict mode arguments is significantly less magical than non-strict mode
* arguments, so it is represented by a different class while sharing some
* functionality.
*/
Class js::StrictArgumentsObjectClass = {
"Arguments",
JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_FOR_OF_ITERATION,
JS_PropertyStub, /* addProperty */
args_delProperty,
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
strictargs_enumerate,
reinterpret_cast<JSResolveOp>(strictargs_resolve),
JS_ConvertStub,
args_finalize, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
args_trace,
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
JS_ElementIteratorStub,
NULL, /* unused */
false, /* isWrappedNative */
}
};

View File

@ -166,12 +166,20 @@ class ArgumentsObject : public JSObject
#endif
void initInitialLength(uint32_t length);
void initData(ArgumentsData *data);
static ArgumentsObject *create(JSContext *cx, uint32_t argc, JSObject &callee);
public:
/* Create an arguments object for the given callee function and frame. */
static ArgumentsObject *create(JSContext *cx, uint32_t argc, JSObject &callee);
/* Create an arguments object for a frame that is expecting them. */
static bool create(JSContext *cx, StackFrame *fp);
/*
* Purposefully disconnect the returned arguments object from the frame
* by always creating a new copy that does not alias formal parameters.
* This allows function-local analysis to determine that formals are
* not aliased and generally simplifies arguments objects.
*/
static ArgumentsObject *createUnexpected(JSContext *cx, StackFrame *fp);
/*
* Return the initial length of the arguments. This may differ from the
@ -223,11 +231,6 @@ class ArgumentsObject : public JSObject
class NormalArgumentsObject : public ArgumentsObject
{
friend bool JSObject::isNormalArguments() const;
friend struct EmptyShape; // for EmptyShape::getEmptyArgumentsShape
friend ArgumentsObject *
ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee);
public:
/*
* Stores arguments.callee, or MagicValue(JS_ARGS_HOLE) if the callee has
@ -237,14 +240,17 @@ class NormalArgumentsObject : public ArgumentsObject
/* Clear the location storing arguments.callee's initial value. */
inline void clearCallee();
/*
* Return 'arguments[index]' for some unmodified NormalArgumentsObject of
* 'fp' (the actual instance of 'arguments' doesn't matter so it does not
* have to be passed or even created).
*/
static bool optimizedGetElem(JSContext *cx, StackFrame *fp, const Value &elem, Value *vp);
};
class StrictArgumentsObject : public ArgumentsObject
{
friend bool JSObject::isStrictArguments() const;
friend ArgumentsObject *
ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee);
};
{};
} // namespace js

View File

@ -65,13 +65,6 @@ js_PutCallObject(StackFrame *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;
@ -253,6 +246,8 @@ CallObject::createForFunction(JSContext *cx, StackFrame *fp)
callobj->setStackFrame(fp);
fp->setScopeChainWithOwnCallObj(*callobj);
if (fp->hasArgsObj())
callobj->setArguments(ObjectValue(fp->argsObj()));
return callobj;
}
@ -271,26 +266,33 @@ CallObject::createForStrictEval(JSContext *cx, StackFrame *fp)
JSBool
CallObject::getArgumentsOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
CallObject &callobj = obj->asCall();
*vp = obj->asCall().arguments();
StackFrame *fp = callobj.maybeStackFrame();
if (fp && callobj.arguments().isMagic(JS_UNASSIGNED_ARGUMENTS)) {
JSObject *argsobj = js_GetArgsObject(cx, fp);
if (!argsobj)
/*
* This can only happen through eval-in-frame. Eventually, this logic can
* be hoisted into debugger scope wrappers. That will allow 'arguments' to
* be a pure data property and allow call_resolve to be removed.
*/
if (vp->isMagic(JS_UNASSIGNED_ARGUMENTS)) {
#ifdef DEBUG
for (StackFrame *fp = cx->fp(); !fp->isDebuggerFrame(); fp = fp->prev())
JS_ASSERT(fp->isEvalFrame());
#endif
StackFrame *fp = obj->asCall().maybeStackFrame();
ArgumentsObject *argsObj = ArgumentsObject::createUnexpected(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();
*vp = ObjectValue(*argsObj);
obj->asCall().setArguments(*vp);
}
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;

View File

@ -302,15 +302,6 @@ StackFrame::actualArgsEnd() const
return formalArgs() + numActualArgs();
}
inline void
StackFrame::setArgsObj(ArgumentsObject &obj)
{
JS_ASSERT_IF(hasArgsObj(), &obj == argsObj_);
JS_ASSERT_IF(!hasArgsObj(), numActualArgs() == obj.initialLength());
argsObj_ = &obj;
flags_ |= HAS_ARGS_OBJ;
}
inline void
StackFrame::setScopeChainNoCallObj(JSObject &obj)
{
@ -366,8 +357,13 @@ inline bool
StackFrame::functionPrologue(JSContext *cx)
{
JS_ASSERT(isNonEvalFunctionFrame());
JS_ASSERT(!isGeneratorFrame());
JSFunction *fun = this->fun();
JSScript *script = fun->script();
if (script->needsArgsObj() && !ArgumentsObject::create(cx, this))
return false;
if (fun->isHeavyweight()) {
if (!CallObject::createForFunction(cx, this))
@ -377,7 +373,7 @@ StackFrame::functionPrologue(JSContext *cx)
scopeChain();
}
if (script()->nesting()) {
if (script->nesting()) {
JS_ASSERT(maintainNestingState());
types::NestingPrologue(cx, this);
}
@ -391,10 +387,9 @@ StackFrame::functionEpilogue()
JS_ASSERT(isNonEvalFunctionFrame());
if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
/* NB: there is an ordering dependency here. */
if (hasCallObj())
js_PutCallObject(this);
else if (hasArgsObj())
if (hasArgsObj())
js_PutArgsObject(this);
}

View File

@ -727,12 +727,18 @@ class StackFrame
template <class Op> inline bool forEachFormalArg(Op op);
bool hasArgsObj() const {
/*
* HAS_ARGS_OBJ is still technically not equivalent to
* script()->needsArgsObj() during functionPrologue (where GC can
* observe a frame that needsArgsObj but has not yet been given the
* args). This can be fixed by creating and rooting the args/call
* object before pushing the frame, which should be done eventually.
*/
return !!(flags_ & HAS_ARGS_OBJ);
}
ArgumentsObject &argsObj() const {
JS_ASSERT(hasArgsObj());
JS_ASSERT(!isEvalFrame());
return *argsObj_;
}
@ -740,7 +746,12 @@ class StackFrame
return hasArgsObj() ? &argsObj() : NULL;
}
inline void setArgsObj(ArgumentsObject &obj);
void initArgsObj(ArgumentsObject &argsObj) {
JS_ASSERT(script()->needsArgsObj());
JS_ASSERT(!hasArgsObj());
argsObj_ = &argsObj;
flags_ |= HAS_ARGS_OBJ;
}
/*
* This value
@ -903,8 +914,8 @@ class StackFrame
/*
* Epilogue for function frames: put any args or call object for the frame
* which may still be live, and maintain type nesting invariants. Note:
* this does not mark the epilogue as having been completed, since the
* frame is about to be popped. Use updateEpilogueFlags for this.
* this does mark the epilogue as having been completed, since the frame is
* about to be popped. Use updateEpilogueFlags for this.
*/
inline void functionEpilogue();