Bug 579183 - loosen-up StackSegment invariants - part 5 - CallArgs (r=waldo)

This commit is contained in:
Luke Wagner
2010-07-30 10:41:03 -07:00
parent ada1f6c7f2
commit fef3752bae
9 changed files with 154 additions and 172 deletions

View File

@@ -4871,13 +4871,12 @@ JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv)
if (!cx->stack().pushInvokeArgs(cx, argc, args))
return NULL;
Value *vp = args.getvp();
vp[0].setObject(*ctor);
vp[1].setNull();
memcpy(vp + 2, argv, argc * sizeof(jsval));
args.callee().setObject(*ctor);
args.thisv().setNull();
memcpy(args.argv(), argv, argc * sizeof(jsval));
bool ok = InvokeConstructor(cx, args);
JSObject *obj = ok ? vp[0].toObjectOrNull() : NULL;
JSObject *obj = ok ? args.rval().toObjectOrNull() : NULL;
LAST_FRAME_CHECKS(cx, ok);
return obj;

View File

@@ -1376,14 +1376,13 @@ array_toString(JSContext *cx, uintN argc, Value *vp)
if (!cx->stack().pushInvokeArgs(cx, 0, args))
return false;
Value *sp = args.getvp();
sp[0] = join;
sp[1].setObject(*obj);
args.callee() = join;
args.thisv().setObject(*obj);
/* Do the call. */
if (!Invoke(cx, args, 0))
return false;
*vp = *args.getvp();
*vp = args.rval();
return true;
}
@@ -1727,18 +1726,17 @@ sort_compare(void *arg, const void *a, const void *b, int *result)
if (!JS_CHECK_OPERATION_LIMIT(cx))
return JS_FALSE;
Value *invokevp = ca->args.getvp();
Value *sp = invokevp;
*sp++ = ca->fval;
*sp++ = NullValue();
*sp++ = *av;
*sp++ = *bv;
CallArgs &args = ca->args;
args.callee() = ca->fval;
args.thisv().setNull();
args[0] = *av;
args[1] = *bv;
if (!Invoke(cx, ca->args, 0))
return JS_FALSE;
jsdouble cmp;
if (!ValueToNumber(cx, *invokevp, &cmp))
if (!ValueToNumber(cx, args.rval(), &cmp))
return JS_FALSE;
/* Clamp cmp to -1, 0, 1. */
@@ -2774,7 +2772,6 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
MUST_FLOW_THROUGH("out");
JSBool ok = JS_TRUE;
JSBool cond;
Value *invokevp = args.getvp();
Value calleev, thisv, objv;
calleev.setObject(*callable);
@@ -2792,18 +2789,16 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
/*
* Push callable and 'this', then args. We must do this for every
* iteration around the loop since js_Invoke uses invokevp[0] for return
* value storage, while some native functions use invokevp[1] for local
* rooting.
* iteration around the loop since Invoke clobbers its arguments.
*/
Value *sp = invokevp;
*sp++ = calleev;
*sp++ = thisv;
args.callee() = calleev;
args.thisv() = thisv;
Value *sp = args.argv();
if (REDUCE_MODE(mode))
*sp++ = *vp;
*sp++ = tvr.value();
sp++->setInt32(i);
*sp++ = objv;
sp[0] = tvr.value();
sp[1].setInt32(i);
sp[2] = objv;
/* Do the call. */
ok = Invoke(cx, args, 0);
@@ -2811,7 +2806,7 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
break;
if (mode > MAP)
cond = js_ValueToBoolean(*invokevp);
cond = js_ValueToBoolean(args.rval());
#ifdef __GNUC__ /* quell GCC overwarning */
else
cond = JS_FALSE;
@@ -2822,10 +2817,10 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
break;
case REDUCE:
case REDUCE_RIGHT:
*vp = *invokevp;
*vp = args.rval();
break;
case MAP:
ok = SetArrayElement(cx, newarr, i, *invokevp);
ok = SetArrayElement(cx, newarr, i, args.rval());
if (!ok)
goto out;
break;

View File

