[JAEGER] Merge from tracemonkey.
authorDavid Mandelin <dmandelin@mozilla.com>
Wed, 11 Aug 2010 11:23:29 -0700
changeset 53382 25bff33134218bafd3ca0d2fa38778765e2417be
parent 53381 2cecc5d72edfe977bd795fa8f1fc77617cdda1f3 (current diff)
parent 50468 7acc0fe02f45f647bcf14e4b4a007c26edcb23ad (diff)
child 53383 82c410fffaa85e2469c653a130445ec206d0ee87
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b4pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
[JAEGER] Merge from tracemonkey.
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsfun.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsopcode.cpp
js/src/jsprvtd.h
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/Retcon.h
js/src/xpconnect/src/xpcwrappedjsclass.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4866,23 +4866,22 @@ JS_New(JSContext *cx, JSObject *ctor, ui
     // This is not a simple variation of JS_CallFunctionValue because JSOP_NEW
     // is not a simple variation of JSOP_CALL. We have to determine what class
     // of object to create, create it, and clamp the return value to an object,
     // among other details. js_InvokeConstructor does the hard work.
     InvokeArgsGuard args;
     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;
 }
 
 JS_PUBLIC_API(JSOperationCallback)
 JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback)
 {
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1375,24 +1375,23 @@ array_toString(JSContext *cx, uintN argc
         return true;
     }
 
     LeaveTrace(cx);
     InvokeArgsGuard args;
     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;
 }
 
 static JSBool
 array_toLocaleString(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
@@ -1726,28 +1725,27 @@ sort_compare(void *arg, const void *a, c
      * come here.
      */
     JS_ASSERT(!av->isMagic() && !av->isUndefined());
     JS_ASSERT(!av->isMagic() && !bv->isUndefined());
 
     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. */
     *result = 0;
     if (!JSDOUBLE_IS_NaN(cmp) && cmp != 0)
         *result = cmp > 0 ? 1 : -1;
 
     /*
@@ -2773,17 +2771,16 @@ array_extra(JSContext *cx, ArrayExtraMod
 
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, argc, args))
         return JS_FALSE;
 
     MUST_FLOW_THROUGH("out");
     JSBool ok = JS_TRUE;
     JSBool cond;
-    Value *invokevp = args.getvp();
 
     Value calleev, thisv, objv;
     calleev.setObject(*callable);
     thisv.setObjectOrNull(thisp);
     objv.setObject(*obj);
     AutoValueRooter tvr(cx);
     for (jsint i = start; i != end; i += step) {
         JSBool hole;
@@ -2791,50 +2788,48 @@ array_extra(JSContext *cx, ArrayExtraMod
              GetArrayElement(cx, obj, i, &hole, tvr.addr());
         if (!ok)
             goto out;
         if (hole)
             continue;
 
         /*
          * 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);
         if (!ok)
             break;
 
         if (mode > MAP)
-            cond = js_ValueToBoolean(*invokevp);
+            cond = js_ValueToBoolean(args.rval());
 #ifdef __GNUC__ /* quell GCC overwarning */
         else
             cond = JS_FALSE;
 #endif
 
         switch (mode) {
           case FOREACH:
             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;
           case FILTER:
             if (!cond)
                 break;
             /* The element passed the filter, so push it onto our result. */
             ok = SetArrayElement(cx, newarr, newlen++, tvr.value());
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -99,27 +99,27 @@ using namespace js;
 static const size_t ARENA_HEADER_SIZE_HACK = 40;
 static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
 
 static void
 FreeContext(JSContext *cx);
 
 #ifdef DEBUG
 JS_REQUIRES_STACK bool
-CallStackSegment::contains(const JSStackFrame *fp) const
+StackSegment::contains(const JSStackFrame *fp) const
 {
     JS_ASSERT(inContext());
     JSStackFrame *start;
     JSStackFrame *stop;
-    if (isSuspended()) {
+    if (isActive()) {
+        start = cx->fp;
+        stop = cx->activeSegment()->initialFrame->down;
+    } else {
         start = suspendedFrame;
         stop = initialFrame->down;
-    } else {
-        start = cx->fp;
-        stop = cx->activeSegment()->initialFrame->down;
     }
     for (JSStackFrame *f = start; f != stop; f = f->down) {
         if (f == fp)
             return true;
     }
     return false;
 }
 #endif
@@ -197,325 +197,232 @@ StackSpace::bumpCommit(Value *from, ptrd
 }
 #endif
 
 JS_REQUIRES_STACK void
 StackSpace::mark(JSTracer *trc)
 {
     /*
      * The correctness/completeness of marking depends on the continuity
-     * invariants described by the CallStackSegment and StackSpace definitions.
+     * invariants described by the StackSegment and StackSpace definitions.
      *
      * NB:
      * Stack slots might be torn or uninitialized in the presence of method
      * JIT'd code. Arguments are an exception and are always fully synced
      * (so they can be read by functions).
      */
     Value *end = firstUnused();
-    for (CallStackSegment *css = currentSegment; css; css = css->getPreviousInMemory()) {
-        if (css->inContext()) {
+    for (StackSegment *seg = currentSegment; seg; seg = seg->getPreviousInMemory()) {
+        if (seg->inContext()) {
             /* This may be the only pointer to the initialVarObj. */
-            if (JSObject *varobj = css->getInitialVarObj())
+            if (JSObject *varobj = seg->getInitialVarObj())
                 JS_CALL_OBJECT_TRACER(trc, varobj, "varobj");
 
             /* Mark slots/args trailing off of the last stack frame. */
-            JSStackFrame *fp = css->getCurrentFrame();
+            JSStackFrame *fp = seg->getCurrentFrame();
             MarkStackRangeConservatively(trc, fp->slots(), end);
 
             /* Mark stack frames and slots/args between stack frames. */
-            JSStackFrame *initialFrame = css->getInitialFrame();
+            JSStackFrame *initialFrame = seg->getInitialFrame();
             for (JSStackFrame *f = fp; f != initialFrame; f = f->down) {
                 js_TraceStackFrame(trc, f);
                 MarkStackRangeConservatively(trc, f->down->slots(), f->argEnd());
             }
 
             /* Mark initialFrame stack frame and leading args. */
             js_TraceStackFrame(trc, initialFrame);
-            MarkValueRange(trc, css->getInitialArgBegin(), initialFrame->argEnd(), "stack");
+            MarkValueRange(trc, seg->getInitialArgBegin(), initialFrame->argEnd(), "stack");
         } else {
             /* Mark slots/args trailing off segment. */
-            JS_ASSERT(end == css->getInitialArgEnd());
-            MarkValueRange(trc, css->getInitialArgBegin(), css->getInitialArgEnd(), "stack");
+            MarkValueRange(trc, seg->getInitialArgBegin(), end, "stack");
         }
-        end = css->previousSegmentEnd();
+        end = seg->previousSegmentEnd();
     }
 }
 
 JS_REQUIRES_STACK bool
-StackSpace::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard &ag)
+StackSpace::pushSegmentForInvoke(JSContext *cx, uintN argc, InvokeArgsGuard &ag)
 {
     Value *start = firstUnused();
-    uintN vplen = 2 + argc;
-    ptrdiff_t nvals = VALUES_PER_CALL_STACK + vplen;
+    ptrdiff_t nvals = VALUES_PER_STACK_SEGMENT + 2 + argc;
     if (!ensureSpace(cx, start, nvals))
         return false;
-    Value *vp = start + VALUES_PER_CALL_STACK;
-    Value *vpend = vp + vplen;
-    memset(vp, 0, vplen * sizeof(Value)); /* Init so GC-safe on exit. */
 
-    CallStackSegment *css = new(start) CallStackSegment;
-    css->setInitialArgEnd(vpend);
-    css->setPreviousInThread(currentSegment);
-    currentSegment = css;
+    StackSegment *seg = new(start) StackSegment;
+    seg->setPreviousInMemory(currentSegment);
+    currentSegment = seg;
 
     ag.cx = cx;
-    ag.css = css;
-    ag.argc = argc;
-    ag.vp = vp;
-    return true;
-}
-
-JS_REQUIRES_STACK JS_FRIEND_API(bool)
-StackSpace::pushInvokeArgsFriendAPI(JSContext *cx, uintN argc,
-                                    InvokeArgsGuard &ag)
-{
-    return cx->stack().pushInvokeArgs(cx, argc, ag);
-}
-
-InvokeFrameGuard::InvokeFrameGuard()
-  : cx(NULL), css(NULL), fp(NULL)
-{}
+    ag.seg = seg;
+    ag.argv_ = seg->getInitialArgBegin() + 2;
+    ag.argc_ = argc;
 
-/*
- * To maintain the 1 to 0..1 relationship between CallStackSegments and
- * js_Interpret activations, a callstack is pushed if one was not pushed for
- * the arguments (viz., if the ternary InvokeArgsGuard constructor was used
- * instead of the nullary constructor + pushInvokeArgs).
- */
-bool
-StackSpace::getInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
-                           uintN nmissing, uintN nfixed,
-                           InvokeFrameGuard &fg) const
-{
-    if (ag.css) {
-        JS_ASSERT(ag.css == currentSegment && !ag.css->inContext());
-        Value *start = ag.css->getInitialArgEnd();
-        ptrdiff_t nvals = nmissing + VALUES_PER_STACK_FRAME + nfixed;
-        if (!ensureSpace(cx, start, nvals))
-            return false;
-        fg.fp = reinterpret_cast<JSStackFrame *>(start + nmissing);
-        return true;
-    }
-
-    assertIsCurrent(cx);
-    JS_ASSERT(currentSegment->isActive());
-    Value *start = cx->regs->sp;
-    ptrdiff_t nvals = nmissing + VALUES_PER_CALL_STACK + VALUES_PER_STACK_FRAME + nfixed;
-    if (!ensureSpace(cx, start, nvals))
-        return false;
-    fg.css = new(start + nmissing) CallStackSegment;
-    fg.fp = reinterpret_cast<JSStackFrame *>(fg.css + 1);
+    /* Use invokeArgEnd to root [vp, vpend) until the frame is pushed. */
+#ifdef DEBUG
+    ag.prevInvokeSegment = invokeSegment;
+    invokeSegment = seg;
+    ag.prevInvokeFrame = invokeFrame;
+    invokeFrame = NULL;
+#endif
+    ag.prevInvokeArgEnd = invokeArgEnd;
+    invokeArgEnd = ag.argv() + ag.argc();
     return true;
 }
 
 JS_REQUIRES_STACK void
-StackSpace::pushInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
-                            InvokeFrameGuard &fg, JSFrameRegs &regs)
-{
-    JS_ASSERT(!!ag.css ^ !!fg.css);
-    JS_ASSERT_IF(ag.css, ag.css == currentSegment && !ag.css->inContext());
-    if (CallStackSegment *css = fg.css) {
-        css->setPreviousInThread(currentSegment);
-        currentSegment = css;
-    }
-    JSStackFrame *fp = fg.fp;
-    fp->down = cx->fp;
-    cx->pushSegmentAndFrame(currentSegment, fp, regs);
-    currentSegment->setInitialVarObj(NULL);
-    fg.cx = cx;
-}
-
-JS_REQUIRES_STACK
-InvokeFrameGuard::~InvokeFrameGuard()
+StackSpace::popSegmentForInvoke(const InvokeArgsGuard &ag)
 {
-    if (!cx)
-        return;
-    JS_ASSERT(fp && fp == cx->fp);
-    JS_ASSERT_IF(css, css == cx->stack().getCurrentSegment());
-    cx->stack().popInvokeFrame(cx, css);
-}
+    JS_ASSERT(!currentSegment->inContext());
+    JS_ASSERT(ag.seg == currentSegment);
+    JS_ASSERT(invokeSegment == currentSegment);
+    JS_ASSERT(invokeArgEnd == ag.argv() + ag.argc());
 
-JS_REQUIRES_STACK void
-StackSpace::popInvokeFrame(JSContext *cx, CallStackSegment *maybecs)
-{
-    assertIsCurrent(cx);
-    JS_ASSERT(currentSegment->getInitialFrame() == cx->fp);
-    JS_ASSERT_IF(maybecs, maybecs == currentSegment);
-    cx->popSegmentAndFrame();
-    if (maybecs)
-        currentSegment = currentSegment->getPreviousInMemory();
-}
+    currentSegment = currentSegment->getPreviousInMemory();
 
-ExecuteFrameGuard::ExecuteFrameGuard()
-  : cx(NULL), vp(NULL), fp(NULL)
-{}
-
-JS_REQUIRES_STACK
-ExecuteFrameGuard::~ExecuteFrameGuard()
-{
-    if (!cx)
-        return;
-    JS_ASSERT(cx->activeSegment() == css);
-    JS_ASSERT(cx->fp == fp);
-    cx->stack().popExecuteFrame(cx);
+#ifdef DEBUG
+    invokeSegment = ag.prevInvokeSegment;
+    invokeFrame = ag.prevInvokeFrame;
+#endif
+    invokeArgEnd = ag.prevInvokeArgEnd;
 }
 
 /*
- * To maintain a 1 to 0..1 relationship between CallStackSegments and
- * js_Interpret activations, we push a segment even if it wasn't otherwise
- * necessary.
+ * Always push a segment when starting a new execute frame since segments
+ * provide initialVarObj, which may change.
  */
 JS_REQUIRES_STACK bool
 StackSpace::getExecuteFrame(JSContext *cx, JSStackFrame *down,
                             uintN vplen, uintN nfixed,
                             ExecuteFrameGuard &fg) const
 {
     Value *start = firstUnused();
-    ptrdiff_t nvals = VALUES_PER_CALL_STACK + vplen + VALUES_PER_STACK_FRAME + nfixed;
+    ptrdiff_t nvals = VALUES_PER_STACK_SEGMENT + vplen + VALUES_PER_STACK_FRAME + nfixed;
     if (!ensureSpace(cx, start, nvals))
         return false;
 
-    fg.css = new(start) CallStackSegment;
-    fg.vp = start + VALUES_PER_CALL_STACK;
+    fg.seg = new(start) StackSegment;
+    fg.vp = start + VALUES_PER_STACK_SEGMENT;
     fg.fp = reinterpret_cast<JSStackFrame *>(fg.vp + vplen);
     fg.down = down;
     return true;
 }
 
 JS_REQUIRES_STACK void
 StackSpace::pushExecuteFrame(JSContext *cx, ExecuteFrameGuard &fg,
                              JSFrameRegs &regs, JSObject *initialVarObj)
 {
     fg.fp->down = fg.down;
-    CallStackSegment *css = fg.css;
-    css->setPreviousInThread(currentSegment);
-    currentSegment = css;
-    cx->pushSegmentAndFrame(css, fg.fp, regs);
-    css->setInitialVarObj(initialVarObj);
+    StackSegment *seg = fg.seg;
+    seg->setPreviousInMemory(currentSegment);
+    currentSegment = seg;
+    cx->pushSegmentAndFrame(seg, fg.fp, regs);
+    seg->setInitialVarObj(initialVarObj);
     fg.cx = cx;
 }
 
 JS_REQUIRES_STACK void
 StackSpace::popExecuteFrame(JSContext *cx)
 {
-    assertIsCurrent(cx);
+    JS_ASSERT(isCurrentAndActive(cx));
     JS_ASSERT(cx->hasActiveSegment());
     cx->popSegmentAndFrame();
     currentSegment = currentSegment->getPreviousInMemory();
 }
 
-JS_REQUIRES_STACK void
-StackSpace::getSynthesizedSlowNativeFrame(JSContext *cx, CallStackSegment *&css, JSStackFrame *&fp)
+JS_REQUIRES_STACK
+ExecuteFrameGuard::~ExecuteFrameGuard()
 {
-    Value *start = firstUnused();
-    JS_ASSERT(size_t(end - start) >= VALUES_PER_CALL_STACK + VALUES_PER_STACK_FRAME);
-    css = new(start) CallStackSegment;
-    fp = reinterpret_cast<JSStackFrame *>(css + 1);
+    if (!pushed())
+        return;
+    JS_ASSERT(cx->activeSegment() == seg);
+    JS_ASSERT(cx->fp == fp);
+    cx->stack().popExecuteFrame(cx);
 }
 
 JS_REQUIRES_STACK void
-StackSpace::pushSynthesizedSlowNativeFrame(JSContext *cx, CallStackSegment *css, JSStackFrame *fp,
+StackSpace::getSynthesizedSlowNativeFrame(JSContext *cx, StackSegment *&seg, JSStackFrame *&fp)
+{
+    Value *start = firstUnused();
+    JS_ASSERT(size_t(end - start) >= VALUES_PER_STACK_SEGMENT + VALUES_PER_STACK_FRAME);
+    seg = new(start) StackSegment;
+    fp = reinterpret_cast<JSStackFrame *>(seg + 1);
+}
+
+JS_REQUIRES_STACK void
+StackSpace::pushSynthesizedSlowNativeFrame(JSContext *cx, StackSegment *seg, JSStackFrame *fp,
                                            JSFrameRegs &regs)
 {
     JS_ASSERT(!fp->script && FUN_SLOW_NATIVE(fp->fun));
     fp->down = cx->fp;
-    css->setPreviousInThread(currentSegment);
-    currentSegment = css;
-    cx->pushSegmentAndFrame(css, fp, regs);
-    css->setInitialVarObj(NULL);
+    seg->setPreviousInMemory(currentSegment);
+    currentSegment = seg;
+    cx->pushSegmentAndFrame(seg, fp, regs);
+    seg->setInitialVarObj(NULL);
 }
 
 JS_REQUIRES_STACK void
 StackSpace::popSynthesizedSlowNativeFrame(JSContext *cx)
 {
-    assertIsCurrent(cx);
+    JS_ASSERT(isCurrentAndActive(cx));
     JS_ASSERT(cx->hasActiveSegment());
     JS_ASSERT(currentSegment->getInitialFrame() == cx->fp);
     JS_ASSERT(!cx->fp->script && FUN_SLOW_NATIVE(cx->fp->fun));
     cx->popSegmentAndFrame();
     currentSegment = currentSegment->getPreviousInMemory();
 }
 
+void
+FrameRegsIter::initSlow()
+{
+    if (!curseg) {
+        curfp = NULL;
+        cursp = NULL;
+        curpc = NULL;
+        return;
+    }
+
+    JS_ASSERT(curseg->isSuspended());
+    curfp = curseg->getSuspendedFrame();
+    cursp = curseg->getSuspendedRegs()->sp;
+    curpc = curseg->getSuspendedRegs()->pc;
+}
+
 /*
- * When a pair of down-linked stack frames are in the same segment, the
+ * Using the invariant described in the js::StackSegment comment, we know that,
+ * when a pair of down-linked stack frames are in the same segment, the
  * up-frame's address is the top of the down-frame's stack, modulo missing
  * arguments.
  */
-static inline Value *
-InlineDownFrameSP(JSStackFrame *up)
-{
-    JS_ASSERT(up->fun && up->script);
-    Value *sp = up->argv + up->argc;
-#ifdef DEBUG
-    uint16 nargs = up->fun->nargs;
-    uintN argc = up->argc;
-    uintN missing = argc < nargs ? nargs - argc : 0;
-    JS_ASSERT(sp == (Value *)up - missing);
-#endif
-    return sp;
-}
-
-JS_REQUIRES_STACK
-FrameRegsIter::FrameRegsIter(JSContext *cx)
+void
+FrameRegsIter::incSlow(JSStackFrame *up, JSStackFrame *down)
 {
-    curcs = cx->getCurrentSegment();
-    if (!curcs) {
-        curfp = NULL;
-        return;
-    }
-    if (curcs->isSuspended()) {
-        curfp = curcs->getSuspendedFrame();
-        cursp = curcs->getSuspendedRegs()->sp;
-        curpc = curcs->getSuspendedRegs()->pc;
-        return;
-    }
-    JS_ASSERT(cx->fp);
-    curfp = cx->fp;
-    cursp = cx->regs->sp;
-    curpc = cx->regs->pc;
-    return;
-}
-
-FrameRegsIter &
-FrameRegsIter::operator++()
-{
-    JSStackFrame *up = curfp;
-    JSStackFrame *down = curfp = curfp->down;
-    if (!down)
-        return *this;
-
-    curpc = down->savedPC;
-
-    /* For a contiguous down and up, compute sp from up. */
-    if (up != curcs->getInitialFrame()) {
-        cursp = InlineDownFrameSP(up);
-        return *this;
-    }
+    JS_ASSERT(down);
+    JS_ASSERT(curpc == down->savedPC);
+    JS_ASSERT(up == curseg->getInitialFrame());
 
     /*
      * If the up-frame is in csup and the down-frame is in csdown, it is not
      * necessarily the case that |csup->getPreviousInContext == csdown| or that
      * |csdown->getSuspendedFrame == down| (because of indirect eval and
      * JS_EvaluateInStackFrame). To compute down's sp, we need to do a linear
      * scan, keeping track of what is immediately after down in memory.
      */
-    curcs = curcs->getPreviousInContext();
-    cursp = curcs->getSuspendedRegs()->sp;
-    JSStackFrame *f = curcs->getSuspendedFrame();
+    curseg = curseg->getPreviousInContext();
+    cursp = curseg->getSuspendedRegs()->sp;
+    JSStackFrame *f = curseg->getSuspendedFrame();
     while (f != down) {
-        if (f == curcs->getInitialFrame()) {
-            curcs = curcs->getPreviousInContext();
-            cursp = curcs->getSuspendedRegs()->sp;
-            f = curcs->getSuspendedFrame();
+        if (f == curseg->getInitialFrame()) {
+            curseg = curseg->getPreviousInContext();
+            cursp = curseg->getSuspendedRegs()->sp;
+            f = curseg->getSuspendedFrame();
         } else {
-            cursp = InlineDownFrameSP(f);
+            cursp = contiguousDownFrameSP(f);
             f = f->down;
         }
     }
-    return *this;
 }
 
 bool
 JSThreadData::init()
 {
 #ifdef DEBUG
     /* The data must be already zeroed. */
     for (size_t i = 0; i != sizeof(*this); ++i)
@@ -2069,32 +1976,32 @@ JSContext::JSContext(JSRuntime *rt)
     compartment(rt->defaultCompartment),
     fp(NULL),
     regs(NULL),
     regExpStatics(this),
     busyArrays(this)
 {}
 
 void
-JSContext::pushSegmentAndFrame(js::CallStackSegment *newcs, JSStackFrame *newfp,
+JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSStackFrame *newfp,
                                JSFrameRegs &newregs)
 {
     if (hasActiveSegment()) {
         JS_ASSERT(fp->savedPC == JSStackFrame::sInvalidPC);
         fp->savedPC = regs->pc;
         currentSegment->suspend(fp, regs);
     }
-    newcs->setPreviousInContext(currentSegment);
-    currentSegment = newcs;
+    newseg->setPreviousInContext(currentSegment);
+    currentSegment = newseg;
 #ifdef DEBUG
     newfp->savedPC = JSStackFrame::sInvalidPC;
 #endif
     setCurrentFrame(newfp);
     setCurrentRegs(&newregs);
-    newcs->joinContext(this, newfp);
+    newseg->joinContext(this, newfp);
 }
 
 void
 JSContext::popSegmentAndFrame()
 {
     JS_ASSERT(currentSegment->maybeContext() == this);
     JS_ASSERT(currentSegment->getInitialFrame() == fp);
     JS_ASSERT(fp->savedPC == JSStackFrame::sInvalidPC);
@@ -2128,17 +2035,17 @@ JSContext::saveActiveSegment()
     fp->savedPC = regs->pc;
     setCurrentFrame(NULL);
     setCurrentRegs(NULL);
 }
 
 void
 JSContext::restoreSegment()
 {
-    js::CallStackSegment *ccs = currentSegment;
+    js::StackSegment *ccs = currentSegment;
     setCurrentFrame(ccs->getSuspendedFrame());
     setCurrentRegs(ccs->getSuspendedRegs());
     ccs->restore();
 #ifdef DEBUG
     fp->savedPC = JSStackFrame::sInvalidPC;
 #endif
 }
 
@@ -2156,43 +2063,43 @@ JSContext::generatorFor(JSStackFrame *fp
     for (size_t i = 0; i < genStack.length(); ++i) {
         if (genStack[i]->liveFrame == fp)
             return genStack[i];
     }
     JS_NOT_REACHED("no matching generator");
     return NULL;
 }
 
-CallStackSegment *
+StackSegment *
 JSContext::containingSegment(const JSStackFrame *target)
 {
     /* The context may have nothing running. */
-    CallStackSegment *css = currentSegment;
-    if (!css)
+    StackSegment *seg = currentSegment;
+    if (!seg)
         return NULL;
 
     /* The active segments's top frame is cx->fp. */
     if (fp) {
-        JS_ASSERT(activeSegment() == css);
+        JS_ASSERT(activeSegment() == seg);
         JSStackFrame *f = fp;
-        JSStackFrame *stop = css->getInitialFrame()->down;
+        JSStackFrame *stop = seg->getInitialFrame()->down;
         for (; f != stop; f = f->down) {
             if (f == target)
-                return css;
+                return seg;
         }
-        css = css->getPreviousInContext();
+        seg = seg->getPreviousInContext();
     }
 
     /* A suspended segment's top frame is its suspended frame. */
-    for (; css; css = css->getPreviousInContext()) {
-        JSStackFrame *f = css->getSuspendedFrame();
-        JSStackFrame *stop = css->getInitialFrame()->down;
+    for (; seg; seg = seg->getPreviousInContext()) {
+        JSStackFrame *f = seg->getSuspendedFrame();
+        JSStackFrame *stop = seg->getInitialFrame()->down;
         for (; f != stop; f = f->down) {
             if (f == target)
-                return css;
+                return seg;
         }
     }
 
     return NULL;
 }
 
 void
 JSContext::checkMallocGCPressure(void *p)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -155,16 +155,20 @@ class TypeMap;
 struct REFragment;
 typedef nanojit::HashMap<REHashKey, REFragment*, REHashFn> REHashMap;
 
 #if defined(JS_JIT_SPEW) || defined(DEBUG)
 struct FragPI;
 typedef nanojit::HashMap<uint32, FragPI, nanojit::DefaultHash<uint32> > FragStatsMap;
 #endif
 
+namespace mjit {
+class CallStackIterator;
+}
+
 /*
  * Allocation policy that calls JSContext memory functions and reports errors
  * to the context. Since the JSContext given on construction is stored for
  * the lifetime of the container, this policy may only be used for containers
  * whose lifetime is a shorter than the given JSContext.
  */
 class ContextAllocPolicy
 {
@@ -275,347 +279,362 @@ struct TraceNativeStorage
 /* Holds data to track a single globa. */
 struct GlobalState {
     JSObject*               globalObj;
     uint32                  globalShape;
     SlotList*               globalSlots;
 };
 
 /*
- * A CallStackSegment (henceforth referred to as a 'segment') logically
- * contains the (possibly empty) set of stack frames associated with a single
- * activation of the VM and the slots associated with each frame. A segment may
- * or may not be "in" a context and a segment is in a context iff its set of
- * stack frames is nonempty. A segment and its contained frames/slots also have
- * an implied memory layout, as described in the js::StackSpace comment.
+ * A StackSegment (referred to as just a 'segment') contains a down-linked set
+ * of stack frames and the slots associated with each frame. A segment and its
+ * contained frames/slots also have a precise memory layout that is described
+ * in the js::StackSpace comment. A key layout invariant for segments is that
+ * down-linked frames are adjacent in memory, separated only by the values that
+ * constitute the locals and expression stack of the down-frame and arguments
+ * of the up-frame.
  *
  * The set of stack frames in a non-empty segment start at the segment's
  * "current frame", which is the most recently pushed frame, and ends at the
  * segment's "initial frame". Note that, while all stack frames in a segment
  * are down-linked, not all down-linked frames are in the same segment. Hence,
- * for a segment |css|, |css->getInitialFrame()->down| may be non-null and in a
- * different segment. This occurs when the VM reenters itself (via js_Invoke or
- * js_Execute). In full generality, a single context may contain a forest of
- * trees of stack frames. With respect to this forest, a segment contains a
- * linear path along a single tree, not necessarily to the root.
+ * for a segment |ss|, |ss->getInitialFrame()->down| may be non-null and in a
+ * different segment. This occurs when the VM reenters itself (via Invoke or
+ * Execute). In full generality, a single context may contain a forest of trees
+ * of stack frames. With respect to this forest, a segment contains a linear
+ * path along a single tree, not necessarily to the root.
  *
- * A segment in a context may additionally be "active" or "suspended". A
- * suspended segment |css| has a "suspended frame" which serves as the current
- * frame of |css|. Additionally, a suspended segment has "suspended regs",
- * which is a snapshot of |cx->regs| when |css| was suspended. There is at most
- * one active segment in a given context. Segments in a context execute LIFO
- * and are maintained in a stack. The top of this stack is the context's
- * "current segment". If a context |cx| has an active segment |css|, then:
- *   1. |css| is |cx|'s current segment,
+ * The frames of a non-empty segment must all be in the same context and thus
+ * each non-empty segment is referred to as being "in" a context. Segments in a
+ * context have an additional state of being either "active" or "suspended". A
+ * suspended segment |ss| has a "suspended frame" which is snapshot of |cx->fp|
+ * when the segment was suspended and serves as the current frame of |ss|.
+ * There is at most one active segment in a given context. Segments in a
+ * context execute LIFO and are maintained in a stack.  The top of this stack
+ * is the context's "current segment". If a context |cx| has an active segment
+ * |ss|, then:
+ *   1. |ss| is |cx|'s current segment,
  *   2. |cx->fp != NULL|, and
- *   3. |css|'s current frame is |cx->fp|.
+ *   3. |ss|'s current frame is |cx->fp|.
  * Moreover, |cx->fp != NULL| iff |cx| has an active segment.
  *
+ * An empty segment is not associated with any context. Empty segments are
+ * created when there is not an active segment for a context at the top of the
+ * stack and claim space for the arguments of an Invoke before the Invoke's
+ * stack frame is pushed. During the intervals when the arguments have been
+ * pushed, but not the stack frame, the segment cannot be pushed onto the
+ * context, since that would require some hack to deal with cx->fp not being
+ * the current frame of cx->currentSegment.
+ *
  * Finally, (to support JS_SaveFrameChain/JS_RestoreFrameChain) a suspended
  * segment may or may not be "saved". Normally, when the active segment is
  * popped, the previous segment (which is necessarily suspended) becomes
  * active. If the previous segment was saved, however, then it stays suspended
  * until it is made active by a call to JS_RestoreFrameChain. This is why a
  * context may have a current segment, but not an active segment.
  */
-class CallStackSegment
+class StackSegment
 {
     /* The context to which this segment belongs. */
     JSContext           *cx;
 
     /* Link for JSContext segment stack mentioned in big comment above. */
-    CallStackSegment    *previousInContext;
+    StackSegment        *previousInContext;
 
     /* Link for StackSpace segment stack mentioned in StackSpace comment. */
-    CallStackSegment    *previousInMemory;
+    StackSegment        *previousInMemory;
 
     /* The first frame executed in this segment. null iff cx is null */
     JSStackFrame        *initialFrame;
 
     /* If this segment is suspended, the top of the segment. */
     JSStackFrame        *suspendedFrame;
 
-    /*
-     * To achieve a sizeof(CallStackSegment) that is a multiple of
-     * sizeof(Value), we compress two fields into one word:
-     *
-     *  suspendedRegs: If this segment is suspended, |cx->regs| when it was
-     *  suspended.
-     *
-     *  saved: Whether this segment was suspended by JS_SaveFrameChain.
-     */
-    AlignedPtrAndFlag<JSFrameRegs> suspendedRegsAndSaved;
-
-    /* End of arguments before the first frame. See StackSpace comment. */
-    Value               *initialArgEnd;
+    /* If this segment is suspended, |cx->regs| when it was suspended. */
+    JSFrameRegs         *suspendedRegs;
 
     /* The varobj on entry to initialFrame. */
     JSObject            *initialVarObj;
 
+    /* Whether this segment was suspended by JS_SaveFrameChain. */
+    bool                saved;
+
+    /*
+     * To make isActive a single null-ness check, this non-null constant is
+     * assigned to suspendedFrame when !inContext.
+     */
+#define NON_NULL_SUSPENDED_FRAME ((JSStackFrame *)0x1)
+
   public:
-    CallStackSegment()
+    StackSegment()
       : cx(NULL), previousInContext(NULL), previousInMemory(NULL),
-        initialFrame(NULL), suspendedFrame(NULL),
-        suspendedRegsAndSaved(NULL, false), initialArgEnd(NULL),
-        initialVarObj(NULL) { }
+        initialFrame(NULL), suspendedFrame(NON_NULL_SUSPENDED_FRAME),
+        suspendedRegs(NULL), initialVarObj(NULL), saved(false)
+    {
+        JS_ASSERT(!inContext());
+    }
 
     /* Safe casts guaranteed by the contiguous-stack layout. */
 
     Value *previousSegmentEnd() const {
         return (Value *)this;
     }
 
     Value *getInitialArgBegin() const {
         return (Value *)(this + 1);
     }
 
     /*
      * As described in the comment at the beginning of the class, a segment
      * is in one of three states:
      *
      *  !inContext:  the segment has been created to root arguments for a
-     *               future call to js_Invoke.
+     *               future call to Invoke.
      *  isActive:    the segment describes a set of stack frames in a context,
      *               where the top frame currently executing.
      *  isSuspended: like isActive, but the top frame has been suspended.
      */
 
     bool inContext() const {
         JS_ASSERT(!!cx == !!initialFrame);
-        JS_ASSERT_IF(!initialFrame, !suspendedFrame && !suspendedRegsAndSaved.flag());
-        return !!cx;
+        JS_ASSERT_IF(!cx, suspendedFrame == NON_NULL_SUSPENDED_FRAME && !saved);
+        return cx;
     }
 
     bool isActive() const {
-        JS_ASSERT_IF(suspendedFrame, inContext());
-        return initialFrame && !suspendedFrame;
+        JS_ASSERT_IF(!suspendedFrame, cx && !saved);
+        JS_ASSERT_IF(!cx, suspendedFrame == NON_NULL_SUSPENDED_FRAME);
+        return !suspendedFrame;
     }
 
     bool isSuspended() const {
-        JS_ASSERT_IF(!suspendedFrame, !suspendedRegsAndSaved.flag());
-        JS_ASSERT_IF(suspendedFrame, inContext());
-        return !!suspendedFrame;
+        JS_ASSERT_IF(!cx || !suspendedFrame, !saved);
+        JS_ASSERT_IF(!cx, suspendedFrame == NON_NULL_SUSPENDED_FRAME);
+        return cx && suspendedFrame;
     }
 
     /* Substate of suspended, queryable in any state. */
+
     bool isSaved() const {
-        JS_ASSERT_IF(suspendedRegsAndSaved.flag(), isSuspended());
-        return suspendedRegsAndSaved.flag();
+        JS_ASSERT_IF(saved, isSuspended());
+        return saved;
     }
 
     /* Transitioning between inContext <--> isActive */
 
     void joinContext(JSContext *cx, JSStackFrame *f) {
         JS_ASSERT(!inContext());
         this->cx = cx;
         initialFrame = f;
+        suspendedFrame = NULL;
         JS_ASSERT(isActive());
     }
 
     void leaveContext() {
         JS_ASSERT(isActive());
         this->cx = NULL;
         initialFrame = NULL;
+        suspendedFrame = NON_NULL_SUSPENDED_FRAME;
         JS_ASSERT(!inContext());
     }
 
     JSContext *maybeContext() const {
         return cx;
     }
 
+#undef NON_NULL_SUSPENDED_FRAME
+
     /* Transitioning between isActive <--> isSuspended */
 
     void suspend(JSStackFrame *fp, JSFrameRegs *regs) {
         JS_ASSERT(isActive());
         JS_ASSERT(fp && contains(fp));
         suspendedFrame = fp;
         JS_ASSERT(isSuspended());
-        suspendedRegsAndSaved.setPtr(regs);
+        suspendedRegs = regs;
     }
 
     void resume() {
         JS_ASSERT(isSuspended());
         suspendedFrame = NULL;
         JS_ASSERT(isActive());
     }
 
     /* When isSuspended, transitioning isSaved <--> !isSaved */
 
     void save(JSStackFrame *fp, JSFrameRegs *regs) {
-        JS_ASSERT(!isSaved());
+        JS_ASSERT(!isSuspended());
         suspend(fp, regs);
-        suspendedRegsAndSaved.setFlag();
+        saved = true;
         JS_ASSERT(isSaved());
     }
 
     void restore() {
         JS_ASSERT(isSaved());
-        suspendedRegsAndSaved.unsetFlag();
+        saved = false;
         resume();
-        JS_ASSERT(!isSaved());
-    }
-
-    /* Data available when !inContext */
-
-    void setInitialArgEnd(Value *v) {
-        JS_ASSERT(!inContext() && !initialArgEnd);
-        initialArgEnd = v;
-    }
-
-    Value *getInitialArgEnd() const {
-        JS_ASSERT(!inContext() && initialArgEnd);
-        return initialArgEnd;
+        JS_ASSERT(!isSuspended());
     }
 
     /* Data available when inContext */
 
     JSStackFrame *getInitialFrame() const {
         JS_ASSERT(inContext());
         return initialFrame;
     }
 
     inline JSStackFrame *getCurrentFrame() const;
+    inline JSFrameRegs *getCurrentRegs() const;
 
     /* Data available when isSuspended. */
 
     JSStackFrame *getSuspendedFrame() const {
         JS_ASSERT(isSuspended());
         return suspendedFrame;
     }
 
     JSFrameRegs *getSuspendedRegs() const {
         JS_ASSERT(isSuspended());
-        return suspendedRegsAndSaved.ptr();
+        return suspendedRegs;
     }
 
     /* JSContext / js::StackSpace bookkeeping. */
 
-    void setPreviousInContext(CallStackSegment *css) {
-        previousInContext = css;
+    void setPreviousInContext(StackSegment *seg) {
+        previousInContext = seg;
     }
 
-    CallStackSegment *getPreviousInContext() const  {
+    StackSegment *getPreviousInContext() const  {
         return previousInContext;
     }
 
-    void setPreviousInThread(CallStackSegment *css) {
-        previousInMemory = css;
+    void setPreviousInMemory(StackSegment *seg) {
+        previousInMemory = seg;
     }
 
-    CallStackSegment *getPreviousInMemory() const  {
+    StackSegment *getPreviousInMemory() const  {
         return previousInMemory;
     }
 
     void setInitialVarObj(JSObject *obj) {
         JS_ASSERT(inContext());
         initialVarObj = obj;
     }
 
     JSObject *getInitialVarObj() const {
         JS_ASSERT(inContext());
         return initialVarObj;
     }
 
 #ifdef DEBUG
     JS_REQUIRES_STACK bool contains(const JSStackFrame *fp) const;
 #endif
-
 };
 
-static const size_t VALUES_PER_CALL_STACK = sizeof(CallStackSegment) / sizeof(Value);
-JS_STATIC_ASSERT(sizeof(CallStackSegment) % sizeof(Value) == 0);
+static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
+JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
+
+/* See StackSpace::pushInvokeArgs. */
+class InvokeArgsGuard : public CallArgs
+{
+    friend class StackSpace;
+    JSContext        *cx;  /* null implies nothing pushed */
+    StackSegment     *seg;
+    Value            *prevInvokeArgEnd;
+#ifdef DEBUG
+    StackSegment     *prevInvokeSegment;
+    JSStackFrame     *prevInvokeFrame;
+#endif
+  public:
+    inline InvokeArgsGuard() : cx(NULL), seg(NULL) {}
+    inline InvokeArgsGuard(JSContext *cx, Value *vp, uintN argc);
+    inline ~InvokeArgsGuard();
+    bool pushed() const { return cx != NULL; }
+};
 
 /*
- * 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. 
+ * This type can be used to call Invoke when the arguments have already been
+ * pushed onto the stack as part of normal execution.
  */
-class InvokeArgsGuard
+struct InvokeArgsAlreadyOnTheStack : CallArgs
 {
-    friend class StackSpace;
-    JSContext        *cx;
-    CallStackSegment *css;  /* null implies nothing pushed */
-    Value            *vp;
-    uintN            argc;
-  public:
-    inline InvokeArgsGuard();
-    inline InvokeArgsGuard(Value *vp, uintN argc);
-    inline ~InvokeArgsGuard();
-    Value *getvp() const { return vp; }
-    uintN getArgc() const { JS_ASSERT(vp != NULL); return argc; }
+    InvokeArgsAlreadyOnTheStack(Value *vp, uintN argc) : CallArgs(vp + 2, argc) {}
 };
 
 /* See StackSpace::pushInvokeFrame. */
 class InvokeFrameGuard
 {
     friend class StackSpace;
     JSContext        *cx;  /* null implies nothing pushed */
-    CallStackSegment *css;
     JSStackFrame     *fp;
+    JSFrameRegs      regs;
+    JSFrameRegs      *prevRegs;
   public:
-    InvokeFrameGuard();
+    InvokeFrameGuard() : cx(NULL), fp(NULL) {}
     JS_REQUIRES_STACK ~InvokeFrameGuard();
-    JSStackFrame *getFrame() const { return fp; }
+    bool pushed() const { return cx != NULL; }
+    JSStackFrame *getFrame() { return fp; }
+    JSFrameRegs &getRegs() { return regs; }
 };
 
 /* See StackSpace::pushExecuteFrame. */
 class ExecuteFrameGuard
 {
     friend class StackSpace;
     JSContext        *cx;  /* null implies nothing pushed */
-    CallStackSegment *css;
+    StackSegment     *seg;
     Value            *vp;
     JSStackFrame     *fp;
     JSStackFrame     *down;
   public:
-    ExecuteFrameGuard();
+    ExecuteFrameGuard() : cx(NULL), vp(NULL), fp(NULL) {}
     JS_REQUIRES_STACK ~ExecuteFrameGuard();
+    bool pushed() const { return cx != NULL; }
     Value *getvp() const { return vp; }
     JSStackFrame *getFrame() const { return fp; }
 };
 
 /*
- * Thread stack layout
+ * Stack layout
  *
  * Each JSThreadData has one associated StackSpace object which allocates all
  * segments for the thread. StackSpace performs all such allocations in a
  * single, fixed-size buffer using a specific layout scheme that allows some
  * associations between segments, frames, and slots to be implicit, rather
  * than explicitly stored as pointers. To maintain useful invariants, stack
  * space is not given out arbitrarily, but rather allocated/deallocated for
  * specific purposes. The use cases currently supported are: calling a function
- * with arguments (e.g. js_Invoke), executing a script (e.g. js_Execute) and
- * inline interpreter calls. See associated member functions below.
+ * with arguments (e.g. Invoke), executing a script (e.g. Execute) and inline
+ * interpreter calls. See associated member functions below.
  *
  * First, we consider the layout of individual segments. (See the
- * js::CallStackSegment comment for terminology.) A non-empty segment (i.e., a
+ * js::StackSegment comment for terminology.) A non-empty segment (i.e., a
  * segment in a context) has the following layout:
  *
- *           initial frame                 current frame -------.  if regs,
- *          .------------.                           |          |  regs->sp
- *          |            V                           V          V
+ *           initial frame                 current frame ------.  if regs,
+ *          .------------.                           |         |  regs->sp
+ *          |            V                           V         V
  *   |segment| slots |frame| slots |frame| slots |frame| slots |
  *                       |  ^          |  ^          |
  *          ? <----------'  `----------'  `----------'
  *                down          down          down
  *
  * Moreover, the bytes in the following ranges form a contiguous array of
  * Values that are marked during GC:
  *   1. between a segment and its first frame
  *   2. between two adjacent frames in a segment
  *   3. between a segment's current frame and (if fp->regs) fp->regs->sp
  * Thus, the VM must ensure that all such Values are safe to be marked.
  *
- * An empty segment roots the initial slots before the initial frame is
- * pushed and after the initial frame has been popped (perhaps to be followed
- * by subsequent initial frame pushes/pops...).
+ * An empty segment is followed by arguments that are rooted by the
+ * StackSpace::invokeArgEnd pointer:
  *
- *         initialArgEnd
- *          .---------.
- *          |         V
+ *              invokeArgEnd
+ *                   |
+ *                   V
  *   |segment| slots |
  *
  * Above the level of segments, a StackSpace is simply a contiguous sequence
  * of segments kept in a linked list:
  *
  *   base                       currentSegment  firstUnused            end
  *    |                               |             |                   |
  *    V                               V             V                   V
@@ -625,49 +644,68 @@ class ExecuteFrameGuard
  *   previous    previous       previous
  *
  * Both js::StackSpace and JSContext maintain a stack of segments, the top of
  * which is the "current segment" for that thread or context, respectively.
  * Since different contexts can arbitrarily interleave execution in a single
  * thread, these stacks are different enough that a segment needs both
  * "previousInMemory" and "previousInContext".
  *
- * For example, in a single thread, a function in segment C1 in a context CX1
+ * For example, in a single thread, a function in segment S1 in a context CX1
  * may call out into C++ code that reenters the VM in a context CX2, which
- * creates a new segment C2 in CX2, and CX1 may or may not equal CX2.
+ * creates a new segment S2 in CX2, and CX1 may or may not equal CX2.
  *
  * Note that there is some structure to this interleaving of segments:
  *   1. the inclusion from segments in a context to segments in a thread
  *      preserves order (in terms of previousInContext and previousInMemory,
  *      respectively).
  *   2. the mapping from stack frames to their containing segment preserves
  *      order (in terms of down and previousInContext, respectively).
  */
 class StackSpace
 {
     Value *base;
 #ifdef XP_WIN
     mutable Value *commitEnd;
 #endif
     Value *end;
-    CallStackSegment *currentSegment;
+    StackSegment *currentSegment;
+#ifdef DEBUG
+    /*
+     * Keep track of which segment/frame bumped invokeArgEnd so that
+     * firstUnused() can assert that, when invokeArgEnd is used as the top of
+     * the stack, it is being used appropriately.
+     */
+    StackSegment *invokeSegment;
+    JSStackFrame *invokeFrame;
+#endif
+    Value        *invokeArgEnd;
+
+    JS_REQUIRES_STACK bool pushSegmentForInvoke(JSContext *cx, uintN argc,
+                                                InvokeArgsGuard &ag);
+    JS_REQUIRES_STACK bool pushInvokeFrameSlow(JSContext *cx, const InvokeArgsGuard &ag,
+                                               InvokeFrameGuard &fg);
+    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 popInvokeArgs(JSContext *cx, Value *vp);
+    JS_REQUIRES_STACK inline void popInvokeArgs(const InvokeArgsGuard &args);
     friend class InvokeFrameGuard;
-    JS_REQUIRES_STACK void popInvokeFrame(JSContext *cx, CallStackSegment *maybecs);
+    JS_REQUIRES_STACK void popInvokeFrame(const InvokeFrameGuard &ag);
     friend class ExecuteFrameGuard;
     JS_REQUIRES_STACK void popExecuteFrame(JSContext *cx);
 
     /* Return a pointer to the first unused slot. */
     JS_REQUIRES_STACK
     inline Value *firstUnused() const;
 
-    inline void assertIsCurrent(JSContext *cx) const;
+    inline bool isCurrentAndActive(JSContext *cx) const;
+    friend class mjit::CallStackIterator;
+    StackSegment *getCurrentSegment() const { return currentSegment; }
 
     /*
      * Allocate nvals on the top of the stack, report error on failure.
      * N.B. the caller must ensure |from == firstUnused()|.
      */
     inline bool ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
 
 #ifdef XP_WIN
@@ -700,17 +738,17 @@ class StackSpace
      * so this function checks that they cannot occur using the size of the
      * TraceNativeStorage as a conservative upper bound.
      */
     inline bool ensureEnoughSpaceToEnterTrace();
 
     /* +1 for slow native's stack frame. */
     static const ptrdiff_t MAX_TRACE_SPACE_VALS =
       MAX_NATIVE_STACK_SLOTS + MAX_CALL_STACK_ENTRIES * VALUES_PER_STACK_FRAME +
-      (VALUES_PER_CALL_STACK + VALUES_PER_STACK_FRAME /* synthesized slow native */);
+      (VALUES_PER_STACK_SEGMENT + VALUES_PER_STACK_FRAME /* synthesized slow native */);
 
     /* Mark all segments, frames, and slots on the stack. */
     JS_REQUIRES_STACK void mark(JSTracer *trc);
 
     /*
      * For all three use cases below:
      *  - The boolean-valued functions call js_ReportOutOfScriptQuota on OOM.
      *  - The "get*Frame" functions do not change any global state, they just
@@ -723,31 +761,30 @@ class StackSpace
      *    to pop the allocation. The caller must ensure the guard has the
      *    appropriate lifetime.
      *  - The get*Frame functions put the 'nmissing' slots contiguously after
      *    the arguments.
      */
 
     /*
      * pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
-     * the arguments to js_Invoke. A single allocation can be used for multiple
-     * js_Invoke calls. The InvokeArgumentsGuard passed to js_Invoke must come
-     * from an immediately-enclosing (stack-wise) call to pushInvokeArgs.
+     * the arguments to Invoke. A single allocation can be used for multiple
+     * Invoke calls. The InvokeArgumentsGuard passed to Invoke must come from
+     * an immediately-enclosing (stack-wise) call to pushInvokeArgs.
      */
     JS_REQUIRES_STACK
     bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard &ag);
 
-    /* These functions are called inside js_Invoke, not js_Invoke clients. */
-    bool getInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
+    /* These functions are called inside Invoke, not Invoke clients. */
+    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, JSFrameRegs &regs);
+    void pushInvokeFrame(JSContext *cx, const CallArgs &args, InvokeFrameGuard &fg);
 
     /*
      * For the simpler case when arguments are allocated at the same time as
      * the frame and it is not necessary to have rooted argument values before
      * pushing the frame.
      */
     JS_REQUIRES_STACK
     bool getExecuteFrame(JSContext *cx, JSStackFrame *down,
@@ -772,56 +809,54 @@ class StackSpace
     JS_REQUIRES_STACK
     inline void popInlineFrame(JSContext *cx, JSStackFrame *up, JSStackFrame *down);
 
     /*
      * For the special case of the slow native stack frame pushed and popped by
      * tracing deep bail logic.
      */
     JS_REQUIRES_STACK
-    void getSynthesizedSlowNativeFrame(JSContext *cx, CallStackSegment *&css, JSStackFrame *&fp);
+    void getSynthesizedSlowNativeFrame(JSContext *cx, StackSegment *&seg, JSStackFrame *&fp);
 
     JS_REQUIRES_STACK
-    void pushSynthesizedSlowNativeFrame(JSContext *cx, CallStackSegment *css, JSStackFrame *fp,
+    void pushSynthesizedSlowNativeFrame(JSContext *cx, StackSegment *seg, JSStackFrame *fp,
                                         JSFrameRegs &regs);
 
     JS_REQUIRES_STACK
     void popSynthesizedSlowNativeFrame(JSContext *cx);
-
-    /* Our privates leak into xpconnect, which needs a public symbol. */
-    JS_REQUIRES_STACK
-    JS_FRIEND_API(bool) pushInvokeArgsFriendAPI(JSContext *, uintN, InvokeArgsGuard &);
-
-    CallStackSegment *getCurrentSegment() const { return currentSegment; }
 };
 
 JS_STATIC_ASSERT(StackSpace::CAPACITY_VALS % StackSpace::COMMIT_VALS == 0);
 
 /*
  * While |cx->fp|'s pc/sp are available in |cx->regs|, to compute the saved
  * value of pc/sp for any other frame, it is necessary to know about that
  * frame's up-frame. This iterator maintains this information when walking down
  * a chain of stack frames starting at |cx->fp|.
  *
  * Usage:
  *   for (FrameRegsIter i(cx); !i.done(); ++i)
  *     ... i.fp() ... i.sp() ... i.pc()
  */
 class FrameRegsIter
 {
-    CallStackSegment  *curcs;
+    StackSegment      *curseg;
     JSStackFrame      *curfp;
     Value             *cursp;
     jsbytecode        *curpc;
 
+    void initSlow();
+    void incSlow(JSStackFrame *up, JSStackFrame *down);
+    static inline Value *contiguousDownFrameSP(JSStackFrame *up);
+
   public:
-    JS_REQUIRES_STACK FrameRegsIter(JSContext *cx);
+    JS_REQUIRES_STACK inline FrameRegsIter(JSContext *cx);
 
     bool done() const { return curfp == NULL; }
-    FrameRegsIter &operator++();
+    inline FrameRegsIter &operator++();
 
     JSStackFrame *fp() const { return curfp; }
     Value *sp() const { return cursp; }
     jsbytecode *pc() const { return curpc; }
 };
 
 /* Holds the number of recording attemps for an address. */
 typedef HashMap<jsbytecode*,
@@ -1860,68 +1895,68 @@ struct JSContext
     /* Interpreter activation count. */
     uintN               interpLevel;
 
     /* Client opaque pointers. */
     void                *data;
     void                *data2;
 
   private:
-    /* Linked list of segments. See CallStackSegment. */
-    js::CallStackSegment *currentSegment;
+    /* Linked list of segments. See StackSegment. */
+    js::StackSegment *currentSegment;
 
   public:
     void assertSegmentsInSync() const {
 #ifdef DEBUG
         if (fp) {
             JS_ASSERT(currentSegment->isActive());
-            if (js::CallStackSegment *prev = currentSegment->getPreviousInContext())
+            if (js::StackSegment *prev = currentSegment->getPreviousInContext())
                 JS_ASSERT(!prev->isActive());
         } else {
             JS_ASSERT_IF(currentSegment, !currentSegment->isActive());
         }
 #endif
     }
 
     /* Return whether this context has an active segment. */
     bool hasActiveSegment() const {
         assertSegmentsInSync();
         return !!fp;
     }
 
     /* Assuming there is an active segment, return it. */
-    js::CallStackSegment *activeSegment() const {
+    js::StackSegment *activeSegment() const {
         JS_ASSERT(hasActiveSegment());
         return currentSegment;
     }
 
     /* Return the current segment, which may or may not be active. */
-    js::CallStackSegment *getCurrentSegment() const {
+    js::StackSegment *getCurrentSegment() const {
         assertSegmentsInSync();
         return currentSegment;
     }
 
     /* Add the given segment to the list as the new active segment. */
-    void pushSegmentAndFrame(js::CallStackSegment *newcs, JSStackFrame *newfp,
+    void pushSegmentAndFrame(js::StackSegment *newseg, JSStackFrame *newfp,
                              JSFrameRegs &regs);
 
     /* Remove the active segment and make the next segment active. */
     void popSegmentAndFrame();
 
     /* Mark the top segment as suspended, without pushing a new one. */
     void saveActiveSegment();
 
     /* Undoes calls to suspendActiveSegment. */
     void restoreSegment();
 
     /*
      * Perform a linear search of all frames in all segments in the given context
      * for the given frame, returning the segment, if found, and null otherwise.
      */
-    js::CallStackSegment *containingSegment(const JSStackFrame *target);
+    js::StackSegment *containingSegment(const JSStackFrame *target);
 
     /*
      * Search the call stack for the nearest frame with static level targetLevel.
      */
     JSStackFrame *findFrameAtLevel(uintN targetLevel) {
         JSStackFrame *fp = this->fp;
         while (true) {
             JS_ASSERT(fp && fp->script);
@@ -2198,70 +2233,36 @@ private:
      * when p is null or that a memory pressure counter has reached some
      * threshold when p is not null. The function takes the pointer and not
      * a boolean flag to minimize the amount of code in its inlined callers.
      */
     JS_FRIEND_API(void) checkMallocGCPressure(void *p);
 };
 
 JS_ALWAYS_INLINE JSObject *
-JSStackFrame::varobj(js::CallStackSegment *css) const
+JSStackFrame::varobj(js::StackSegment *seg) const
 {
-    JS_ASSERT(css->contains(this));
-    return fun ? callobj : css->getInitialVarObj();
+    JS_ASSERT(seg->contains(this));
+    return fun ? callobj : seg->getInitialVarObj();
 }
 
 JS_ALWAYS_INLINE JSObject *
 JSStackFrame::varobj(JSContext *cx) const
 {
     JS_ASSERT(cx->activeSegment()->contains(this));
     return fun ? callobj : cx->activeSegment()->getInitialVarObj();
 }
 
 JS_ALWAYS_INLINE jsbytecode *
 JSStackFrame::pc(JSContext *cx) const
 {
     JS_ASSERT(cx->containingSegment(this) != NULL);
     return (cx->fp == this) ? cx->regs->pc : savedPC;
 }
 
-/*
- * InvokeArgsGuard is used outside the JS engine (where jscntxtinlines.h is
- * not included). To avoid visibility issues, force members inline.
- */
-namespace js {
-
-JS_ALWAYS_INLINE void
-StackSpace::popInvokeArgs(JSContext *cx, Value *vp)
-{
-    JS_ASSERT(!currentSegment->inContext());
-    currentSegment = currentSegment->getPreviousInMemory();
-}
-
-JS_ALWAYS_INLINE
-InvokeArgsGuard::InvokeArgsGuard()
-  : cx(NULL), css(NULL), vp(NULL)
-{}
-
-JS_ALWAYS_INLINE
-InvokeArgsGuard::InvokeArgsGuard(Value *vp, uintN argc)
-  : cx(NULL), css(NULL), vp(vp), argc(argc)
-{}
-
-JS_ALWAYS_INLINE
-InvokeArgsGuard::~InvokeArgsGuard()
-{
-    if (!css)
-        return;
-    JS_ASSERT(css == cx->stack().getCurrentSegment());
-    cx->stack().popInvokeArgs(cx, vp);
-}
-
-} /* namespace js */
-
 #ifdef JS_THREADSAFE
 # define JS_THREAD_ID(cx)       ((cx)->thread ? (cx)->thread->id : 0)
 #endif
 
 #if defined JS_THREADSAFE && defined DEBUG
 
 namespace js {
 
@@ -3256,16 +3257,22 @@ class AutoValueVector : private AutoGCRo
     const Value &operator[](size_t i) const { return vector[i]; }
 
     const Value *begin() const { return vector.begin(); }
     Value *begin() { return vector.begin(); }
 
     const Value *end() const { return vector.end(); }
     Value *end() { return vector.end(); }
 
+    const jsval *jsval_begin() const { return Jsvalify(begin()); }
+    jsval *jsval_begin() { return Jsvalify(begin()); }
+
+    const jsval *jsval_end() const { return Jsvalify(end()); }
+    jsval *jsval_end() { return Jsvalify(end()); }
+
     const Value &back() const { return vector.back(); }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
     
   private:
     Vector<Value, 8> vector;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -52,45 +52,64 @@ JSContext::ensureGeneratorStackSpace()
     if (!ok)
         js_ReportOutOfMemory(this);
     return ok;
 }
 
 namespace js {
 
 JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame *
-CallStackSegment::getCurrentFrame() const
+StackSegment::getCurrentFrame() const
 {
     JS_ASSERT(inContext());
-    return isSuspended() ? getSuspendedFrame() : cx->fp;
+    return isActive() ? cx->fp : getSuspendedFrame();
+}
+
+JS_REQUIRES_STACK JS_ALWAYS_INLINE JSFrameRegs *
+StackSegment::getCurrentRegs() const
+{
+    JS_ASSERT(inContext());
+    return isActive() ? cx->regs : getSuspendedRegs();
 }
 
 JS_REQUIRES_STACK inline Value *
 StackSpace::firstUnused() const
 {
-    CallStackSegment *ccs = currentSegment;
-    if (!ccs)
+    StackSegment *seg = currentSegment;
+    if (!seg) {
+        JS_ASSERT(invokeArgEnd == NULL);
         return base;
-    if (JSContext *cx = ccs->maybeContext()) {
-        if (!ccs->isSuspended())
-            return cx->regs->sp;
-        return ccs->getSuspendedRegs()->sp;
     }
-    return ccs->getInitialArgEnd();
+    if (JSContext *cx = seg->maybeContext()) {
+        Value *sp = seg->getCurrentRegs()->sp;
+        if (invokeArgEnd > sp) {
+            JS_ASSERT(invokeSegment == currentSegment);
+            JS_ASSERT_IF(cx->fp, invokeFrame == cx->fp);
+            return invokeArgEnd;
+        }
+        return sp;
+    }
+    JS_ASSERT(invokeArgEnd);
+    JS_ASSERT(invokeSegment == currentSegment);
+    return invokeArgEnd;
 }
 
+
 /* Inline so we don't need the friend API. */
-JS_ALWAYS_INLINE void
-StackSpace::assertIsCurrent(JSContext *cx) const
+JS_ALWAYS_INLINE bool
+StackSpace::isCurrentAndActive(JSContext *cx) const
 {
 #ifdef DEBUG
-    JS_ASSERT(cx == currentSegment->maybeContext());
-    JS_ASSERT(cx->getCurrentSegment() == currentSegment);
+    JS_ASSERT_IF(cx->getCurrentSegment(),
+                 cx->getCurrentSegment()->maybeContext() == cx);
     cx->assertSegmentsInSync();
 #endif
+    return currentSegment &&
+           currentSegment->isActive() &&
+           currentSegment == cx->getCurrentSegment();
 }
 
 JS_ALWAYS_INLINE bool
 StackSpace::ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
 {
     JS_ASSERT(from == firstUnused());
 #ifdef XP_WIN
     JS_ASSERT(from <= commitEnd);
@@ -121,64 +140,245 @@ JS_ALWAYS_INLINE bool
 StackSpace::ensureEnoughSpaceToEnterTrace()
 {
 #ifdef XP_WIN
     return ensureSpace(NULL, firstUnused(), MAX_TRACE_SPACE_VALS);
 #endif
     return end - firstUnused() > MAX_TRACE_SPACE_VALS;
 }
 
+JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+StackSpace::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard &ag)
+{
+    if (JS_UNLIKELY(!isCurrentAndActive(cx)))
+        return pushSegmentForInvoke(cx, argc, ag);
+
+    Value *sp = cx->regs->sp;
+    Value *start = invokeArgEnd > sp ? invokeArgEnd : sp;
+    JS_ASSERT(start == firstUnused());
+    uintN nvals = 2 + argc;
+    if (!ensureSpace(cx, start, nvals))
+        return false;
+
+    Value *vp = start;
+    Value *vpend = vp + nvals;
+    MakeValueRangeGCSafe(vp, vpend);
+
+    /* Use invokeArgEnd to root [vp, vpend) until the frame is pushed. */
+    ag.prevInvokeArgEnd = invokeArgEnd;
+    invokeArgEnd = vpend;
+#ifdef DEBUG
+    ag.prevInvokeSegment = invokeSegment;
+    invokeSegment = currentSegment;
+    ag.prevInvokeFrame = invokeFrame;
+    invokeFrame = cx->fp;
+#endif
+
+    ag.cx = cx;
+    ag.argv_ = vp + 2;
+    ag.argc_ = argc;
+    return true;
+}
+
+JS_REQUIRES_STACK JS_ALWAYS_INLINE void
+StackSpace::popInvokeArgs(const InvokeArgsGuard &ag)
+{
+    if (JS_UNLIKELY(ag.seg != NULL)) {
+        popSegmentForInvoke(ag);
+        return;
+    }
+
+    JS_ASSERT(isCurrentAndActive(ag.cx));
+    JS_ASSERT(invokeSegment == currentSegment);
+    JS_ASSERT(invokeFrame == ag.cx->fp);
+    JS_ASSERT(invokeArgEnd == ag.argv() + ag.argc());
+
+#ifdef DEBUG
+    invokeSegment = ag.prevInvokeSegment;
+    invokeFrame = ag.prevInvokeFrame;
+#endif
+    invokeArgEnd = ag.prevInvokeArgEnd;
+}
+
+JS_ALWAYS_INLINE
+InvokeArgsGuard::~InvokeArgsGuard()
+{
+    if (JS_UNLIKELY(!pushed()))
+        return;
+    cx->stack().popInvokeArgs(*this);
+}
+
+JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+StackSpace::getInvokeFrame(JSContext *cx, const CallArgs &args,
+                           uintN nmissing, uintN nfixed,
+                           InvokeFrameGuard &fg) const
+{
+    JS_ASSERT(firstUnused() == args.argv() + args.argc());
+
+    Value *start = args.argv() + args.argc();
+    ptrdiff_t nvals = nmissing + VALUES_PER_STACK_FRAME + nfixed;
+    if (!ensureSpace(cx, start, nvals))
+        return false;
+    fg.fp = reinterpret_cast<JSStackFrame *>(start + nmissing);
+    return true;
+}
+
+JS_REQUIRES_STACK JS_ALWAYS_INLINE void
+StackSpace::pushInvokeFrame(JSContext *cx, const CallArgs &args,
+                            InvokeFrameGuard &fg)
+{
+    JS_ASSERT(firstUnused() == args.argv() + args.argc());
+
+    JSStackFrame *fp = fg.fp;
+    JSStackFrame *down = cx->fp;
+    fp->down = down;
+    if (JS_UNLIKELY(!currentSegment->inContext())) {
+        cx->pushSegmentAndFrame(currentSegment, fp, fg.regs);
+    } else {
+#ifdef DEBUG
+        fp->savedPC = JSStackFrame::sInvalidPC;
+        JS_ASSERT(down->savedPC == JSStackFrame::sInvalidPC);
+#endif
+        down->savedPC = cx->regs->pc;
+        cx->setCurrentFrame(fp);
+        fg.prevRegs = cx->regs;
+        cx->setCurrentRegs(&fg.regs);
+    }
+    fg.cx = cx;
+
+    JS_ASSERT(isCurrentAndActive(cx));
+}
+
+JS_REQUIRES_STACK JS_ALWAYS_INLINE void
+StackSpace::popInvokeFrame(const InvokeFrameGuard &fg)
+{
+    JSContext *cx = fg.cx;
+    JSStackFrame *fp = fg.fp;
+
+    JS_ASSERT(isCurrentAndActive(cx));
+    if (JS_UNLIKELY(currentSegment->getInitialFrame() == fp)) {
+        cx->popSegmentAndFrame();
+    } else {
+        JS_ASSERT(fp == cx->fp);
+        JS_ASSERT(&fg.regs == cx->regs);
+        cx->setCurrentFrame(fp->down);
+        cx->setCurrentRegs(fg.prevRegs);
+#ifdef DEBUG
+        cx->fp->savedPC = JSStackFrame::sInvalidPC;
+#endif
+    }
+}
+
+JS_REQUIRES_STACK JS_ALWAYS_INLINE
+InvokeFrameGuard::~InvokeFrameGuard()
+{
+    if (JS_UNLIKELY(!pushed()))
+        return;
+    cx->stack().popInvokeFrame(*this);
+}
+
 JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame *
 StackSpace::getInlineFrame(JSContext *cx, Value *sp,
                            uintN nmissing, uintN nfixed) const
 {
-    assertIsCurrent(cx);
+    JS_ASSERT(isCurrentAndActive(cx));
     JS_ASSERT(cx->hasActiveSegment());
     JS_ASSERT(cx->regs->sp == sp);
 
     ptrdiff_t nvals = nmissing + VALUES_PER_STACK_FRAME + nfixed;
     if (!ensureSpace(cx, sp, nvals))
         return NULL;
 
     JSStackFrame *fp = reinterpret_cast<JSStackFrame *>(sp + nmissing);
     return fp;
 }
 
 JS_REQUIRES_STACK JS_ALWAYS_INLINE void
 StackSpace::pushInlineFrame(JSContext *cx, JSStackFrame *fp, jsbytecode *pc,
                             JSStackFrame *newfp)
 {
-    assertIsCurrent(cx);
-    JS_ASSERT(cx->hasActiveSegment());
+    JS_ASSERT(isCurrentAndActive(cx));
     JS_ASSERT(cx->fp == fp && cx->regs->pc == pc);
 
     fp->savedPC = pc;
     newfp->down = fp;
 #ifdef DEBUG
     newfp->savedPC = JSStackFrame::sInvalidPC;
 #endif
     cx->setCurrentFrame(newfp);
 }
 
 JS_REQUIRES_STACK JS_ALWAYS_INLINE void
 StackSpace::popInlineFrame(JSContext *cx, JSStackFrame *up, JSStackFrame *down)
 {
-    assertIsCurrent(cx);
+    JS_ASSERT(isCurrentAndActive(cx));
     JS_ASSERT(cx->hasActiveSegment());
     JS_ASSERT(cx->fp == up && up->down == down);
     JS_ASSERT(up->savedPC == JSStackFrame::sInvalidPC);
 
     JSFrameRegs *regs = cx->regs;
     regs->pc = down->savedPC;
 #ifdef DEBUG
     down->savedPC = JSStackFrame::sInvalidPC;
 #endif
     cx->setCurrentFrame(down);
 }
 
+JS_REQUIRES_STACK inline
+FrameRegsIter::FrameRegsIter(JSContext *cx)
+{
+    curseg = cx->getCurrentSegment();
+    if (JS_UNLIKELY(!curseg || !curseg->isActive()))
+        initSlow();
+    JS_ASSERT(cx->fp);
+    curfp = cx->fp;
+    cursp = cx->regs->sp;
+    curpc = cx->regs->pc;
+    return;
+}
+
+inline Value *
+FrameRegsIter::contiguousDownFrameSP(JSStackFrame *up)
+{
+    JS_ASSERT(up->argv);
+    Value *sp = up->argv + up->argc;
+#ifdef DEBUG
+    JS_ASSERT(sp <= up->argEnd());
+    JS_ASSERT(sp >= (up->down->script ? up->down->base() : up->down->slots()));
+    if (up->fun) {
+        uint16 nargs = up->fun->nargs;
+        uintN argc = up->argc;
+        uintN missing = argc < nargs ? nargs - argc : 0;
+        JS_ASSERT(sp == up->argEnd() - missing);
+    } else {
+        JS_ASSERT(sp == up->argEnd());
+    }
+#endif
+    return sp;
+}
+
+inline FrameRegsIter &
+FrameRegsIter::operator++()
+{
+    JSStackFrame *up = curfp;
+    JSStackFrame *down = curfp = curfp->down;
+    if (!down)
+        return *this;
+
+    curpc = down->savedPC;
+
+    if (JS_UNLIKELY(up == curseg->getInitialFrame())) {
+        incSlow(up, down);
+        return *this;
+    }
+
+    cursp = contiguousDownFrameSP(up);
+    return *this;
+}
+
 void
 AutoIdArray::trace(JSTracer *trc) {
     JS_ASSERT(tag == IDARRAY);
     MarkIdRange(trc, idArray->length, idArray->vector, "JSAutoIdArray.idArray");
 }
 
 class AutoNamespaceArray : protected AutoGCRooter {
   public:
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2032,22 +2032,22 @@ js_fun_call(JSContext *cx, uintN argc, V
     }
 
     /* Allocate stack space for fval, obj, and the args. */
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, argc, args))
         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;
 }
 
 /* ES5 15.3.4.3 */
 JSBool
 js_fun_apply(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
@@ -2113,54 +2113,54 @@ js_fun_apply(JSContext *cx, uintN argc, 
     /* Step 6. */
     uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX));
 
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, n, args))
         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()) {
         /*
          * Two cases, two loops: note how in the case of an active stack frame
          * backing aobj, even though we copy from fp->argv, we still must check
          * aobj->getArgsElement(i) for a hole, to handle a delete on the
          * 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;
 }
 
 static JSFunctionSpec function_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,   fun_toSource,   0,0),
 #endif
     JS_FN(js_toString_str,   fun_toString,   0,0),
@@ -2659,24 +2659,43 @@ js_ValueToCallableObject(JSContext *cx, 
 
 void
 js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
 {
     const char *name = NULL, *source = NULL;
     AutoValueRooter tvr(cx);
     uintN error = (flags & JSV2F_CONSTRUCT) ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
     LeaveTrace(cx);
+
+    /*
+     * We try to the print the code that produced vp if vp is a value in the
+     * most recent interpreted stack frame. Note that additional values, not
+     * directly produced by the script, may have been pushed onto the frame's
+     * expression stack (e.g. by InvokeFromEngine) thereby incrementing sp past
+     * the depth simulated by ReconstructPCStack. Since we must pass an offset
+     * from the top of the simulated stack to js_ReportValueError3, it is
+     * important to do bounds checking using the simulated, rather than actual,
+     * stack depth.
+     */
+    ptrdiff_t spindex = 0;
+
     FrameRegsIter i(cx);
     while (!i.done() && !i.pc())
         ++i;
 
-    ptrdiff_t spindex =
-        (!i.done() && i.fp()->base() <= vp && vp < i.sp())
-        ? vp - i.sp()
-        : ((flags & JSV2F_SEARCH_STACK) ? JSDVG_SEARCH_STACK : JSDVG_IGNORE_STACK);
+    if (!i.done()) {
+        uintN depth = js_ReconstructStackDepth(cx, i.fp()->script, i.pc());
+        Value *simsp = i.fp()->base() + depth;
+        JS_ASSERT(simsp <= i.sp());
+        if (i.fp()->base() <= vp && vp < simsp)
+            spindex = vp - simsp;
+    }
+
+    if (!spindex)
+        spindex = ((flags & JSV2F_SEARCH_STACK) ? JSDVG_SEARCH_STACK : JSDVG_IGNORE_STACK);
 
     js_ReportValueError3(cx, error, spindex, *vp, NULL, name, source);
 }
 
 /*
  * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
  * their name are stored as the JSLocalNames.array.
  */
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -391,28 +391,27 @@ NoSuchMethod(JSContext *cx, uintN argc, 
     if (!cx->stack().pushInvokeArgs(cx, 2, args))
         return JS_FALSE;
 
     JS_ASSERT(vp[0].isObject());
     JS_ASSERT(vp[1].isObject());
     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;
 }
 
 #endif /* JS_HAS_NO_SUCH_METHOD */
 
 namespace js {
 
 class AutoPreserveEnumerators {
@@ -478,99 +477,98 @@ callJSNative(JSContext *cx, CallOp callO
         return true;
     }
     return false;
 }
 
 template <typename T>
 static JS_REQUIRES_STACK bool
 InvokeCommon(JSContext *cx, JSFunction *fun, JSScript *script, T native,
-       const InvokeArgsGuard &args, uintN flags)
-{
-    uintN argc = args.getArgc();
-    Value *vp = args.getvp();
+             const CallArgs &argsRef, uintN flags)
+{
+    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)
             ASSERT_NOT_THROWING(cx);
 #endif
         return ok;
     }
 
     /* Calculate slot usage. */
     uintN nmissing;
     uintN nvars;
     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 {
         nvars = nmissing = 0;
     }
 
     uintN nfixed = script ? script->nslots : 0;
 
     /*
      * Get a pointer to new frame/slots. This memory is not "claimed", so the
      * code before pushInvokeFrame must not reenter the interpreter.
      */
-    JSFrameRegs regs;
     InvokeFrameGuard frame;
     if (!cx->stack().getInvokeFrame(cx, args, nmissing, nfixed, frame))
         return false;
     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;
     fp->blockChain = NULL;
     fp->imacpc = NULL;
     fp->flags = flags;
 
     /* Initialize regs. */
+    JSFrameRegs &regs = frame.getRegs();
     if (script) {
         regs.pc = script->code;
         regs.sp = fp->slots() + script->nfixed;
     } else {
         regs.pc = NULL;
         regs.sp = fp->slots();
     }
 
     /* Officially push |fp|. |frame|'s destructor pops. */
-    cx->stack().pushInvokeFrame(cx, args, frame, regs);
+    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)
             fp->scopeChain = down->scopeChain;
 
         /* Ensure that we have a scope chain. */
         if (!fp->scopeChain)
             fp->scopeChain = parent;
@@ -581,35 +579,35 @@ InvokeCommon(JSContext *cx, JSFunction *
             return false;
     }
 
     /*
      * Compute |this|. Currently, this must happen after the frame is pushed
      * and fp->scopeChain is correct because the thisObject hook may call
      * JS_GetScopeChain.
      */
-    JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !vp[1].isPrimitive());
-    if (vp[1].isObject() && !(flags & JSINVOKE_CONSTRUCT)) {
+    JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !args.thisv().isPrimitive());
+    if (args.thisv().isObject() && !(flags & JSINVOKE_CONSTRUCT)) {
         /*
          * We must call the thisObject hook in case we are not called from the
          * interpreter, where a prior bytecode has computed an appropriate
          * |this| already.
          */
-        JSObject *thisp = vp[1].toObjectOrNull();
+        JSObject *thisp = args.thisv().toObjectOrNull();
         if (!thisp) {
-            JSObject *funobj = &args.getvp()[0].toObject();
+            JSObject *funobj = &args.callee().toObject();
             thisp = funobj->getGlobal();
         }
         thisp = thisp->thisObject(cx);
         if (!thisp)
              return false;
-         vp[1].setObject(*thisp);
-         fp->thisv.setObject(*thisp);
-    }
-    JS_ASSERT_IF(!vp[1].isPrimitive(), IsSaneThisObject(vp[1].toObject()));
+        args.thisv().setObject(*thisp);
+        fp->thisv.setObject(*thisp);
+    }
+    JS_ASSERT_IF(!args.thisv().isPrimitive(), IsSaneThisObject(args.thisv().toObject()));
 
     /* Call the hook if present after we fully initialized the frame. */
     JSInterpreterHook hook = cx->debugHooks->callHook;
     void *hookData = NULL;
     if (hook)
         hookData = hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData);
 
     DTrace::enterJSFun(cx, fp, fun, fp->down, fp->argc, fp->argv);
@@ -640,17 +638,17 @@ InvokeCommon(JSContext *cx, JSFunction *
 
     if (hookData) {
         hook = cx->debugHooks->callHook;
         if (hook)
             hook(cx, fp, JS_FALSE, &ok, hookData);
     }
 
     fp->putActivationObjects(cx);
-    *vp = fp->rval;
+    args.rval() = fp->rval;
     return ok;
 }
 
 static JSBool
 DoConstruct(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
 
     Class *clasp = argv[-2].toObject().getClass();
@@ -686,95 +684,91 @@ DoSlowCall(JSContext *cx, uintN argc, Va
 
 /*
  * Find a function reference and its 'this' value implicit first parameter
  * under argc arguments on cx's stack, and call the function.  Push missing
  * required arguments, allocate declared local variables, and pop everything
  * when done.  Then push the return value.
  */
 JS_REQUIRES_STACK bool
-Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
-{
-    Value *vp = args.getvp();
-    uintN argc = args.getArgc();
-    JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
-
-    const Value &v = vp[0];
-    if (v.isPrimitive()) {
-        js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
+Invoke(JSContext *cx, const CallArgs &args, uintN flags)
+{
+    JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
+
+    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) {
         /* Get private data and set derived locals from it. */
         JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
         Native native;
         JSScript *script;
         if (FUN_INTERPRETED(fun)) {
             native = NULL;
             script = fun->u.i.script;
             JS_ASSERT(script);
 
             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;
             }
         } else {
             native = fun->u.n.native;
             script = NULL;
         }
 
         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
              * interpreter, where a prior bytecode has computed an appropriate
              * |this| already.
              *
              * But we need to compute |this| eagerly only for so-called "slow"
              * (i.e., not fast) native functions. Fast natives must use either
              * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
              * 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;
             }
         }
         return InvokeCommon(cx, fun, script, native, args, 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);
     }
     CallOp callOp = (clasp->flags & Class::CALL_IS_FAST) ? (CallOp) clasp->call : DoSlowCall;
     return InvokeCommon(cx, NULL, NULL, callOp, args, flags);
 }
 
@@ -789,30 +783,30 @@ InternalInvoke(JSContext *cx, const Valu
                   uintN argc, Value *argv, Value *rval)
 {
     LeaveTrace(cx);
 
     InvokeArgsGuard args;
     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;
 
     /*
      * Store *rval in the lastInternalResult pigeon-hole GC root, solely
      * so users of js_InternalInvoke and its direct and indirect
      * (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();
 
     return JS_TRUE;
 }
 
 bool
 InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
@@ -893,18 +887,17 @@ Execute(JSContext *cx, JSObject *chain, 
         fp->annotation = down->annotation;
         fp->scopeChain = chain;
 
         /*
          * We want to call |down->varobj()|, but this requires knowing the
          * CallStackSegment of |down|. If |down == cx->fp|, the callstack is
          * simply the context's active callstack, so we can use
          * |down->varobj(cx)|.  When |down != cx->fp|, we need to do a slow
-         * linear search. Luckily, this only happens with indirect eval and
-         * JS_EvaluateInStackFrame.
+         * linear search. Luckily, this only happens with EvaluateInFrame.
          */
         initialVarObj = (down == cx->fp)
                         ? down->varobj(cx)
                         : down->varobj(cx->containingSegment(down));
     } else {
         fp->callobj = NULL;
         fp->argsobj = NULL;
         fp->fun = NULL;
@@ -1182,91 +1175,81 @@ InstanceOfSlow(JSContext *cx, JSObject *
                                  ? obj->getClass()->name
                                  : js_null_str);
         }
     }
     return false;
 }
 
 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();
-
-    if (vp->isPrimitive()) {
+    CallArgs args = argsRef;
+
+    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();
 
     /*
      * Call fast constructors without making the object first.
      * The native will be able to make the right new object faster.
      */
     if (obj2->isFunction()) {
         JSFunction *fun = (JSFunction *) obj2->getPrivate();
         if (fun->isFastConstructor()) {
-            vp[1].setMagic(JS_FAST_CONSTRUCTOR);
+            args.thisv().setMagic(JS_FAST_CONSTRUCTOR);
 
             FastNative fn = (FastNative)fun->u.n.native;
-            if (!fn(cx, args.getArgc(), vp))
+            if (!fn(cx, args.argc(), args.base()))
                 return JS_FALSE;
-            JS_ASSERT(!vp->isPrimitive());
+            JS_ASSERT(!args.callee().isPrimitive());
             return JS_TRUE;
         }
     }
 
-    /*
-     * 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;
 
     if (obj2->getClass() == &js_FunctionClass) {
         JSFunction *f = GET_FUNCTION_PRIVATE(cx, obj2);
         if (!f->isInterpreted() && f->u.n.clasp)
             clasp = f->u.n.clasp;
     }
 
     JSObject* obj = NewObject<WithProto::Class>(cx, clasp, proto, parent);
     if (!obj) {
         return JS_FALSE;
     }
 
 
-    /* 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);
     return JS_TRUE;
 }
 
 bool
 ValueToId(JSContext *cx, const Value &v, jsid *idp)
@@ -2796,16 +2779,17 @@ BEGIN_CASE(JSOP_STOP)
             if (fp->rval.isPrimitive()) {
                 JS_ASSERT(!fp->thisv.isPrimitive());
                 fp->rval = fp->thisv;
             }
             JS_RUNTIME_METER(cx->runtime, constructs);
         }
 
         JSStackFrame *down = fp->down;
+        bool recursive = fp->script == down->script;
         Value *newsp = fp->argv - 1;
 
         /* Pop the frame. */
         cx->stack().popInlineFrame(cx, fp, down);
 
         /* Propagate return value before fp is lost. */
         regs.sp = newsp;
         regs.sp[-1] = fp->rval;
@@ -4675,17 +4659,17 @@ BEGIN_CASE(JSOP_NEW)
             }
 
             vp[1].setObject(*obj2);
             flags = JSFRAME_CONSTRUCTING;
             goto inline_call;
         }
     }
 
-    if (!InvokeConstructor(cx, InvokeArgsGuard(vp, argc)))
+    if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
         goto error;
     regs.sp = vp + 1;
     CHECK_INTERRUPT_HANDLER();
     TRACE_0(NativeCallComplete);
 
   end_new:;
 }
 END_CASE(JSOP_NEW)
@@ -4845,34 +4829,34 @@ BEGIN_CASE(JSOP_APPLY)
             if (!ok)
                 goto error;
             TRACE_0(NativeCallComplete);
             goto end_call;
         }
     }
 
     bool ok;
-    ok = Invoke(cx, InvokeArgsGuard(vp, argc), 0);
+    ok = Invoke(cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0);
     regs.sp = vp + 1;
     CHECK_INTERRUPT_HANDLER();
     if (!ok)
         goto error;
     JS_RUNTIME_METER(rt, nonInlineCalls);
     TRACE_0(NativeCallComplete);
 
   end_call:;
 }
 END_CASE(JSOP_CALL)
 }
 
 BEGIN_CASE(JSOP_SETCALL)
 {
     uintN argc = GET_ARGC(regs.pc);
     Value *vp = regs.sp - argc - 2;
-    JSBool ok = Invoke(cx, InvokeArgsGuard(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;
 }
 END_CASE(JSOP_SETCALL)
 
 #define SLOW_PUSH_THISV(cx, obj)                                            \
     JS_BEGIN_MACRO                                                          \
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -223,22 +223,22 @@ struct JSStackFrame
     }
 
     JSObject *callee() {
         return argv ? &argv[-2].toObject() : NULL;
     }
 
     /*
      * Get the "variable object" (ES3 term) associated with the Execution
-     * Context's VariableEnvironment (ES5 10.3). The given CallStackSegment
+     * Context's VariableEnvironment (ES5 10.3). The given StackSegment
      * must contain this stack frame.
      */
-    JSObject *varobj(js::CallStackSegment *css) const;
+    JSObject *varobj(js::StackSegment *seg) const;
 
-    /* Short for: varobj(cx->activeCallStack()). */
+    /* Short for: varobj(cx->activeSegment()). */
     JSObject *varobj(JSContext *cx) const;
 
     inline JSObject *getThisObject(JSContext *cx);
 
     bool isGenerator() const { return !!(flags & JSFRAME_GENERATOR); }
     bool isFloatingGenerator() const {
         JS_ASSERT_IF(flags & JSFRAME_FLOATING_GENERATOR, isGenerator());
         return !!(flags & JSFRAME_FLOATING_GENERATOR);
@@ -320,28 +320,50 @@ PrimitiveThisTest(JSFunction *fun, const
 {
     uint16 flags = fun->flags;
     return (v.isString() && !!(flags & JSFUN_THISP_STRING)) ||
            (v.isNumber() && !!(flags & JSFUN_THISP_NUMBER)) ||
            (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]; }
+
+    bool 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,
  * i.e., there must have been no un-popped pushes to cx->stack(). Furthermore,
  * |args.getvp()[0]| should be the callee, |args.getvp()[1]| should be |this|,
  * and the range [args.getvp() + 2, args.getvp() + 2 + args.getArgc()) should
  * be initialized actual arguments.
  */
 extern JS_REQUIRES_STACK bool
-Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags);
-
-extern JS_REQUIRES_STACK JS_FRIEND_API(bool)
-InvokeFriendAPI(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
  * we can share bits stored in JSStackFrame.flags and passed to:
  *
  *   js_Invoke
  *   js_InternalInvoke
  *   js_ValueToFunction
@@ -384,17 +406,17 @@ extern bool
 InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
                  JSAccessMode mode, uintN argc, Value *argv, Value *rval);
 
 extern JS_FORCES_STACK bool
 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, JSStackFrame *stopFp, uintN inlineCallCount = 0);
 
 extern JS_REQUIRES_STACK bool
 RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain);
 
 #define JSPROP_INITIALIZER 0x100   /* NB: Not a valid property attribute. */
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -71,16 +71,17 @@
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jsstaticcheck.h"
 #include "jstracer.h"
 #include "jsvector.h"
 
 #include "jsscriptinlines.h"
