mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 733950 - create arguments object eagerly (r=bhackett)
--HG-- rename : js/src/jsfun.cpp => js/src/vm/ArgumentsObject.cpp
This commit is contained in:
parent
225350a951
commit
fccfecaaff
@ -150,6 +150,7 @@ CPPSRCS = \
|
||||
jsxml.cpp \
|
||||
prmjtime.cpp \
|
||||
sharkctl.cpp \
|
||||
ArgumentsObject.cpp \
|
||||
ScopeObject.cpp \
|
||||
Debugger.cpp \
|
||||
GlobalObject.cpp \
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
507
js/src/jsfun.cpp
507
js/src/jsfun.cpp
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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___ */
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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()) ||
|
||||
|
@ -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
|
||||
|
576
js/src/vm/ArgumentsObject.cpp
Normal file
576
js/src/vm/ArgumentsObject.cpp
Normal 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 */
|
||||
}
|
||||
};
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user