@@ -237,8 +237,7 @@ JS_REQUIRES_STACK bool
StackSpace::pushSegmentForInvoke(JSContext *cx, uintN argc, InvokeArgsGuard &ag)
{
Value *start = firstUnused();
uintN vplen = 2 + argc;
ptrdiff_t nvals = VALUES_PER_STACK_SEGMENT + vplen;
ptrdiff_t nvals = VALUES_PER_STACK_SEGMENT + 2 + argc;
if (!ensureSpace(cx, start, nvals))
return false;
@@ -248,8 +247,8 @@ StackSpace::pushSegmentForInvoke(JSContext *cx, uintN argc, InvokeArgsGuard &ag)
ag.cx = cx;
ag.seg = seg;
ag.vp = seg->getInitialArgBegin();
ag.argc = argc;
ag.argv_ = seg->getInitialArgBegin() + 2;
ag.argc_ = argc;
/* Use invokeArgEnd to root [vp, vpend) until the frame is pushed. */
#ifdef DEBUG
@@ -259,7 +258,7 @@ StackSpace::pushSegmentForInvoke(JSContext *cx, uintN argc, InvokeArgsGuard &ag)
invokeFrame = NULL;
#endif
ag.prevInvokeArgEnd = invokeArgEnd;
invokeArgEnd = ag.vp + vplen;
invokeArgEnd = ag.argv() + ag.argc();
return true;
}
@@ -269,7 +268,7 @@ StackSpace::popSegmentForInvoke(const InvokeArgsGuard &ag)
JS_ASSERT(!currentSegment->inContext());
JS_ASSERT(ag.seg == currentSegment);
JS_ASSERT(invokeSegment == currentSegment);
JS_ASSERT(invokeArgEnd == ag.vp + 2 + ag.argc);
JS_ASSERT(invokeArgEnd == ag.argv() + ag.argc());
currentSegment = currentSegment->getPreviousInMemory();

View File