+#include "jscntxtinlines.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
 
 /*
  * Index limit must stay within 32 bits.
  */
@@ -5159,25 +5160,29 @@ js_DecompileValueGenerator(JSContext *cx
             do {
                 if (sp == stackBase) {
                     pcdepth = -1;
                     goto release_pcstack;
                 }
             } while (*--sp != v);
 
             /*
-             * The value may have come from beyond stackBase + pcdepth,
-             * meaning that it came from a temporary slot that the
-             * interpreter uses for GC roots or when JSOP_APPLY extended
-             * the stack to fit the argument array elements. Only update pc
-             * if beneath stackBase + pcdepth; otherwise blame existing
-             * (current) PC.
+             * The value may have come from beyond stackBase + pcdepth, meaning
+             * that it came from a temporary slot pushed by the interpreter or
+             * arguments pushed for an InvokeFromEngine call. Only update pc if
+             * beneath stackBase + pcdepth. If above, we don't know whether the
+             * value is associated with the current pc or from a fast native
+             * whose arguments have been pushed, so just print the value.
              */
-            if (sp < stackBase + pcdepth)
-                pc = pcstack[sp - stackBase];
+            if (sp >= stackBase + pcdepth) {
+                pcdepth = -1;
+                goto release_pcstack;
+            }
+
+            pc = pcstack[sp - stackBase];
         }
 
       release_pcstack:
         cx->free(pcstack);
         if (pcdepth < 0)
             goto do_fallback;
     }
 
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -138,17 +138,17 @@ extern "C++" {
 namespace js {
 
 class ExecuteArgsGuard;
 class InvokeFrameGuard;
 class InvokeArgsGuard;
 class TraceRecorder;
 struct TraceMonitor;
 class StackSpace;
-class CallStackSegment;
+class StackSegment;
 
 class TokenStream;
 struct Token;
 struct TokenPos;
 struct TokenPtr;
 
 class ContextAllocPolicy;
 class SystemAllocPolicy;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1896,20 +1896,16 @@ str_search(JSContext *cx, uintN argc, Va
 }
 
 struct ReplaceData
 {
     ReplaceData(JSContext *cx)
      : g(cx), cb(cx)
     {}
 
-    bool argsPushed() const {
-        return args.getvp() != NULL;
-    }
-
     JSString        *str;           /* 'this' parameter object as a string */
     RegExpGuard     g;              /* regexp parameter object and private data */
     JSObject        *lambda;        /* replacement function object or null */
     JSString        *repstr;        /* replacement string */
     jschar          *dollar;        /* null or pointer to first $ in repstr */
     jschar          *dollarEnd;     /* limit pointer for js_strchr_limit */
     jsint           index;          /* index in result of next replacement */
     jsint           leftIndex;      /* left context index in str->chars */
@@ -1973,24 +1969,24 @@ InterpretDollar(JSContext *cx, jschar *d
         return &res->leftContext;
       case '\'':
         return &res->rightContext;
     }
     return NULL;
 }
 
 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;
 }
 
 class PreserveRegExpStatics {
     JSContext *cx;
     JSRegExpStatics save;
 
   public:
@@ -2022,33 +2018,35 @@ FindReplaceLength(JSContext *cx, Replace
          * DoReplace.  The lambda is called with arguments ($&, $1, $2, ...,
          * index, input), i.e., all the properties of a regexp match array.
          * For $&, etc., we must create string jsvals from cx->regExpStatics.
          * We grab up stack space to keep the newborn strings GC-rooted.
          */
         uintN p = rdata.g.re()->parenCount;
         uintN argc = 1 + p + 2;
 
-        if (!rdata.argsPushed() && !cx->stack().pushInvokeArgs(cx, argc, rdata.args))
+        if (!rdata.args.pushed() && !cx->stack().pushInvokeArgs(cx, argc, rdata.args))
             return false;
 
         PreserveRegExpStatics save(cx);
 
         /* Push lambda and its 'this' parameter. */
-        Value *sp = rdata.args.getvp();
-        sp++->setObject(*lambda);
-        sp++->setNull();
+        CallArgs &args = rdata.args;
+        args.callee().setObject(*lambda);
+        args.thisv().setNull();
+
+        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;
         }
 
         /* Make sure to push undefined for any unmatched parens. */
         for (; i < p; i++)
             *sp++ = UndefinedValue();
 
         /* Push match index and input string. */
@@ -2058,17 +2056,17 @@ FindReplaceLength(JSContext *cx, Replace
         if (!Invoke(cx, rdata.args, 0))
             return false;
 
         /*
          * NB: we count on the newborn string root to hold any string
          * 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;
 
         rdata.repstr = repstr;
         *sizep = repstr->length();
 
         return true;
     }
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -5762,19 +5762,19 @@ SynthesizeFrame(JSContext* cx, const Fra
 JS_REQUIRES_STACK static void
 SynthesizeSlowNativeFrame(TracerState& state, JSContext *cx, VMSideExit *exit)
 {
     /*
      * StackSpace::getInlineFrame calls js_ReportOutOfScriptQuota if there is
      * no space (which will try to deep bail, which is bad), however we already
      * check on entry to ExecuteTree that there is enough space.
      */
-    CallStackSegment *css;
+    StackSegment *seg;
     JSStackFrame *fp;
-    cx->stack().getSynthesizedSlowNativeFrame(cx, css, fp);
+    cx->stack().getSynthesizedSlowNativeFrame(cx, seg, fp);
 
 #ifdef DEBUG
     JSObject *callee = &state.nativeVp[0].toObject();
     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, callee);
     JS_ASSERT(!fun->isInterpreted() && !fun->isFastNative());
     JS_ASSERT(fun->u.n.extra == 0);
 #endif
 
@@ -5790,17 +5790,17 @@ SynthesizeSlowNativeFrame(TracerState& s
     fp->annotation = NULL;
     JS_ASSERT(cx->fp->scopeChain);
     fp->scopeChain = cx->fp->scopeChain;
     fp->blockChain = NULL;
     fp->flags = exit->constructing() ? JSFRAME_CONSTRUCTING : 0;
 
     state.bailedSlowNativeRegs = *cx->regs;
 
-    cx->stack().pushSynthesizedSlowNativeFrame(cx, css, fp, state.bailedSlowNativeRegs);
+    cx->stack().pushSynthesizedSlowNativeFrame(cx, seg, fp, state.bailedSlowNativeRegs);
 
     state.bailedSlowNativeRegs.pc = NULL;
     state.bailedSlowNativeRegs.sp = fp->slots();
 }
 
 static JS_REQUIRES_STACK bool
 RecordTree(JSContext* cx, TreeFragment* first, jsbytecode* outer,
            uint32 outerArgc, SlotList* globalSlots, RecordReason reason)
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -391,17 +391,17 @@ stubs::SlowCall(VMFrame &f, uint32 argc)
 
             FastNative fn = (FastNative)fun->u.n.native;
             if (!fn(cx, argc, vp))
                 THROWV(NULL);
             return NULL;
         }
     }
 