@@ -489,18 +489,12 @@ class StackSegment
static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
/*
* The ternary constructor is used when arguments are already pushed on the
* stack (as the sp of the current frame), which should only happen from within
* Interpret. Otherwise, see StackSpace::pushInvokeArgs.
*/
class InvokeArgsGuard
/* See StackSpace::pushInvokeArgs. */
class InvokeArgsGuard : public CallArgs
{
friend class StackSpace;
JSContext *cx; /* null implies nothing pushed */
StackSegment *seg;
Value *vp;
uintN argc;
Value *prevInvokeArgEnd;
#ifdef DEBUG
StackSegment *prevInvokeSegment;
@@ -511,8 +505,15 @@ class InvokeArgsGuard
inline InvokeArgsGuard(JSContext *cx, Value *vp, uintN argc);
inline ~InvokeArgsGuard();
bool pushed() const { return cx != NULL; }
Value *getvp() const { JS_ASSERT(pushed()); return vp; }
uintN getArgc() const { JS_ASSERT(pushed()); return argc; }
};
/*
* This type can be used to call Invoke when the arguments have already been
* pushed onto the stack as part of normal execution.
*/
struct InvokeArgsAlreadyOnTheStack : CallArgs
{
InvokeArgsAlreadyOnTheStack(Value *vp, uintN argc) : CallArgs(vp + 2, argc) {}
};
/* See StackSpace::pushInvokeFrame. */
@@ -639,13 +640,12 @@ class StackSpace
InvokeArgsGuard &ag);
JS_REQUIRES_STACK bool pushInvokeFrameSlow(JSContext *cx, const InvokeArgsGuard &ag,
InvokeFrameGuard &fg);
JS_REQUIRES_STACK void popInvokeFrameSlow(const InvokeArgsGuard &ag);
JS_REQUIRES_STACK void popInvokeFrameSlow(const CallArgs &args);
JS_REQUIRES_STACK void popSegmentForInvoke(const InvokeArgsGuard &ag);
/* Although guards are friends, XGuard should only call popX(). */
friend class InvokeArgsGuard;
JS_REQUIRES_STACK inline void bumpInvokeArgEnd(InvokeArgsGuard &ag);
JS_REQUIRES_STACK inline void popInvokeArgs(const InvokeArgsGuard &ag);
JS_REQUIRES_STACK inline void popInvokeArgs(const InvokeArgsGuard &args);
friend class InvokeFrameGuard;
JS_REQUIRES_STACK void popInvokeFrame(const InvokeFrameGuard &ag);
friend class ExecuteFrameGuard;
@@ -732,13 +732,12 @@ class StackSpace
bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard &ag);
/* These functions are called inside Invoke, not Invoke clients. */
bool getInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
bool getInvokeFrame(JSContext *cx, const CallArgs &args,
uintN nmissing, uintN nfixed,
InvokeFrameGuard &fg) const;
JS_REQUIRES_STACK
void pushInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
InvokeFrameGuard &fg);
void pushInvokeFrame(JSContext *cx, const CallArgs &args, InvokeFrameGuard &fg);
/*
* For the simpler case when arguments are allocated at the same time as

View File

@@ -173,31 +173,11 @@ StackSpace::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard &ag)
#endif
ag.cx = cx;
ag.vp = vp;
ag.argc = argc;
ag.argv_ = vp + 2;
ag.argc_ = argc;
return true;
}
JS_ALWAYS_INLINE void
StackSpace::bumpInvokeArgEnd(InvokeArgsGuard &ag)
{
#ifdef DEBUG
ag.prevInvokeSegment = invokeSegment;
invokeSegment = ag.cx->currentSegment;
ag.prevInvokeFrame = invokeFrame;
invokeFrame = ag.cx->fp;
#endif
ag.prevInvokeArgEnd = invokeArgEnd;
invokeArgEnd = ag.vp + 2 + ag.argc;
}
JS_ALWAYS_INLINE
InvokeArgsGuard::InvokeArgsGuard(JSContext *cx, Value *vp, uintN argc)
: cx(cx), seg(NULL), vp(vp), argc(argc)
{
cx->stack().bumpInvokeArgEnd(*this);
}
JS_REQUIRES_STACK JS_ALWAYS_INLINE void
StackSpace::popInvokeArgs(const InvokeArgsGuard &ag)
{
@@ -209,7 +189,7 @@ StackSpace::popInvokeArgs(const InvokeArgsGuard &ag)
JS_ASSERT(isCurrentAndActive(ag.cx));
JS_ASSERT(invokeSegment == currentSegment);
JS_ASSERT(invokeFrame == ag.cx->fp);
JS_ASSERT(invokeArgEnd == ag.vp + 2 + ag.argc);
JS_ASSERT(invokeArgEnd == ag.argv() + ag.argc());
#ifdef DEBUG
invokeSegment = ag.prevInvokeSegment;
@@ -227,14 +207,13 @@ InvokeArgsGuard::~InvokeArgsGuard()
}
JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
StackSpace::getInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
StackSpace::getInvokeFrame(JSContext *cx, const CallArgs &args,
uintN nmissing, uintN nfixed,
InvokeFrameGuard &fg) const
{
JS_ASSERT(firstUnused() == invokeArgEnd);
JS_ASSERT(invokeArgEnd == ag.vp + 2 + ag.argc);
JS_ASSERT(firstUnused() == args.argv() + args.argc());
Value *start = invokeArgEnd;
Value *start = args.argv() + args.argc();
ptrdiff_t nvals = nmissing + VALUES_PER_STACK_FRAME + nfixed;
if (!ensureSpace(cx, start, nvals))
return false;
@@ -243,10 +222,10 @@ StackSpace::getInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
}
JS_REQUIRES_STACK JS_ALWAYS_INLINE void
StackSpace::pushInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
StackSpace::pushInvokeFrame(JSContext *cx, const CallArgs &args,
InvokeFrameGuard &fg)
{
JS_ASSERT(firstUnused() == ag.vp + 2 + ag.argc);
JS_ASSERT(firstUnused() == args.argv() + args.argc());
JSStackFrame *fp = fg.fp;
JSStackFrame *down = cx->fp;

View File

@@ -2030,12 +2030,12 @@ js_fun_call(JSContext *cx, uintN argc, Value *vp)
return JS_FALSE;
/* Push fval, obj, and the args. */
args.getvp()[0] = fval;
args.getvp()[1] = ObjectOrNullValue(obj);
memcpy(args.getvp() + 2, argv, argc * sizeof *argv);
args.callee() = fval;
args.thisv().setObjectOrNull(obj);
memcpy(args.argv(), argv, argc * sizeof *argv);
bool ok = Invoke(cx, args, 0);
*vp = *args.getvp();
*vp = args.rval();
return ok;
}
@@ -2111,9 +2111,8 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp)
return false;
/* Push fval, obj, and aobj's elements as args. */
Value *sp = args.getvp();
*sp++ = fval;
*sp++ = ObjectOrNullValue(obj);
args.callee() = fval;
args.thisv().setObjectOrNull(obj);
/* Steps 7-8. */
if (aobj && aobj->isArguments() && !aobj->isArgsLengthOverridden()) {
@@ -2124,31 +2123,32 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp)
* corresponding arguments element. See args_delProperty.
*/
JSStackFrame *fp = (JSStackFrame *) aobj->getPrivate();
Value *argv = args.argv();
if (fp) {
memcpy(sp, fp->argv, n * sizeof(Value));
memcpy(argv, fp->argv, n * sizeof(Value));
for (uintN i = 0; i < n; i++) {
if (aobj->getArgsElement(i).isMagic(JS_ARGS_HOLE)) // suppress deleted element
sp[i].setUndefined();
argv[i].setUndefined();
}
} else {
for (uintN i = 0; i < n; i++) {
sp[i] = aobj->getArgsElement(i);
if (sp[i].isMagic(JS_ARGS_HOLE))
sp[i].setUndefined();
argv[i] = aobj->getArgsElement(i);
if (argv[i].isMagic(JS_ARGS_HOLE))
argv[i].setUndefined();
}
}
} else {
Value *argv = args.argv();
for (uintN i = 0; i < n; i++) {
if (!aobj->getProperty(cx, INT_TO_JSID(jsint(i)), sp))
if (!aobj->getProperty(cx, INT_TO_JSID(jsint(i)), &argv[i]))
return JS_FALSE;
sp++;
}
}
/* Step 9. */
if (!Invoke(cx, args, 0))
return false;
*vp = *args.getvp();
*vp = args.rval();
return true;
}

View File

@@ -412,18 +412,17 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags)
JSObject *obj = &vp[0].toObject();
JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass);
Value *invokevp = args.getvp();
invokevp[0] = obj->fslots[JSSLOT_FOUND_FUNCTION];
invokevp[1] = vp[1];
invokevp[2] = obj->fslots[JSSLOT_SAVED_ID];
args.callee() = obj->fslots[JSSLOT_FOUND_FUNCTION];
args.thisv() = vp[1];
args[0] = obj->fslots[JSSLOT_SAVED_ID];
JSObject *argsobj = js_NewArrayObject(cx, argc, vp + 2);
if (!argsobj)
return JS_FALSE;
invokevp[3].setObject(*argsobj);
args[1].setObject(*argsobj);
JSBool ok = (flags & JSINVOKE_CONSTRUCT)
? InvokeConstructor(cx, args)
: Invoke(cx, args, flags);
vp[0] = invokevp[0];
vp[0] = args.rval();
return ok;
}
@@ -460,16 +459,15 @@ callJSNative(JSContext *cx, CallOp callOp, JSObject *thisp, uintN argc, Value *a
template <typename T>
static JS_REQUIRES_STACK bool
InvokeCommon(JSContext *cx, JSFunction *fun, JSScript *script, T native,
const InvokeArgsGuard &args, uintN flags)
const CallArgs &argsRef, uintN flags)
{
uintN argc = args.getArgc();
Value *vp = args.getvp();
CallArgs args = argsRef;
if (native && fun && fun->isFastNative()) {
#ifdef DEBUG_NOT_THROWING
JSBool alreadyThrowing = cx->throwing;
#endif
JSBool ok = callJSFastNative(cx, (FastNative) native, argc, vp);
JSBool ok = callJSFastNative(cx, (FastNative) native, args.argc(), args.base());
JS_RUNTIME_METER(cx->runtime, nativeCalls);
#ifdef DEBUG_NOT_THROWING
if (ok && !alreadyThrowing)
@@ -484,13 +482,13 @@ InvokeCommon(JSContext *cx, JSFunction *fun, JSScript *script, T native,
if (fun) {
if (fun->isInterpreted()) {
uintN minargs = fun->nargs;
nmissing = minargs > argc ? minargs - argc : 0;
nmissing = minargs > args.argc() ? minargs - args.argc() : 0;
nvars = fun->u.i.nvars;
} else if (fun->isFastNative()) {
nvars = nmissing = 0;
} else {
uintN minargs = fun->nargs;
nmissing = (minargs > argc ? minargs - argc : 0) + fun->u.n.extra;
nmissing = (minargs > args.argc() ? minargs - args.argc() : 0) + fun->u.n.extra;
nvars = 0;
}
} else {
@@ -509,18 +507,18 @@ InvokeCommon(JSContext *cx, JSFunction *fun, JSScript *script, T native,
JSStackFrame *fp = frame.getFrame();
/* Initialize missing missing arguments and new local variables. */
Value *missing = vp + 2 + argc;
Value *missing = args.argv() + args.argc();
SetValueRangeToUndefined(missing, nmissing);
SetValueRangeToUndefined(fp->slots(), nvars);
/* Initialize frame. */
fp->thisv = vp[1];
fp->thisv = args.thisv();
fp->callobj = NULL;
fp->argsobj = NULL;
fp->script = script;
fp->fun = fun;
fp->argc = argc;
fp->argv = vp + 2;
fp->argc = args.argc();
fp->argv = args.argv();
fp->rval = (flags & JSINVOKE_CONSTRUCT) ? fp->thisv : UndefinedValue();
fp->annotation = NULL;
fp->scopeChain = NULL;
@@ -542,7 +540,7 @@ InvokeCommon(JSContext *cx, JSFunction *fun, JSScript *script, T native,
cx->stack().pushInvokeFrame(cx, args, frame);
/* Now that the frame has been pushed, fix up the scope chain. */
JSObject *parent = vp[0].toObject().getParent();
JSObject *parent = args.callee().toObject().getParent();
if (native) {
/* Slow natives and call ops expect the caller's scopeChain as their scopeChain. */
if (JSStackFrame *down = fp->down)
@@ -597,7 +595,7 @@ InvokeCommon(JSContext *cx, JSFunction *fun, JSScript *script, T native,
}
fp->putActivationObjects(cx);
*vp = fp->rval;
args.rval() = fp->rval;
return ok;
}
@@ -643,19 +641,16 @@ DoSlowCall(JSContext *cx, uintN argc, Value *vp)
* when done. Then push the return value.
*/
JS_REQUIRES_STACK bool
Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
Invoke(JSContext *cx, const CallArgs &args, uintN flags)
{
Value *vp = args.getvp();
uintN argc = args.getArgc();
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
const Value &v = vp[0];
if (v.isPrimitive()) {
js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
if (args.callee().isPrimitive()) {
js_ReportIsNotFunction(cx, &args.callee(), flags & JSINVOKE_FUNFLAGS);
return false;
}
JSObject *funobj = &v.toObject();
JSObject *funobj = &args.callee().toObject();
Class *clasp = funobj->getClass();
if (clasp == &js_FunctionClass) {
@@ -670,10 +665,10 @@ Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
if (script->isEmpty()) {
if (flags & JSINVOKE_CONSTRUCT) {
JS_ASSERT(vp[1].isObject());
*vp = vp[1];
JS_ASSERT(args.thisv().isObject());
args.rval() = args.thisv();
} else {
vp->setUndefined();
args.rval().setUndefined();
}
return true;
}
@@ -684,15 +679,15 @@ Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
/* Handle bound method special case. */
vp[1].setObject(*funobj->getParent());
} else if (!vp[1].isObjectOrNull()) {
args.thisv().setObject(*funobj->getParent());
} else if (!args.thisv().isObjectOrNull()) {
JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
if (PrimitiveThisTest(fun, vp[1]))
if (PrimitiveThisTest(fun, args.thisv()))
return InvokeCommon(cx, fun, script, native, args, flags);
}
if (flags & JSINVOKE_CONSTRUCT) {
JS_ASSERT(args.getvp()[1].isObject());
JS_ASSERT(args.thisv().isObject());
} else {
/*
* We must call js_ComputeThis in case we are not called from the
@@ -705,8 +700,7 @@ Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
* the appropriate this-computing bytecode, e.g., JSOP_THIS.
*/
if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
Value *vp = args.getvp();
if (!ComputeThisFromVp(cx, vp))
if (!args.computeThis(cx))
return false;
flags |= JSFRAME_COMPUTED_THIS;
}
@@ -716,13 +710,13 @@ Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
#if JS_HAS_NO_SUCH_METHOD
if (clasp == &js_NoSuchMethodClass)
return NoSuchMethod(cx, argc, vp, flags);
return NoSuchMethod(cx, args.argc(), args.base(), flags);
#endif
/* Try a call or construct native object op. */
if (flags & JSINVOKE_CONSTRUCT) {
if (!vp[1].isObjectOrNull()) {
if (!js_PrimitiveToObject(cx, &vp[1]))
if (!args.thisv().isObjectOrNull()) {
if (!js_PrimitiveToObject(cx, &args.thisv()))
return false;
}
return InvokeCommon(cx, NULL, NULL, DoConstruct, args, flags);
@@ -747,9 +741,9 @@ InternalInvoke(JSContext *cx, const Value &thisv, const Value &fval, uintN flags
if (!cx->stack().pushInvokeArgs(cx, argc, args))
return JS_FALSE;
args.getvp()[0] = fval;
args.getvp()[1] = thisv;
memcpy(args.getvp() + 2, argv, argc * sizeof(Value));
args.callee() = fval;
args.thisv() = thisv;
memcpy(args.argv(), argv, argc * sizeof(Value));
if (!Invoke(cx, args, flags))
return JS_FALSE;
@@ -760,7 +754,7 @@ InternalInvoke(JSContext *cx, const Value &thisv, const Value &fval, uintN flags
* (js_ValueToString for example) callers do not need to manage roots
* for local, temporary references to such results.
*/
*rval = *args.getvp();
*rval = args.rval();
if (rval->isMarkable())
cx->weakRoots.lastInternalResult = rval->asGCThing();
@@ -1139,30 +1133,24 @@ InstanceOfSlow(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
}
JS_REQUIRES_STACK bool
InvokeConstructor(JSContext *cx, const InvokeArgsGuard &args)
InvokeConstructor(JSContext *cx, const CallArgs &argsRef)
{
JS_ASSERT(!js_FunctionClass.construct);
Value *vp = args.getvp();
CallArgs args = argsRef;
if (vp->isPrimitive()) {
if (args.callee().isPrimitive()) {
/* Use js_ValueToFunction to report an error. */
JS_ALWAYS_TRUE(!js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT));
JS_ALWAYS_TRUE(!js_ValueToFunction(cx, &args.callee(), JSV2F_CONSTRUCT));
return false;
}
JSObject *obj2 = &vp->toObject();
JSObject *obj2 = &args.callee().toObject();
/*
* Get the constructor prototype object for this function.
* Use the nominal 'this' parameter slot, vp[1], as a local
* root to protect this prototype, in case it has no other
* strong refs.
*/
if (!obj2->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &vp[1]))
Value protov;
if (!obj2->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov))
return false;
const Value &v = vp[1];
JSObject *proto = v.isObjectOrNull() ? v.toObjectOrNull() : NULL;
JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
JSObject *parent = obj2->getParent();
Class *clasp = &js_ObjectClass;
@@ -1178,25 +1166,21 @@ InvokeConstructor(JSContext *cx, const InvokeArgsGuard &args)
}
/* Keep |obj| rooted in case vp[1] is overwritten with a primitive. */
AutoObjectRooter tvr(cx, obj);
/* Now we have an object with a constructor method; call it. */
vp[1].setObject(*obj);
args.thisv().setObject(*obj);
if (!Invoke(cx, args, JSINVOKE_CONSTRUCT))
return JS_FALSE;
/* Check the return value and if it's primitive, force it to be obj. */
const Value &rval = *vp;
if (rval.isPrimitive()) {
if (args.rval().isPrimitive()) {
if (obj2->getClass() != &js_FunctionClass) {
/* native [[Construct]] returning primitive is error */
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_NEW_RESULT,
js_ValueToPrintableString(cx, *vp));
js_ValueToPrintableString(cx, args.rval()));
return JS_FALSE;
}
vp->setObject(*obj);
args.rval().setObject(*obj);
}
JS_RUNTIME_METER(cx->runtime, constructs);
@@ -4567,7 +4551,7 @@ BEGIN_CASE(JSOP_NEW)
}
}
if (!InvokeConstructor(cx, InvokeArgsGuard(cx, vp, argc)))
if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
goto error;
regs.sp = vp + 1;
CHECK_INTERRUPT_HANDLER();
@@ -4724,7 +4708,7 @@ BEGIN_CASE(JSOP_APPLY)
}
bool ok;
ok = Invoke(cx, InvokeArgsGuard(cx, vp, argc), 0);
ok = Invoke(cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0);
regs.sp = vp + 1;
CHECK_INTERRUPT_HANDLER();
if (!ok)
@@ -4741,7 +4725,7 @@ BEGIN_CASE(JSOP_SETCALL)
{
uintN argc = GET_ARGC(regs.pc);
Value *vp = regs.sp - argc - 2;
JSBool ok = Invoke(cx, InvokeArgsGuard(cx, vp, argc), 0);
JSBool ok = Invoke(cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0);
if (ok)
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
goto error;

View File

@@ -292,6 +292,31 @@ PrimitiveThisTest(JSFunction *fun, const Value &v)
(v.isBoolean() && !!(flags & JSFUN_THISP_BOOLEAN));
}
/*
* Abstracts the layout of the stack passed to natives from the engine and from
* natives to js::Invoke.
*/
struct CallArgs
{
Value *argv_;
uintN argc_;
protected:
CallArgs() {}
CallArgs(Value *argv, uintN argc) : argv_(argv), argc_(argc) {}
public:
Value *base() const { return argv_ - 2; }
Value &callee() const { return argv_[-2]; }
Value &thisv() const { return argv_[-1]; }
Value &operator[](unsigned i) const { JS_ASSERT(i < argc_); return argv_[i]; }
Value *argv() const { return argv_; }
uintN argc() const { return argc_; }
Value &rval() const { return argv_[-2]; }
JSObject *computeThis(JSContext *cx) const {
return ComputeThisFromArgv(cx, argv_);
}
};
/*
* The js::InvokeArgumentsGuard passed to js_Invoke must come from an
* immediately-enclosing successful call to js::StackSpace::pushInvokeArgs,
@@ -301,7 +326,7 @@ PrimitiveThisTest(JSFunction *fun, const Value &v)
* be initialized actual arguments.
*/
extern JS_REQUIRES_STACK bool
Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags);
Invoke(JSContext *cx, const CallArgs &args, uintN flags);
/*
* Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that
@@ -354,7 +379,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
JSStackFrame *down, uintN flags, Value *result);
extern JS_REQUIRES_STACK bool
InvokeConstructor(JSContext *cx, const InvokeArgsGuard &args);
InvokeConstructor(JSContext *cx, const CallArgs &args);
extern JS_REQUIRES_STACK bool
Interpret(JSContext *cx);

View File

@@ -1973,14 +1973,14 @@ InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata,
}
static JS_ALWAYS_INLINE bool
PushRegExpSubstr(JSContext *cx, const JSSubString &sub, Value *&sp)
PushRegExpSubstr(JSContext *cx, const JSSubString &sub, Value *sp)
{
JSString *whole = cx->regExpStatics.input;
size_t off = sub.chars - whole->chars();
JSString *str = js_NewDependentString(cx, whole, off, sub.length);
if (!str)
return false;
sp++->setString(str);
sp->setString(str);
return true;
}
@@ -2028,17 +2028,19 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
PreserveRegExpStatics save(cx);
/* Push lambda and its 'this' parameter. */
Value *sp = rdata.args.getvp();
sp++->setObject(*lambda);
sp++->setObjectOrNull(lambda->getParent());
CallArgs &args = rdata.args;
args.callee().setObject(*lambda);
args.thisv().setObjectOrNull(lambda->getParent());
Value *sp = args.argv();
/* Push $&, $1, $2, ... */
if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp))
if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp++))
return false;
uintN i = 0;
for (uintN n = cx->regExpStatics.parens.length(); i < n; i++) {
if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[i], sp))
if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[i], sp++))
return false;
}
@@ -2058,7 +2060,7 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
* created by this js_ValueToString that would otherwise be GC-
* able, until we use rdata.repstr in DoReplace.
*/
repstr = js_ValueToString(cx, *rdata.args.getvp());
repstr = js_ValueToString(cx, args.rval());
if (!repstr)
return false;