-    if (!Invoke(f.cx, InvokeArgsGuard(vp, argc), 0))
+    if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
         THROWV(NULL);
 
     return NULL;
 }
 
 void * JS_FASTCALL
 stubs::SlowNew(VMFrame &f, uint32 argc)
 {
@@ -450,17 +450,17 @@ stubs::SlowNew(VMFrame &f, uint32 argc)
             if (!fn(cx, argc, vp))
                 THROWV(NULL);
             JS_ASSERT(!vp->isPrimitive());
 
             return NULL;
         }
     }
 
-    if (!InvokeConstructor(cx, InvokeArgsGuard(vp, argc)))
+    if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
         THROWV(NULL);
 
     return NULL;
 }
 
 static inline bool
 CreateLightFrame(VMFrame &f, uint32 flags, uint32 argc)
 {
--- a/js/src/methodjit/Retcon.h
+++ b/js/src/methodjit/Retcon.h
@@ -118,26 +118,27 @@ public:
 
 private:
     JSStackFrame *curfp;
 };
 
 class CallStackIterator
 {
 public:
-    CallStackIterator(JSContext *cx) : curcs(cx->stack().getCurrentSegment()) { };
+    CallStackIterator(JSContext *cx)
+      : curcs(cx->stack().getCurrentSegment()) { };
   
     bool done() const { return curcs == NULL; }
     CallStackIterator& operator++();
     FrameIterator top() const;
     FrameIterator bottom() const;
 
-    CallStackSegment *cs() const { return curcs; }
+    StackSegment *cs() const { return curcs; }
 
 private:
-    CallStackSegment *curcs;    
+    StackSegment *curcs;    
 };
 
 } /* namespace mjit */
 } /* namespace js */
 
 #endif
 
--- a/js/src/xpconnect/src/xpcwrappedjsclass.cpp
+++ b/js/src/xpconnect/src/xpcwrappedjsclass.cpp
@@ -1261,34 +1261,33 @@ class ContextPrincipalGuard
     ~ContextPrincipalGuard() { if (ssm) ssm->PopContextPrincipal(ccx); }
 };
 
 NS_IMETHODIMP
 nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex,
                                 const XPTMethodDescriptor* info,
                                 nsXPTCMiniVariant* nativeParams)
 {
-    jsval* stackbase = nsnull;
     jsval* sp = nsnull;
+    jsval* argv = nsnull;
     uint8 i;
     uint8 argc=0;
     uint8 paramCount=0;
     nsresult retval = NS_ERROR_FAILURE;
     nsresult pending_result = NS_OK;
     JSBool success;
     JSBool readyToDoTheCall = JS_FALSE;
     nsID  param_iid;
     JSObject* obj;
     const char* name = info->name;
     jsval fval;
     JSBool foundDependentParam;
     XPCContext* xpcc;
     JSContext* cx;
     JSObject* thisObj;
-    bool invokeCall;
 
     // Make sure not to set the callee on ccx until after we've gone through
     // the whole nsIXPCFunctionThisTranslator bit.  That code uses ccx to
     // convert natives to JSObjects, but we do NOT plan to pass those JSObjects
     // to our real callee.
     JSContext *context = GetContextFromObject(wrapper->GetJSObject());
     XPCCallContext ccx(NATIVE_CALLER, context);
     if(ccx.IsValid())
@@ -1298,17 +1297,17 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
     }
     else
     {
         xpcc = nsnull;
         cx = nsnull;
     }
 
     AutoScriptEvaluate scriptEval(cx);
-    js::InvokeArgsGuard args;
+    js::AutoValueVector args(cx);
     ContextPrincipalGuard principalGuard(ccx);
 
     obj = thisObj = wrapper->GetJSObject();
 
     JSAutoEnterCompartment autoCompartment(ccx, obj);
 
     // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
     paramCount = info->num_args;
@@ -1354,18 +1353,17 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
     // This adds a fair amount of complexity, but it's a good optimization
     // compared to calling JS_AddRoot for each item.
 
     js::LeaveTrace(cx);
 
     // setup stack
 
     // if this isn't a function call then we don't need to push extra stuff
-    invokeCall = !(XPT_MD_IS_SETTER(info->flags) || XPT_MD_IS_GETTER(info->flags));
-    if (invokeCall)
+    if (!(XPT_MD_IS_SETTER(info->flags) || XPT_MD_IS_GETTER(info->flags)))
     {
         // We get fval before allocating the stack to avoid gc badness that can
         // happen if the GetProperty call leaves our request and the gc runs
         // while the stack we allocate contains garbage.
 
         // If the interface is marked as a [function] then we will assume that
         // our JSObject is a function and not an object with a named method.
 
@@ -1456,35 +1454,24 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
             // specifically report the failure to find a function with this name.
             // This is what we do below if the property is found but is not a
             // function. We just need to factor better so we can get to that
             // reporting path from here.
             goto pre_call_clean_up;
         }
     }
 
-    /*
-     * pushInvokeArgs allocates |2 + argc| slots, but getters and setters
-     * require only one rooted jsval, so waste one value.
-     */
-    JS_ASSERT_IF(!invokeCall, argc < 2);
-    if (!cx->stack().pushInvokeArgsFriendAPI(cx, invokeCall ? argc : 0, args))
+    if (!args.resize(argc))
     {
         retval = NS_ERROR_OUT_OF_MEMORY;
         goto pre_call_clean_up;
     }
 
-    sp = stackbase = Jsvalify(args.getvp());
-
-    // this is a function call, so push function and 'this'
-    if(invokeCall)
-    {
-        *sp++ = fval;
-        *sp++ = OBJECT_TO_JSVAL(thisObj);
-    }
+    argv = args.jsval_begin();
+    sp = argv;
 
     // Figure out what our callee is
     if(XPT_MD_IS_GETTER(info->flags) || XPT_MD_IS_SETTER(info->flags))
     {
         // Pull the getter or setter off of |obj|
         uintN attrs;
         JSBool found;
         JSPropertyOp getter;
@@ -1674,27 +1661,37 @@ pre_call_clean_up:
 
     if(!readyToDoTheCall)
         return retval;
 
     // do the deed - note exceptions
 
     JS_ClearPendingException(cx);
 
-    /* On success, the return value is placed in |*stackbase|. */
-    /* On success, the return value is placed in |*stackbase|. */
+    jsval rval;
     if(XPT_MD_IS_GETTER(info->flags))
-        success = JS_GetProperty(cx, obj, name, stackbase);
+    {
+        success = JS_GetProperty(cx, obj, name, argv);
+        rval = *argv;
+    }
     else if(XPT_MD_IS_SETTER(info->flags))
-        success = JS_SetProperty(cx, obj, name, stackbase);
+    {
+        success = JS_SetProperty(cx, obj, name, argv);
+        rval = *argv;
+    }
     else
     {
         if(!JSVAL_IS_PRIMITIVE(fval))
         {
-            success = js::InvokeFriendAPI(cx, args, 0);
+            uint32 oldOpts = JS_GetOptions(cx);
+            JS_SetOptions(cx, oldOpts | JSOPTION_DONT_REPORT_UNCAUGHT);
+
+            success = JS_CallFunctionValue(cx, thisObj, fval, argc, argv, &rval);
+
+            JS_SetOptions(cx, oldOpts);
         }
         else
         {
             // The property was not an object so can't be a function.
             // Let's build and 'throw' an exception.
 
             static const nsresult code =
                     NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
@@ -1757,19 +1754,19 @@ pre_call_clean_up:
         nsXPTCMiniVariant* pv;
 
         if(param.IsDipper())
             pv = (nsXPTCMiniVariant*) &nativeParams[i].val.p;
         else
             pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
 
         if(param.IsRetval())
-            val = *stackbase;
-        else if(JSVAL_IS_PRIMITIVE(stackbase[i+2]) ||
-                !JS_GetPropertyById(cx, JSVAL_TO_OBJECT(stackbase[i+2]),
+            val = rval;
+        else if(JSVAL_IS_PRIMITIVE(argv[i]) ||
+                !JS_GetPropertyById(cx, JSVAL_TO_OBJECT(argv[i]),
                     mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
                     &val))
             break;
 
         // setup allocator and/or iid
 
         if(type_tag == nsXPTType::T_INTERFACE)
         {
@@ -1808,18 +1805,18 @@ pre_call_clean_up:
             PRBool isSizedString = isArray ?
                     JS_FALSE :
                     type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
                     type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
 
             pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
 
             if(param.IsRetval())
-                val = *stackbase;
-            else if(!JS_GetPropertyById(cx, JSVAL_TO_OBJECT(stackbase[i+2]),
+                val = rval;
+            else if(!JS_GetPropertyById(cx, JSVAL_TO_OBJECT(argv[i]),
                         mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
                         &val))
                 break;
 
             // setup allocator and/or iid
 
             if(isArray)
             {