Bug 656462, part 4 - Simplify stack code, keep track of native calls, create new iterator over native/scripted callstack, make JS_SaveFrameChain fallible (r=waldo,mrbkap)
authorLuke Wagner <luke@mozilla.com>
Fri, 13 May 2011 08:56:26 -0700
changeset 70987 bb9e5496b0aca48c09052c1342e4c4351a8ede87
parent 70986 8ab0930a7b83055c6dbe8d3f02686e359fdd7d94
child 70988 fd805dbc154c49345e21b37bb56447e43de6d61d
push idunknown
push userunknown
push dateunknown
reviewerswaldo, mrbkap
bugs656462
milestone7.0a1
Bug 656462, part 4 - Simplify stack code, keep track of native calls, create new iterator over native/scripted callstack, make JS_SaveFrameChain fallible (r=waldo,mrbkap)
content/xbl/src/nsXBLProtoImplMethod.cpp
dom/base/nsJSEnvironment.cpp
js/src/jit-test/tests/basic/testStackIter.js
js/src/jit-test/tests/basic/testStackIterDebug.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsinterpinlines.h
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jsxml.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/shell/js.cpp
js/src/tracejit/Writer.cpp
js/src/tracejit/Writer.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/src/xpconnect/loader/mozJSComponentLoader.cpp
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcthreadcontext.cpp
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -337,16 +337,17 @@ nsXBLProtoImplAnonymousMethod::Execute(n
                                 0 /* argc */, nsnull /* argv */, &retval);
   }
 
   if (!ok) {
     // If a constructor or destructor threw an exception, it doesn't stop
     // anything else.  We just report it.  Note that we need to set aside the
     // frame chain here, since the constructor invocation is not related to
     // whatever is on the stack right now, really.
-    JSStackFrame* frame = JS_SaveFrameChain(cx);
-    ::JS_ReportPendingException(cx);
-    JS_RestoreFrameChain(cx, frame);
+    JSBool saved = JS_SaveFrameChain(cx);
+    JS_ReportPendingException(cx);
+    if (saved)
+        JS_RestoreFrameChain(cx);
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3515,19 +3515,20 @@ nsJSContext::DropScriptObject(void* aScr
 }
 
 void
 nsJSContext::ReportPendingException()
 {
   // set aside the frame chain, since it has nothing to do with the
   // exception we're reporting.
   if (mIsInitialized && ::JS_IsExceptionPending(mContext)) {
-    JSStackFrame* frame = JS_SaveFrameChain(mContext);
+    PRBool saved = ::JS_SaveFrameChain(mContext);
     ::JS_ReportPendingException(mContext);
-    JS_RestoreFrameChain(mContext, frame);
+    if (saved)
+        ::JS_RestoreFrameChain(mContext);
   }
 }
 
 /**********************************************************************
  * nsJSRuntime implementation
  *********************************************************************/
 
 // QueryInterface implementation for nsJSRuntime
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testStackIter.js
@@ -0,0 +1,174 @@
+function stackToString(stack) {
+    var str = "|";
+    for (var i = 0; i < stack.length; ++i) {
+        if (typeof stack[i] === "string")
+            str += stack[i];
+        else
+            str += stack[i].name;
+        str += "|";
+    }
+    return str;
+}
+
+function assertStackIs(s1) {
+    var s2 = dumpStack();
+    var me = s2.shift();
+    assertEq(me, assertStackIs);
+    try {
+        if (s1.length != s2.length)
+            throw "Length " + s1.length + " not equal " + s2.length;
+        for (var i = 0; i < s1.length; ++i) {
+            var match;
+            if (typeof s1[i] === "string" && (m = s1[i].match(/bound\((.*)\)/))) {
+                if (s2[i].name != m[1] || s2[i].toSource().indexOf("[native code]") < 0)
+                    throw "Element " + i + " not bound function";
+            } else if (s1[i] != s2[i]) {
+                throw "Element " + i + " not equal";
+            }
+        }
+    }
+    catch (e) {
+        print("Given     = " + stackToString(s1));
+        print("dumpStack = " + stackToString(s2));
+        throw e;
+    }
+}
+
+/***********/
+
+assertStackIs(["global-code"]);
+(function f() { assertStackIs([f, "global-code"]) })();
+eval("assertStackIs(['eval-code', 'global-code'])");
+(function f() { eval("assertStackIs(['eval-code', f, 'global-code'])"); })();
+(function f() { eval("(function g() { assertStackIs([g, 'eval-code', f, 'global-code']); })()"); })();
+(function f() { assertStackIs([f, 'bound(f)', 'global-code']); }).bind()()
+this['eval']("assertStackIs(['eval-code', eval, 'global-code'])");
+eval.bind(null, "assertStackIs(['eval-code', eval, 'bound(eval)', 'global-code'])")();
+(function f() { assertStackIs([f, Function.prototype.call, 'global-code']) }).call(null);
+(function f() { assertStackIs([f, Function.prototype.apply, 'global-code']) }).apply(null, {});
+(function f() { (function g(x,y,z) { assertStackIs([g,f,'global-code']); })() })(1);
+
+/***********/
+
+var gen = (function g() { assertStackIs([g, gen.next, fun, 'global-code']); yield; })();
+var fun = function f() { gen.next() };
+fun();
+
+var gen = (function g(x) { assertStackIs([g, gen.next, fun, 'global-code']); yield; })(1,2,3);
+var fun = function f() { gen.next() };
+fun();
+
+var gen = (function g(x) { assertStackIs([g, gen.next, 'eval-code', fun, 'global-code']); yield; })(1,2,3);
+var fun = function f() { eval('gen.next()') };
+fun();
+
+/***********/
+
+const N = 100;
+
+(function f(x) {
+    if (x === 0) {
+        var s = dumpStack();
+        for (var i = 0; i < N; ++i)
+            assertEq(s[i], f);
+        return;
+    }
+    f(x-1);
+})(N);
+
+/***********/
+
+"abababab".replace(/b/g, function g() {
+    assertStackIs([g, String.prototype.replace, "global-code"]);
+});
+
+/***********/
+
+var obj = {
+    toString:function toString() {
+        assertStackIs([toString, String.prototype.concat, "global-code"]);
+        return "";
+    }
+}
+"a".concat(obj);
+
+(function f() {
+    var obj = {
+        toString:(Array.prototype.sort.bind([1,2], function cb() {
+            assertStackIs([cb, Array.prototype.sort, "bound(sort)",
+                           String.prototype.concat, f, "global-code"]);
+            throw "OK";
+        }))
+    }
+
+    try {
+        "a".concat(obj);
+    } catch(e) {
+        assertEq(e, "OK");
+    }
+})();
+
+/***********/
+
+var obj = { valueOf:function valueOf() {
+    assertStackIs([valueOf, Math.sin, Array.prototype.sort, "global-code"]);
+} };
+[obj, obj].sort(Math.sin);
+
+var obj = { valueOf:(function valueOf() {
+    assertStackIs([valueOf, "bound(valueOf)", Math.sin, Array.prototype.sort, "global-code"]);
+}).bind() };
+[obj, obj].sort(Math.sin);
+
+var obj = { valueOf:(function valueOf() {
+    assertStackIs([valueOf, "bound(valueOf)", "bound(valueOf)", "bound(valueOf)",
+                   Math.sin, Array.prototype.sort, "global-code"]);
+}).bind().bind().bind() };
+[obj, obj].sort(Math.sin);
+
+/***********/
+
+for (var i = 0; i < 10; ++i) {
+    /* No loss for scripts. */
+    (function f() {
+        assertStackIs([f, Function.prototype.apply, 'global-code']);
+    }).apply(null, {});
+    (function f() {
+        assertStackIs([f, Function.prototype.call, 'global-code']);
+    }).call(null);
+
+    /* Loss for natives. */
+    (function f() {
+        var stack = dumpStack();
+        assertEq(stack[0], f);
+        if (stack.length === 4) {
+            assertEq(stack[1].name, 'f');
+            assertEq(stack[2], Function.prototype.call);
+        } else {
+            assertEq(stack.length, 3);
+            assertEq(stack[1], Function.prototype.call);
+        }
+    }).bind().call(null);
+    (function f() {
+        var stack = dumpStack();
+        assertEq(stack[0], f);
+        if (stack.length === 4) {
+            assertEq(stack[1].name, 'f');
+            assertEq(stack[2], Function.prototype.apply);
+        } else {
+            assertEq(stack.length, 3);
+            assertEq(stack[1], Function.prototype.apply);
+        }
+    }).bind().apply(null, {});
+    (function f() {
+        var stack = dumpStack();
+        assertEq(stack[0], f);
+        if (stack.length === 4) {
+            assertEq(stack[1].name, 'f');
+            assertEq(stack[2], Function.prototype.apply);
+        } else {
+            assertEq(stack.length, 3);
+            assertEq(stack[1], Function.prototype.apply);
+        }
+    }).bind().apply(null, [1,2,3,4,5]);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testStackIterDebug.js
@@ -0,0 +1,52 @@
+// |jit-test| mjitalways;debug
+
+function stackToString(stack) {
+    var str = "|";
+    for (var i = 0; i < stack.length; ++i) {
+        if (typeof stack[i] === "string")
+            str += stack[i];
+        else
+            str += stack[i].name;
+        str += "|";
+    }
+    return str;
+}
+
+function assertStackIs(s1) {
+    var s2 = dumpStack();
+    var me = s2.shift();
+    assertEq(me, assertStackIs);
+    try {
+        if (s1.length != s2.length)
+            throw "Length " + s1.length + " not equal " + s2.length;
+        for (var i = 0; i < s1.length; ++i) {
+            var match;
+            if (typeof s1[i] === "string" && (m = s1[i].match(/bound\((.*)\)/))) {
+                if (s2[i].name != m[1] || s2[i].toSource().indexOf("[native code]") < 0)
+                    throw "Element " + i + " not bound function";
+            } else if (s1[i] != s2[i]) {
+                throw "Element " + i + " not equal";
+            }
+        }
+    }
+    catch (e) {
+        print("Given     = " + stackToString(s1));
+        print("dumpStack = " + stackToString(s2));
+        throw e;
+    }
+}
+
+/*********************************************/
+
+(function f() { evalInFrame(0, "assertStackIs(['eval-code', evalInFrame, f, 'global-code'])"); })();
+(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])"); })() })();
+(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])"); })() }).bind()();
+
+(function f() { evalInFrame(0, "assertStackIs(['eval-code', evalInFrame, f, 'global-code'])", true); })();
+(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])", true); })() })();
+(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); })() }).bind()();
+(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); }).bind()() }).bind()();
+(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); }).bind().bind()() }).bind()();
+
+(function f() { var o = { toString:function() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])"); }}; [o,o].sort() })();
+(function f() { var o = { toString:function() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])", true); }}; [o,o].sort() })();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5196,36 +5196,30 @@ JS_IsRunning(JSContext *cx)
     JS_ASSERT_IF(JS_ON_TRACE(cx) && JS_TRACE_MONITOR_ON_TRACE(cx)->tracecx == cx, cx->hasfp());
 #endif
     StackFrame *fp = cx->maybefp();
     while (fp && fp->isDummyFrame())
         fp = fp->prev();
     return fp != NULL;
 }
 
-JS_PUBLIC_API(JSStackFrame *)
+JS_PUBLIC_API(JSBool)
 JS_SaveFrameChain(JSContext *cx)
 {
     CHECK_REQUEST(cx);
-    StackFrame *fp = js_GetTopStackFrame(cx);
-    if (!fp)
-        return NULL;
-    cx->stack.saveActiveSegment();
-    return Jsvalify(fp);
+    LeaveTrace(cx);
+    return cx->stack.saveFrameChain();
 }
 
 JS_PUBLIC_API(void)
-JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp)
+JS_RestoreFrameChain(JSContext *cx)
 {
     CHECK_REQUEST(cx);
     JS_ASSERT_NOT_ON_TRACE(cx);
-    JS_ASSERT(!cx->hasfp());
-    if (!fp)
-        return;
-    cx->stack.restoreSegment();
+    cx->stack.restoreFrameChain();
 }
 
 /************************************************************************/
 JS_PUBLIC_API(JSString *)
 JS_NewStringCopyN(JSContext *cx, const char *s, size_t n)
 {
     CHECK_REQUEST(cx);
     return js_NewStringCopyN(cx, s, n);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2938,25 +2938,23 @@ JS_IsRunning(JSContext *cx);
  * Saving and restoring frame chains.
  *
  * These two functions are used to set aside cx's call stack while that stack
  * is inactive. After a call to JS_SaveFrameChain, it looks as if there is no
  * code running on cx. Before calling JS_RestoreFrameChain, cx's call stack
  * must be balanced and all nested calls to JS_SaveFrameChain must have had
  * matching JS_RestoreFrameChain calls.
  *
- * JS_SaveFrameChain deals with cx not having any code running on it. A null
- * return does not signify an error, and JS_RestoreFrameChain handles a null
- * frame pointer argument safely.
+ * JS_SaveFrameChain deals with cx not having any code running on it.
  */
-extern JS_PUBLIC_API(JSStackFrame *)
+extern JS_PUBLIC_API(JSBool)
 JS_SaveFrameChain(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
-JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp);
+JS_RestoreFrameChain(JSContext *cx);
 
 /************************************************************************/
 
 /*
  * Strings.
  *
  * NB: JS_NewUCString takes ownership of bytes on success, avoiding a copy;
  * but on error (signified by null return), it leaves chars owned by the
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1535,17 +1535,18 @@ JSContext::wrapPendingException()
     clearPendingException();
     if (compartment->wrap(this, &v))
         setPendingException(v);
 }
 
 JSGenerator *
 JSContext::generatorFor(StackFrame *fp) const
 {
-    JS_ASSERT(stack.contains(fp) && fp->isGeneratorFrame());
+    JS_ASSERT(stack.containsSlow(fp));
+    JS_ASSERT(fp->isGeneratorFrame());
     JS_ASSERT(!fp->isFloatingGenerator());
     JS_ASSERT(!genStack.empty());
 
     if (JS_LIKELY(fp == genStack.back()->liveFrame()))
         return genStack.back();
 
     /* General case; should only be needed for debug APIs. */
     for (size_t i = 0; i < genStack.length(); ++i) {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1181,20 +1181,21 @@ struct JSContext
         return true;
     }
 
     /*
      * If there is no code on the stack, turn the override version into the
      * default version.
      */
     void maybeMigrateVersionOverride() {
-        if (JS_LIKELY(!isVersionOverridden() && stack.empty()))
-            return;
-        defaultVersion = versionOverride;
-        clearVersionOverride();
+        JS_ASSERT(stack.empty());
+        if (JS_UNLIKELY(isVersionOverridden())) {
+            defaultVersion = versionOverride;
+            clearVersionOverride();
+        }
     }
 
     /*
      * Return:
      * - The override version, if there is an override version.
      * - The newest scripted frame's version, if there is such a frame.
      * - The default verion.
      *
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -165,19 +165,19 @@ CompartmentHasLiveScripts(JSCompartment 
     // for debug asserts where such a race would be rare
     JSContext *iter = NULL;
     JSContext *icx;
     while ((icx = JS_ContextIterator(comp->rt, &iter))) {
 #if defined(JS_METHODJIT) && defined(JS_THREADSAFE)
         if (JS_GetContextThread(icx) != currentThreadId)
             continue;
 #endif
-        for (AllFramesIter i(icx); !i.done(); ++i) {
-            JSScript *script = i.fp()->maybeScript();
-            if (script && script->compartment == comp)
+        for (FrameRegsIter i(icx); !i.done(); ++i) {
+            JSScript *script = i.fp()->script();
+            if (script->compartment == comp)
                 return JS_TRUE;
         }
     }
 
     return JS_FALSE;
 }
 #endif
 
@@ -1486,32 +1486,32 @@ JS_GetFrameObject(JSContext *cx, JSStack
 {
     return &Valueify(fp)->scopeChain();
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fpArg)
 {
     StackFrame *fp = Valueify(fpArg);
-    JS_ASSERT(cx->stack.contains(fp));
+    JS_ASSERT(cx->stack.containsSlow(fp));
 
     js::AutoCompartment ac(cx, &fp->scopeChain());
     if (!ac.enter())
         return NULL;
 
     /* Force creation of argument and call objects if not yet created */
     (void) JS_GetFrameCallObject(cx, Jsvalify(fp));
     return GetScopeChain(cx, fp);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fpArg)
 {
     StackFrame *fp = Valueify(fpArg);
-    JS_ASSERT(cx->stack.contains(fp));
+    JS_ASSERT(cx->stack.containsSlow(fp));
 
     if (!fp->isFunctionFrame())
         return NULL;
 
     js::AutoCompartment ac(cx, &fp->scopeChain());
     if (!ac.enter())
         return NULL;
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -351,18 +351,17 @@ InitExnPrivate(JSContext *cx, JSObject *
             elem->argc = fp->numActualArgs();
             fp->forEachCanonicalActualArg(CopyTo(Valueify(values)));
             values += elem->argc;
         }
         elem->ulineno = 0;
         elem->filename = NULL;
         if (fp->isScriptFrame()) {
             elem->filename = fp->script()->filename;
-            if (fp->isScriptFrame())
-                elem->ulineno = js_FramePCToLineNumber(cx, fp, iter.pc());
+            elem->ulineno = js_FramePCToLineNumber(cx, fp, iter.pc());
         }
         ++elem;
     }
     JS_ASSERT(priv->stackElems + stackDepth == elem);
     JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values);
 
     exnObject->setPrivate(priv);
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -242,17 +242,17 @@ JSObject *
 js_GetArgsObject(JSContext *cx, StackFrame *fp)
 {
     /*
      * We must be in a function activation; the function must be lightweight
      * or else fp must have a variable object.
      */
     JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj());
 
-    while (fp->isDirectEvalOrDebuggerFrame())
+    while (fp->isEvalInFunction())
         fp = fp->prev();
 
     /* Create an arguments object for fp only if it lacks one. */
     if (fp->hasArgsObj())
         return &fp->argsObj();
 
     ArgumentsObject *argsobj =
         ArgumentsObject::create(cx, fp->numActualArgs(), fp->callee());
@@ -1583,22 +1583,19 @@ fun_getProperty(JSContext *cx, JSObject 
             return true;
         obj = obj->getProto();
         if (!obj)
             return true;
     }
     JSFunction *fun = obj->getFunctionPrivate();
 
     /* Find fun's top-most activation record. */
-    StackFrame *fp;
-    for (fp = js_GetTopStackFrame(cx);
-         fp && (fp->maybeFun() != fun || fp->isDirectEvalOrDebuggerFrame());
-         fp = fp->prev()) {
-        continue;
-    }
+    StackFrame *fp = js_GetTopStackFrame(cx);
+    while (fp && (fp->maybeFun() != fun || fp->isEvalInFunction()))
+        fp = fp->prev();
 
     switch (slot) {
       case FUN_ARGUMENTS:
         /* Warn if strict about f.arguments or equivalent unqualified uses. */
         if (!JS_ReportErrorFlagsAndNumber(cx,
                                           JSREPORT_WARNING | JSREPORT_STRICT,
                                           js_GetErrorMessage, NULL,
                                           JSMSG_DEPRECATED_USAGE,
@@ -3011,19 +3008,16 @@ js_ReportIsNotFunction(JSContext *cx, co
      * Conversely, values may have been popped from the stack in preparation
      * for a call (e.g., by SplatApplyArgs). Since we must pass an offset from
      * the top of the simulated stack to js_ReportValueError3, we do bounds
      * checking using the minimum of both the simulated and actual stack depth.
      */
     ptrdiff_t spindex = 0;
 
     FrameRegsIter i(cx);
-    while (!i.done() && !i.pc())
-        ++i;
-
     if (!i.done()) {
         uintN depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
         Value *simsp = i.fp()->base() + depth;
         if (i.fp()->base() <= vp && vp < Min(simsp, i.sp()))
             spindex = vp - simsp;
     }
 
     if (!spindex)
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -212,16 +212,21 @@ struct JSFunction : public JSObject_Slot
         JS_ASSERT(joinable());
         getSlotRef(METHOD_ATOM_SLOT).setString(atom);
     }
 
     js::Native maybeNative() const {
         return isInterpreted() ? NULL : u.n.native;
     }
 
+    js::Native native() const {
+        JS_ASSERT(isNative());
+        return u.n.native;
+    }
+
     JSScript *script() const {
         JS_ASSERT(isInterpreted());
         return u.i.script;
     }
 
     static uintN offsetOfNativeOrScript() {
         JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.script));
         JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, nativeOrScript));
@@ -295,16 +300,29 @@ IsFunctionObject(const js::Value &v, JSF
 {
     JSObject *funobj;
     bool b = IsFunctionObject(v, &funobj);
     if (b)
         *fun = funobj->getFunctionPrivate();
     return b;
 }
 
+static JS_ALWAYS_INLINE bool
+IsNativeFunction(const js::Value &v)
+{
+    JSFunction *fun;
+    return IsFunctionObject(v, &fun) && fun->isNative();
+}
+
+static JS_ALWAYS_INLINE bool
+IsNativeFunction(const js::Value &v, JSFunction **fun)
+{
+    return IsFunctionObject(v, fun) && (*fun)->isNative();
+}
+
 extern JS_ALWAYS_INLINE bool
 SameTraceType(const Value &lhs, const Value &rhs)
 {
     return SameType(lhs, rhs) &&
            (lhs.isPrimitive() ||
             lhs.toObject().isFunction() == rhs.toObject().isFunction());
 }
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -666,17 +666,17 @@ Invoke(JSContext *cx, const CallArgs &ar
         } else {
             args.rval().setUndefined();
         }
         return true;
     }
 
     /* Get pointer to new frame/slots, prepare arguments. */
     InvokeFrameGuard ifg;
-    if (!cx->stack.pushInvokeFrame(cx, args, construct, callee, fun, script, &ifg))
+    if (!cx->stack.pushInvokeFrame(cx, args, construct, &ifg))
         return false;
 
     /* Now that the new frame is rooted, maybe create a call object. */
     StackFrame *fp = ifg.fp();
     if (fun->isHeavyweight() && !CreateFunCallObject(cx, fp))
         return false;
 
     /* Run function until JSOP_STOP, JSOP_RETURN or error. */
@@ -726,17 +726,17 @@ InvokeSessionGuard::start(JSContext *cx,
         /*
          * The frame will remain pushed even when the callee isn't active which
          * will affect the observable current global, so avoid any change.
          */
         if (callee.getGlobal() != GetGlobalForScopeChain(cx))
             break;
 
         /* Push the stack frame once for the session. */
-        if (!stack.pushInvokeFrame(cx, args_, NO_CONSTRUCT, callee, fun, script_, &ifg_))
+        if (!stack.pushInvokeFrame(cx, args_, NO_CONSTRUCT, &ifg_))
             return false;
 
         StackFrame *fp = ifg_.fp();
 #ifdef JS_METHODJIT
         /* Hoist dynamic checks from RunScript. */
         mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp, mjit::CompileRequest_JIT);
         if (status == mjit::Compile_Error)
             return false;
@@ -853,17 +853,17 @@ InitSharpSlots(JSContext *cx, StackFrame
 {
     StackFrame *prev = fp->prev();
     JSScript *script = fp->script();
     JS_ASSERT(script->nfixed >= SHARP_NSLOTS);
 
     Value *sharps = &fp->slots()[script->nfixed - SHARP_NSLOTS];
     if (!fp->isGlobalFrame() && prev->script()->hasSharps) {
         JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
-        int base = (prev->isFunctionFrame() && !prev->isDirectEvalOrDebuggerFrame())
+        int base = prev->isNonEvalFunctionFrame()
                    ? prev->fun()->script()->bindings.sharpSlotBase(cx)
                    : prev->numFixed() - SHARP_NSLOTS;
         if (base < 0)
             return false;
         sharps[0] = prev->slots()[base];
         sharps[1] = prev->slots()[base + 1];
     } else {
         sharps[0].setUndefined();
@@ -1403,17 +1403,17 @@ js_DoIncDec(JSContext *cx, const JSCodeS
 
 const Value &
 js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
 {
     JS_ASSERT(closureLevel >= cookie.level() && cookie.level() > 0);
     const uintN targetLevel = closureLevel - cookie.level();
     JS_ASSERT(targetLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
 
-    StackFrame *fp = cx->stack.findFrameAtLevel(targetLevel);
+    StackFrame *fp = FindUpvarFrame(cx, targetLevel);
     uintN slot = cookie.slot();
     const Value *vp;
 
     if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
         vp = fp->slots() + fp->numFixed();
     } else if (slot < fp->numFormalArgs()) {
         vp = fp->formalArgs();
     } else if (slot == UpvarCookie::CALLEE_SLOT) {
@@ -1423,16 +1423,29 @@ js::GetUpvar(JSContext *cx, uintN closur
         slot -= fp->numFormalArgs();
         JS_ASSERT(slot < fp->numSlots());
         vp = fp->slots();
     }
 
     return vp[slot];
 }
 
+extern StackFrame *
+js::FindUpvarFrame(JSContext *cx, uintN targetLevel)
+{
+    StackFrame *fp = cx->fp();
+    while (true) {
+        JS_ASSERT(fp && fp->isScriptFrame());
+        if (fp->script()->staticLevel == targetLevel)
+            break;
+        fp = fp->prev();
+    }
+    return fp;
+}
+
 #ifdef DEBUG
 
 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
 js_LogOpcode(JSContext *cx)
 {
     FILE *logfp;
     StackFrame *fp;
     FrameRegs *regs;
@@ -2805,17 +2818,17 @@ BEGIN_CASE(JSOP_STOP)
         JS_ASSERT(!js_IsActiveWithOrBlock(cx, &regs.fp()->scopeChain(), 0));
         interpReturnOK = ScriptEpilogue(cx, regs.fp(), interpReturnOK);
         CHECK_INTERRUPT_HANDLER();
 
         /* The JIT inlines ScriptEpilogue. */
 #ifdef JS_METHODJIT
   jit_return:
 #endif
-        cx->stack.popInlineFrame();
+        cx->stack.popInlineFrame(regs);
 
         /* Sync interpreter locals. */
         script = regs.fp()->script();
         argv = regs.fp()->maybeFormalArgs();
         atoms = FrameAtomBase(cx, regs.fp());
 
         /* Resume execution in the calling frame. */
         RESET_USE_METHODJIT();
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -147,20 +147,36 @@ ComputeThis(JSContext *cx, StackFrame *f
 /*
  * 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
+extern bool
 Invoke(JSContext *cx, const CallArgs &args, MaybeConstruct construct = NO_CONSTRUCT);
 
 /*
+ * For calls to natives, the InvokeArgsGuard object provides a record of the
+ * call for the debugger's callstack. For this to work, the InvokeArgsGuard
+ * record needs to know when the call is actually active (because the
+ * InvokeArgsGuard can be pushed long before and popped long after the actual
+ * call, during which time many stack-observing things can happen).
+ */
+inline bool
+Invoke(JSContext *cx, InvokeArgsGuard &args, MaybeConstruct construct = NO_CONSTRUCT)
+{
+    args.setActive();
+    bool ok = Invoke(cx, ImplicitCast<CallArgs>(args), construct);
+    args.setInactive();
+    return ok;
+}
+
+/*
  * Natives like sort/forEach/replace call Invoke repeatedly with the same
  * callee, this, and number of arguments. To optimize this, such natives can
  * start an "invoke session" to factor out much of the dynamic setup logic
  * required by a normal Invoke. Usage is:
  *
  *   InvokeSessionGuard session(cx);
  *   if (!session.start(cx, callee, thisp, argc, &session))
  *     ...
@@ -269,18 +285,22 @@ ValueToId(JSContext *cx, const Value &v,
 
 /*
  * @param closureLevel      The static level of the closure that the cookie
  *                          pertains to.
  * @param cookie            Level amount is a "skip" (delta) value from the
  *                          closure level.
  * @return  The value of the upvar.
  */
-extern const js::Value &
-GetUpvar(JSContext *cx, uintN level, js::UpvarCookie cookie);
+extern const Value &
+GetUpvar(JSContext *cx, uintN level, UpvarCookie cookie);
+
+/* Search the call stack for the nearest frame with static level targetLevel. */
+extern StackFrame *
+FindUpvarFrame(JSContext *cx, uintN targetLevel);
 
 } /* namespace js */
 
 /*
  * JS_LONE_INTERPRET indicates that the compiler should see just the code for
  * the js_Interpret function when compiling jsinterp.cpp. The rest of the code
  * from the file should be visible only when compiling jsinvoke.cpp. It allows
  * platform builds to optimize selectively js_Interpret when the granularity
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -84,17 +84,17 @@ class InvokeSessionGuard
 
     bool optimized() const { return ifg_.pushed(); }
 
   public:
     InvokeSessionGuard() : args_(), ifg_() {}
     ~InvokeSessionGuard() {}
 
     bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc);
-    bool invoke(JSContext *cx) const;
+    bool invoke(JSContext *cx);
 
     bool started() const {
         return args_.pushed();
     }
 
     Value &operator[](unsigned i) const {
         JS_ASSERT(i < argc());
         Value &arg = i < nformals_ ? formals_[i] : actuals_[i];
@@ -108,17 +108,17 @@ class InvokeSessionGuard
     }
 
     const Value &rval() const {
         return optimized() ? ifg_.fp()->returnValue() : args_.rval();
     }
 };
 
 inline bool
-InvokeSessionGuard::invoke(JSContext *cx) const
+InvokeSessionGuard::invoke(JSContext *cx)
 {
     /* N.B. Must be kept in sync with Invoke */
 
     /* Refer to canonical (callee, this) for optimized() sessions. */
     formals_[-2] = savedCallee_;
     formals_[-1] = savedThis_;
 
     /* Prevent spurious accessing-callee-after-rval assert. */
@@ -134,25 +134,27 @@ InvokeSessionGuard::invoke(JSContext *cx
 
     /* Clear any garbage left from the last Invoke. */
     StackFrame *fp = ifg_.fp();
     fp->resetCallFrame(script_);
 
     JSBool ok;
     {
         AutoPreserveEnumerators preserve(cx);
+        args_.setActive();  /* From js::Invoke(InvokeArgsGuard) overload. */
         Probes::enterJSFun(cx, fp->fun(), script_);
 #ifdef JS_METHODJIT
         ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_);
         cx->regs().pc = stop_;
 #else
         cx->regs().pc = script_->code;
         ok = Interpret(cx, cx->fp());
 #endif
         Probes::exitJSFun(cx, fp->fun(), script_);
+        args_.setInactive();
     }
 
     /* Don't clobber callee with rval; rval gets read from fp->rval. */
     return ok;
 }
 
 namespace detail {
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1176,17 +1176,17 @@ js_NewGenerator(JSContext *cx)
 
     /* Initialize JSGenerator. */
     gen->obj = obj;
     gen->state = JSGEN_NEWBORN;
     gen->enumerators = NULL;
     gen->floating = genfp;
 
     /* Copy from the stack to the generator's floating frame. */
-    gen->regs.rebaseFromTo(stackRegs, genfp);
+    gen->regs.rebaseFromTo(stackRegs, *genfp);
     genfp->stealFrameAndSlots(genvp, stackfp, stackvp, stackRegs.sp);
     genfp->initFloatingGenerator();
 
     obj->setPrivate(gen);
     return obj;
 }
 
 JSGenerator *
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -209,17 +209,16 @@ js_NewGenerator(JSContext *cx);
  * since it is stable, and maintain, in the generator object, a pointer to the
  * "live" stack frame (either a copy on the stack or the floating frame). Thus,
  * Block and With objects must "normalize" to and from the floating/live frames
  * in the case of generators using the following functions.
  */
 inline js::StackFrame *
 js_FloatingFrameIfGenerator(JSContext *cx, js::StackFrame *fp)
 {
-    JS_ASSERT(cx->stack.contains(fp));
     if (JS_UNLIKELY(fp->isGeneratorFrame()))
         return cx->generatorFor(fp)->floatingFrame();
     return fp;
 }
 
 /* Given a floating frame, given the JSGenerator containing it. */
 extern JSGenerator *
 js_FloatingFrameToGenerator(js::StackFrame *fp);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1736,43 +1736,43 @@ enum MatchControlFlags {
 
    MATCH_ARGS    = TEST_GLOBAL_BIT,
    MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
    REPLACE_ARGS  = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
 };
 
 /* Factor out looping and matching logic. */
 static bool
-DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep,
-        DoMatchCallback callback, void *data, MatchControlFlags flags)
+DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &rep,
+        DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
 {
     RegExp &re = rep.re();
     if (re.global()) {
         /* global matching ('g') */
         bool testGlobal = flags & TEST_GLOBAL_BIT;
         if (rep.reobj())
             rep.reobj()->zeroRegExpLastIndex();
         for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
-            if (!re.execute(cx, res, str, &i, testGlobal, vp))
+            if (!re.execute(cx, res, str, &i, testGlobal, rval))
                 return false;
-            if (!Matched(testGlobal, *vp))
+            if (!Matched(testGlobal, *rval))
                 break;
             if (!callback(cx, res, count, data))
                 return false;
             if (!res->matched())
                 ++i;
         }
     } else {
         /* single match */
         bool testSingle = !!(flags & TEST_SINGLE_BIT),
              callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
         size_t i = 0;
-        if (!re.execute(cx, res, str, &i, testSingle, vp))
+        if (!re.execute(cx, res, str, &i, testSingle, rval))
             return false;
-        if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, res, 0, data))
+        if (callbackOnSingle && Matched(testSingle, *rval) && !callback(cx, res, 0, data))
             return false;
     }
     return true;
 }
 
 static bool
 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp)
 {
@@ -1837,22 +1837,24 @@ str_match(JSContext *cx, uintN argc, Val
 
     const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
     if (!rep)
         return false;
 
     AutoObjectRooter array(cx);
     MatchArgType arg = array.addr();
     RegExpStatics *res = cx->regExpStatics();
-    if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
+    Value rval;
+    if (!DoMatch(cx, res, str, *rep, MatchCallback, arg, MATCH_ARGS, &rval))
         return false;
 
-    /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
     if (rep->re().global())
         vp->setObjectOrNull(array.object());
+    else
+        *vp = rval;
     return true;
 }
 
 static JSBool
 str_search(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *str = ThisToStringForStringProto(cx, vp);
     if (!str)
@@ -2318,17 +2320,18 @@ str_replace_regexp(JSContext *cx, uintN 
     const RegExpPair *rep = rdata.g.normalizeRegExp(true, 2, argc, vp);
     if (!rep)
         return false;
 
     rdata.leftIndex = 0;
     rdata.calledBack = false;
 
     RegExpStatics *res = cx->regExpStatics();
-    if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS))
+    Value tmp;
+    if (!DoMatch(cx, res, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
         return false;
 
     if (!rdata.calledBack) {
         /* Didn't match, so the string is unmodified. */
         vp->setString(rdata.str);
         return true;
     }
 
@@ -2844,33 +2847,28 @@ out:
  */
 static JSBool
 str_concat(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *str = ThisToStringForStringProto(cx, vp);
     if (!str)
         return false;
 
-    /* Set vp (aka rval) early to handle the argc == 0 case. */
-    vp->setString(str);
-
-    Value *argv;
-    uintN i;
-    for (i = 0, argv = vp + 2; i < argc; i++) {
+    Value *argv = JS_ARGV(cx, vp);
+    for (uintN i = 0; i < argc; i++) {
         JSString *str2 = js_ValueToString(cx, argv[i]);
         if (!str2)
             return false;
-        argv[i].setString(str2);
 
         str = js_ConcatStrings(cx, str, str2);
         if (!str)
             return false;
-        vp->setString(str);
     }
 
+    JS_SET_RVAL(cx, vp, StringValue(str));
     return true;
 }
 
 static JSBool
 str_slice(JSContext *cx, uintN argc, Value *vp)
 {
     if (argc == 1 && vp[1].isString() && vp[2].isInt32()) {
         size_t begin, end, length;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -310,16 +310,17 @@ nanojit::LInsPrinter::accNames[] = {
     "tdata",        // (1 << 20) == ACCSET_TARRAY_DATA
     "iter",         // (1 << 21) == ACCSET_ITER
     "iterprops",    // (1 << 22) == ACCSET_ITER_PROPS
     "str",          // (1 << 23) == ACCSET_STRING
     "strmchars",    // (1 << 24) == ACCSET_STRING_MCHARS
     "typemap",      // (1 << 25) == ACCSET_TYPEMAP
     "fcslots",      // (1 << 26) == ACCSET_FCSLOTS
     "argsdata",     // (1 << 27) == ACCSET_ARGS_DATA
+    "seg",          // (1 << 28) == ACCSET_SEG
 
     "?!"            // this entry should never be used, have it just in case
 };
 
 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(nanojit::LInsPrinter::accNames) == TM_NUM_USED_ACCS + 1);
 #endif
 
 } /* namespace nanojit */
@@ -377,16 +378,23 @@ ValueToTypeChar(const Value &v)
     if (v.isString()) return 'S';
     if (v.isObject()) return v.toObject().isFunction() ? 'F' : 'O';
     if (v.isBoolean()) return 'B';
     if (v.isNull()) return 'N';
     if (v.isUndefined()) return 'U';
     if (v.isMagic()) return 'M';
     return '?';
 }
+
+static inline uintN
+FramePCOffset(JSContext *cx, js::StackFrame* fp)
+{
+    jsbytecode *pc = fp->pcQuadratic(cx);
+    return uintN(pc - fp->script()->code);
+}
 #endif
 
 static inline uintN
 CurrentPCOffset(JSContext *cx)
 {
     StackFrame *fp = cx->fp();
     jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : cx->regs().pc;
     return uintN(pc - fp->script()->code);
@@ -3304,17 +3312,17 @@ GetUpvarOnTrace(JSContext* cx, uint32 up
         return state->callstackBase[0]->get_typemap()[native_slot];
     }
 
     /*
      * If we did not find the upvar in the frames for the active traces,
      * then we simply get the value from the interpreter state.
      */
     JS_ASSERT(upvarLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
-    StackFrame* fp = cx->stack.findFrameAtLevel(upvarLevel);
+    StackFrame* fp = FindUpvarFrame(cx, upvarLevel);
     Value v = T::interp_get(fp, slot);
     JSValueType type = getCoercedType(v);
     ValueToNative(v, type, result);
     return type;
 }
 
 // For this traits type, 'slot' is the argument index, which may be -2 for callee.
 struct UpvarArgTraits {
@@ -13443,17 +13451,17 @@ TraceRecorder::upvar(JSScript* script, J
         return ins;
 
     /*
      * The upvar is not in the current trace, so get the upvar value exactly as
      * the interpreter does and unbox.
      */
     uint32 level = script->staticLevel - cookie.level();
     uint32 cookieSlot = cookie.slot();
-    StackFrame* fp = cx->stack.findFrameAtLevel(level);
+    StackFrame* fp = FindUpvarFrame(cx, level);
     const CallInfo* ci;
     int32 slot;
     if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
         ci = &GetUpvarStackOnTrace_ci;
         slot = cookieSlot;
     } else if (cookieSlot < fp->numFormalArgs()) {
         ci = &GetUpvarArgOnTrace_ci;
         slot = cookieSlot;
@@ -15081,35 +15089,23 @@ TraceRecorder::record_JSOP_BINDNAME()
 {
     TraceMonitor *localtm = traceMonitor;
     StackFrame* const fp = cx->fp();
     JSObject *obj;
 
     if (!fp->isFunctionFrame()) {
         obj = &fp->scopeChain();
 
-#ifdef DEBUG
-        StackFrame *fp2 = fp;
-#endif
-
         /*
          * In global code, fp->scopeChain can only contain blocks whose values
          * are still on the stack.  We never use BINDNAME to refer to these.
          */
         while (obj->isBlock()) {
             // The block's values are still on the stack.
-#ifdef DEBUG
-            // NB: fp2 can't be a generator frame, because !fp->hasFunction.
-            while (obj->getPrivate() != fp2) {
-                JS_ASSERT(fp2->isDirectEvalOrDebuggerFrame());
-                fp2 = fp2->prev();
-                if (!fp2)
-                    JS_NOT_REACHED("bad stack frame");
-            }
-#endif
+            JS_ASSERT(obj->getPrivate() == fp);
             obj = obj->getParent();
             // Blocks always have parents.
             JS_ASSERT(obj);
         }
 
         /*
          * If this is a strict mode eval frame, we will have a Call object for
          * it. For now just don't trace this case.
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1731,21 +1731,19 @@ ParseXMLSource(JSContext *cx, JSString *
     js_strncpy(chars + offset, srcp, srclen);
     offset += srclen;
     dstlen = length - offset + 1;
     InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, &dstlen);
     chars [offset + dstlen] = 0;
 
     LeaveTrace(cx);
     xml = NULL;
-    FrameRegsIter i(cx);
-    for (; !i.done() && !i.pc(); ++i)
-        JS_ASSERT(!i.fp()->isScriptFrame());
     filename = NULL;
     lineno = 1;
+    FrameRegsIter i(cx);
     if (!i.done()) {
         op = (JSOp) *i.pc();
         if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
             filename = i.fp()->script()->filename;
             lineno = js_FramePCToLineNumber(cx, i.fp(), i.pc());
             for (endp = srcp + srclen; srcp < endp; srcp++) {
                 if (*srcp == '\n')
                     --lineno;
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -173,17 +173,17 @@ top:
 /*
  * Clean up a frame and return.
  */
 static void
 InlineReturn(VMFrame &f)
 {
     JS_ASSERT(f.fp() != f.entryfp);
     JS_ASSERT(!js_IsActiveWithOrBlock(f.cx, &f.fp()->scopeChain(), 0));
-    f.cx->stack.popInlineFrame();
+    f.cx->stack.popInlineFrame(f.regs);
 }
 
 void JS_FASTCALL
 stubs::SlowCall(VMFrame &f, uint32 argc)
 {
     if (!Invoke(f.cx, CallArgsFromSp(argc, f.regs.sp)))
         THROW();
 }
@@ -191,40 +191,29 @@ stubs::SlowCall(VMFrame &f, uint32 argc)
 void JS_FASTCALL
 stubs::SlowNew(VMFrame &f, uint32 argc)
 {
     if (!InvokeConstructor(f.cx, CallArgsFromSp(argc, f.regs.sp)))
         THROW();
 }
 
 /*
- * This function must only be called after the early prologue, since it depends
- * on fp->exec.fun.
- */
-static inline void
-RemovePartialFrame(JSContext *cx, StackFrame *fp)
-{
-    cx->stack.popInlineFrame();
-}
-
-/*
  * HitStackQuota is called after the early prologue pushing the new frame would
  * overflow f.stackLimit.
  */
 void JS_FASTCALL
 stubs::HitStackQuota(VMFrame &f)
 {
     /* Include space to push another frame. */
     uintN nvals = f.fp()->script()->nslots + VALUES_PER_STACK_FRAME;
     JS_ASSERT(f.regs.sp == f.fp()->base());
     if (f.cx->stack.space().tryBumpLimit(NULL, f.regs.sp, nvals, &f.stackLimit))
         return;
 
-    /* Remove the current partially-constructed frame before throwing. */
-    RemovePartialFrame(f.cx, f.fp());
+    f.cx->stack.popFrameAfterOverflow();
     js_ReportOverRecursed(f.cx);
     THROW();
 }
 
 /*
  * This function must only be called after the early prologue, since it depends
  * on fp->exec.fun.
  */
@@ -280,33 +269,30 @@ stubs::CompileFunction(VMFrame &f, uint3
     /*
      * Since we can only use members set by initJitFrameCallerHalf,
      * we must carefully extract the callee from the nactual.
      */
     JSObject &callee = fp->formalArgsEnd()[-(int(nactual) + 2)].toObject();
     JSFunction *fun = callee.getFunctionPrivate();
     JSScript *script = fun->script();
 
-    /*
-     * FixupArity/RemovePartialFrame expect to be called after the early
-     * prologue.
-     */
+    /* FixupArity expect to be called after the early prologue. */
     fp->initJitFrameEarlyPrologue(fun, nactual);
 
     if (nactual != fp->numFormalArgs()) {
         fp = (StackFrame *)FixupArity(f, nactual);
         if (!fp)
             return NULL;
     }
 
     /* Finish frame initialization. */
     fp->initJitFrameLatePrologue();
 
     /* These would have been initialized by the prologue. */
-    f.regs.prepareToRun(fp, script);
+    f.regs.prepareToRun(*fp, script);
 
     if (fun->isHeavyweight() && !js::CreateFunCallObject(cx, fp))
         THROWV(NULL);
 
     CompileStatus status = CanMethodJIT(cx, script, fp, CompileRequest_JIT);
     if (status == Compile_Okay)
         return script->getJIT(fp->isConstructing())->invokeEntry;
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2825,16 +2825,62 @@ DumpObject(JSContext *cx, uintN argc, js
     js_DumpObject(arg0);
 
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return JS_TRUE;
 }
 
 #endif /* DEBUG */
 
+/*
+ * This shell function is temporary (used by testStackIter.js) and should be
+ * removed once JSD2 lands wholly subsumes the functionality here.
+ */
+JSBool
+DumpStack(JSContext *cx, uintN argc, Value *vp)
+{
+    JSObject *arr = JS_NewArrayObject(cx, 0, NULL);
+    if (!arr)
+        return false;
+
+    JSString *evalStr = JS_NewStringCopyZ(cx, "eval-code");
+    if (!evalStr)
+        return false;
+
+    JSString *globalStr = JS_NewStringCopyZ(cx, "global-code");
+    if (!globalStr)
+        return false;
+
+    StackIter iter(cx);
+    JS_ASSERT(iter.nativeArgs().callee().getFunctionPrivate()->native() == DumpStack);
+    ++iter;
+
+    jsint index = 0;
+    for (; !iter.done(); ++index, ++iter) {
+        Value v;
+        if (iter.isScript()) {
+            if (iter.fp()->isNonEvalFunctionFrame()) {
+                if (!iter.fp()->getValidCalleeObject(cx, &v))
+                    return false;
+            } else if (iter.fp()->isEvalFrame()) {
+                v = StringValue(evalStr);
+            } else {
+                v = StringValue(globalStr);
+            }
+        } else {
+            v = iter.nativeArgs().calleev();
+        }
+        if (!JS_SetElement(cx, arr, index, Jsvalify(&v)))
+            return false;
+    }
+
+    JS_SET_RVAL(cx, vp, ObjectValue(*arr));
+    return true;
+}
+
 #ifdef TEST_CVTARGS
 #include <ctype.h>
 
 static const char *
 EscapeWideString(jschar *w)
 {
     static char enuf[80];
     static char hex[] = "0123456789abcdef";
@@ -3731,33 +3777,33 @@ EvalInFrame(JSContext *cx, uintN argc, j
     }
 
     StackFrame *const fp = fi.fp();
     if (!fp->isScriptFrame()) {
         JS_ReportError(cx, "cannot eval in non-script frame");
         return JS_FALSE;
     }
 
-    JSStackFrame *oldfp = NULL;
+    JSBool saved = JS_FALSE;;
     if (saveCurrent)
-        oldfp = JS_SaveFrameChain(cx);
+        saved = JS_SaveFrameChain(cx);
 
     size_t length;
     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
     if (!chars)
         return JS_FALSE;
 
     JSBool ok = JS_EvaluateUCInStackFrame(cx, Jsvalify(fp), chars, length,
                                           fp->script()->filename,
                                           JS_PCToLineNumber(cx, fp->script(),
                                                             fi.pc()),
                                           vp);
 
-    if (saveCurrent)
-        JS_RestoreFrameChain(cx, oldfp);
+    if (saved)
+        JS_RestoreFrameChain(cx);
 
     return ok;
 }
 
 static JSBool
 ShapeOf(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval v;
@@ -4842,16 +4888,17 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("disfile",        DisassFile,     1,0),
     JS_FN("dissrc",         DisassWithSrc,  1,0),
     JS_FN("dumpHeap",       DumpHeap,       0,0),
     JS_FN("dumpObject",     DumpObject,     1,0),
     JS_FN("notes",          Notes,          1,0),
     JS_FN("tracing",        Tracing,        0,0),
     JS_FN("stats",          DumpStats,      1,0),
 #endif
+    JS_FN("dumpStack",      DumpStack,      1,0),
 #ifdef TEST_CVTARGS
     JS_FN("cvtargs",        ConvertArgs,    0,0),
 #endif
     JS_FN("build",          BuildDate,      0,0),
     JS_FN("clear",          Clear,          0,0),
     JS_FN("intern",         Intern,         1,0),
     JS_FN("clone",          Clone,          1,0),
     JS_FN("getpda",         GetPDA,         1,0),
@@ -4975,16 +5022,17 @@ static const char *const shell_help_mess
 "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
 "  Interface to JS_DumpHeap with output sent to file",
 "dumpObject()             Dump an internal representation of an object",
 "notes([fun])             Show source notes for functions",
 "tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
 "                         With filename, send to file.",
 "stats([string ...])      Dump 'arena', 'atom', 'global' stats",
 #endif
+"dumpStack()              Dump the stack as an array of callees (youngest first)",
 #ifdef TEST_CVTARGS
 "cvtargs(arg1..., arg12)  Test argument formatter",
 #endif
 "build()                  Show build date and time",
 "clear([obj])             Clear properties of object",
 "intern(str)              Internalize str in the atom table",
 "clone(fun[, scope])      Clone function object",
 "getpda(obj)              Get the property descriptors for obj",
--- a/js/src/tracejit/Writer.cpp
+++ b/js/src/tracejit/Writer.cpp
@@ -371,17 +371,17 @@ void ValidateWriter::checkAccSet(LOpcode
               base->oprnd1()->isop(LIR_allocp));
         break;
 
       case ACCSET_FRAMEREGS:
         // base = ldp.cx ...[offsetof(JSContext, regs)]
         // ins  = ldp.regs base[<disp within FrameRegs>]
         ok = op == LIR_ldp &&
              dispWithin(FrameRegs) && 
-             match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, stack) + ContextStack::offsetOfRegs());
+             match(base, LIR_ldp, ACCSET_SEG, StackSegment::offsetOfRegs());
         break;
 
       case ACCSET_STACKFRAME:
         // base = ldp.regs ...[offsetof(FrameRegs, fp)]
         // ins  = {ld,st}X.sf base[<disp within StackFrame>]
         ok = dispWithin(StackFrame) && 
              match(base, LIR_ldp, ACCSET_FRAMEREGS, FrameRegs::offsetOfFp);
         break;
@@ -547,16 +547,22 @@ void ValidateWriter::checkAccSet(LOpcode
         // base_oprnd1 = <const private ptr slots[JSSLOT_ARGS_DATA]>
         // base        = addp base_oprnd1, ...
         // ins         = {ld,st}X.argsdata base[...]
         ok = (isConstPrivatePtr(base, ArgumentsObject::DATA_SLOT) ||
               (base->isop(LIR_addp) &&
                isConstPrivatePtr(base->oprnd1(), ArgumentsObject::DATA_SLOT)));
         break;
 
+      case ACCSET_SEG:
+        // Match the ACCSET_SEG load that comes out of ldpContextRegs
+        ok = dispWithin(StackSegment) &&
+             match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, stack) + ContextStack::offsetOfSeg());
+        break;
+
       default:
         // This assertion will fail if any single-region AccSets aren't covered
         // by the switch -- only multi-region AccSets should be handled here.
         JS_ASSERT(!isSingletonAccSet(accSet));
         ok = true;
         break;
     }
 
--- a/js/src/tracejit/Writer.h
+++ b/js/src/tracejit/Writer.h
@@ -121,16 +121,17 @@ enum LC_TMBits {
  * - ACCSET_TARRAY_DATA:   All TypedArray data arrays.
  * - ACCSET_ITER:          All NativeIterator structs.
  * - ACCSET_ITER_PROPS:    The props_arrays of all NativeIterator structs.
  * - ACCSET_STRING:        All JSString structs.
  * - ACCSET_STRING_MCHARS: All JSString mchars arrays.
  * - ACCSET_TYPEMAP:       All typemaps form a single region.
  * - ACCSET_FCSLOTS:       All fcslots arrays form a single region.
  * - ACCSET_ARGS_DATA:     All Arguments data arrays form a single region.
+ * - ACCSET_SEG:           All StackSegment structs.
  */
 static const nanojit::AccSet ACCSET_STATE         = (1 <<  0);
 static const nanojit::AccSet ACCSET_STACK         = (1 <<  1);
 static const nanojit::AccSet ACCSET_RSTACK        = (1 <<  2);
 static const nanojit::AccSet ACCSET_CX            = (1 <<  3);
 static const nanojit::AccSet ACCSET_TM            = (1 <<  4);
 static const nanojit::AccSet ACCSET_EOS           = (1 <<  5);
 static const nanojit::AccSet ACCSET_ALLOC         = (1 <<  6);
@@ -153,18 +154,19 @@ static const nanojit::AccSet ACCSET_TARR
 static const nanojit::AccSet ACCSET_TARRAY_DATA   = (1 << 20);
 static const nanojit::AccSet ACCSET_ITER          = (1 << 21);
 static const nanojit::AccSet ACCSET_ITER_PROPS    = (1 << 22);
 static const nanojit::AccSet ACCSET_STRING        = (1 << 23);
 static const nanojit::AccSet ACCSET_STRING_MCHARS = (1 << 24);
 static const nanojit::AccSet ACCSET_TYPEMAP       = (1 << 25);
 static const nanojit::AccSet ACCSET_FCSLOTS       = (1 << 26);
 static const nanojit::AccSet ACCSET_ARGS_DATA     = (1 << 27);
+static const nanojit::AccSet ACCSET_SEG           = (1 << 28);
 
-static const uint8_t TM_NUM_USED_ACCS = 28; // number of access regions used by TraceMonkey
+static const uint8_t TM_NUM_USED_ACCS = 29; // number of access regions used by TraceMonkey
 
 /*
  * An Address describes everything about a loaded/stored memory location.  One
  * only be created via the sub-classes below and only accessed via class
  * Writer;  this is so that AccSets are encapsulated as much as possible.
  */
 struct Address
 {
@@ -422,18 +424,21 @@ class Writer
     }
     #define ldpContextField(fieldname) \
         name(w.ldpContextFieldHelper(cx_ins, offsetof(JSContext, fieldname), LOAD_NORMAL), \
              #fieldname)
     #define ldpConstContextField(fieldname) \
         name(w.ldpContextFieldHelper(cx_ins, offsetof(JSContext, fieldname), LOAD_CONST), \
              #fieldname)
     nj::LIns *ldpContextRegs(nj::LIns *cx) const {
-        int32 offset = offsetof(JSContext, stack) + ContextStack::offsetOfRegs();
-        return name(ldpContextFieldHelper(cx, offset, nj::LOAD_NORMAL),"regs");
+        int32 segOff = offsetof(JSContext, stack) + ContextStack::offsetOfSeg();
+        nj::LIns *seg = ldpContextFieldHelper(cx, segOff, nj::LOAD_CONST);
+        int32 regsOff = StackSegment::offsetOfRegs();
+        return name(lir->insLoad(nj::LIR_ldp, seg, regsOff, ACCSET_SEG, nj::LOAD_CONST), "cx->regs()");
+
     }
 
     nj::LIns *stContextField(nj::LIns *value, nj::LIns *cx, int32 offset) const {
         return lir->insStore(value, cx, offset, ACCSET_CX);
     }
     #define stContextField(value, fieldname) \
         stContextField((value), cx_ins, offsetof(JSContext, fieldname))
 
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -45,225 +45,16 @@
 #include "jscompartment.h"
 
 #include "Stack.h"
 
 #include "ArgumentsObject-inl.h"
 
 namespace js {
 
-/*****************************************************************************/
-
-/* See VM stack layout comment in Stack.h. */
-class StackSegment
-{
-    /* The context to which this segment belongs. */
-    ContextStack        *stack_;
-
-    /* Link for JSContext segment stack mentioned in big comment above. */
-    StackSegment        *previousInContext_;
-
-    /* Link for StackSpace segment stack mentioned in StackSpace comment. */
-    StackSegment        *previousInMemory_;
-
-    /* The first frame executed in this segment. null iff cx is null */
-    StackFrame          *initialFrame_;
-
-    /* If this segment is suspended, |cx->regs| when it was suspended. */
-    FrameRegs           *suspendedRegs_;
-
-    /* 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 suspendedRegs when empty.
-     */
-#define NON_NULL_SUSPENDED_REGS ((FrameRegs *)0x1)
-
-  public:
-    StackSegment()
-      : stack_(NULL), previousInContext_(NULL), previousInMemory_(NULL),
-        initialFrame_(NULL), suspendedRegs_(NON_NULL_SUSPENDED_REGS),
-        saved_(false)
-    {
-        JS_ASSERT(empty());
-    }
-
-    /* Safe casts guaranteed by the contiguous-stack layout. */
-
-    Value *valueRangeBegin() const {
-        return (Value *)(this + 1);
-    }
-
-    /*
-     * The set of fields provided by a segment depend on its state. In addition
-     * to the "active" and "suspended" states described in Stack.h, segments
-     * have a third state: empty. An empty segment contains no frames and is
-     * pushed for the purpose of preparing the args to Invoke. Invoke args
-     * requires special handling because anything can happen between pushing
-     * Invoke args and calling Invoke. Since an empty segment contains no
-     * frames, it cannot become the "current segment" of a ContextStack (for
-     * various arcane and hopefully temporary reasons). Thus, an empty segment
-     * is pushed onto the StackSpace but only pushed onto a ContextStack when it
-     * gets its first frame pushed from js::Invoke.
-     *
-     * 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.
-     * Hopefully, this feature will be removed.
-     */
-
-    bool empty() const {
-        JS_ASSERT(!!stack_ == !!initialFrame_);
-        JS_ASSERT_IF(!stack_, suspendedRegs_ == NON_NULL_SUSPENDED_REGS && !saved_);
-        return !stack_;
-    }
-
-    bool isActive() const {
-        JS_ASSERT_IF(!suspendedRegs_, stack_ && !saved_);
-        JS_ASSERT_IF(!stack_, suspendedRegs_ == NON_NULL_SUSPENDED_REGS);
-        return !suspendedRegs_;
-    }
-
-    bool isSuspended() const {
-        JS_ASSERT_IF(!stack_ || !suspendedRegs_, !saved_);
-        JS_ASSERT_IF(!stack_, suspendedRegs_ == NON_NULL_SUSPENDED_REGS);
-        return stack_ && suspendedRegs_;
-    }
-
-    /* Substate of suspended, queryable in any state. */
-
-    bool isSaved() const {
-        JS_ASSERT_IF(saved_, isSuspended());
-        return saved_;
-    }
-
-    /* Transitioning between empty <--> isActive */
-
-    void joinContext(ContextStack &stack, StackFrame &frame) {
-        JS_ASSERT(empty());
-        stack_ = &stack;
-        initialFrame_ = &frame;
-        suspendedRegs_ = NULL;
-        JS_ASSERT(isActive());
-    }
-
-    void leaveContext() {
-        JS_ASSERT(isActive());
-        stack_ = NULL;
-        initialFrame_ = NULL;
-        suspendedRegs_ = NON_NULL_SUSPENDED_REGS;
-        JS_ASSERT(empty());
-    }
-
-    ContextStack &stack() const {
-        JS_ASSERT(!empty());
-        return *stack_;
-    }
-
-    ContextStack *maybeStack() const {
-        return stack_;
-    }
-
-#undef NON_NULL_SUSPENDED_REGS
-
-    /* Transitioning between isActive <--> isSuspended */
-
-    void suspend(FrameRegs &regs) {
-        JS_ASSERT(isActive());
-        JS_ASSERT(contains(regs.fp()));
-        suspendedRegs_ = &regs;
-        JS_ASSERT(isSuspended());
-    }
-
-    void resume() {
-        JS_ASSERT(isSuspended());
-        suspendedRegs_ = NULL;
-        JS_ASSERT(isActive());
-    }
-
-    /* When isSuspended, transitioning isSaved <--> !isSaved */
-
-    void save(FrameRegs &regs) {
-        JS_ASSERT(!isSuspended());
-        suspend(regs);
-        saved_ = true;
-        JS_ASSERT(isSaved());
-    }
-
-    void restore() {
-        JS_ASSERT(isSaved());
-        saved_ = false;
-        resume();
-        JS_ASSERT(!isSuspended());
-    }
-
-    /* Data available when !empty */
-
-    StackFrame *initialFrame() const {
-        JS_ASSERT(!empty());
-        return initialFrame_;
-    }
-
-    FrameRegs &currentRegs() const {
-        JS_ASSERT(!empty());
-        return isActive() ? stack_->regs() : suspendedRegs();
-    }
-
-    StackFrame *currentFrame() const {
-        return currentRegs().fp();
-    }
-
-    StackFrame *currentFrameOrNull() const {
-        return empty() ? NULL : currentFrame();
-    }
-
-    /* Data available when isSuspended. */
-
-    FrameRegs &suspendedRegs() const {
-        JS_ASSERT(isSuspended());
-        return *suspendedRegs_;
-    }
-
-    StackFrame *suspendedFrame() const {
-        return suspendedRegs_->fp();
-    }
-
-    /* JSContext / js::StackSpace bookkeeping. */
-
-    void setPreviousInContext(StackSegment *seg) {
-        previousInContext_ = seg;
-    }
-
-    StackSegment *previousInContext() const  {
-        return previousInContext_;
-    }
-
-    void setPreviousInMemory(StackSegment *seg) {
-        previousInMemory_ = seg;
-    }
-
-    StackSegment *previousInMemory() const  {
-        return previousInMemory_;
-    }
-
-    bool contains(const StackFrame *fp) const;
-
-    StackFrame *computeNextFrame(StackFrame *fp) const;
-};
-
-static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
-JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
-
-/*****************************************************************************/
-
 inline void
 StackFrame::initPrev(JSContext *cx)
 {
     JS_ASSERT(flags_ & HAS_PREVPC);
     if (FrameRegs *regs = cx->maybeRegs()) {
         prev_ = regs->fp();
         prevpc_ = regs->pc;
         JS_ASSERT_IF(!prev_->isDummyFrame() && !prev_->hasImacropc(),
@@ -382,111 +173,16 @@ StackFrame::initJitFrameEarlyPrologue(JS
  * checked for stack overflow and formal/actual arg mismatch.
  */
 inline void
 StackFrame::initJitFrameLatePrologue()
 {
     SetValueRangeToUndefined(slots(), script()->nfixed);
 }
 
-inline void
-StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, const Value &thisv,
-                             JSObject &scopeChain, ExecuteType type)
-{
-    /*
-     * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
-     * script in the context of another frame and the frame type is determined
-     * by the context.
-     */
-    flags_ = type | HAS_SCOPECHAIN;
-    if (!(flags_ & GLOBAL))
-        flags_ |= (prev->flags_ & (FUNCTION | GLOBAL));
-
-    Value *dstvp = (Value *)this - 2;
-    dstvp[1] = thisv;
-
-    if (isFunctionFrame()) {
-        dstvp[0] = prev->calleev();
-        exec = prev->exec;
-        args.script = script;
-    } else {
-        JS_ASSERT(isGlobalFrame());
-        dstvp[0] = NullValue();
-        exec.script = script;
-#ifdef DEBUG
-        args.script = (JSScript *)0xbad;
-#endif
-    }
-
-    scopeChain_ = &scopeChain;
-    prev_ = prev;
-#ifdef DEBUG
-    ncode_ = (void *)0xbad;
-    Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
-    prevpc_ = (jsbytecode *)0xbad;
-    hookData_ = (void *)0xbad;
-    annotation_ = (void *)0xbad;
-#endif
-
-    if (flags_ & HAS_ANNOTATION)
-        annotation_ = prev->annotation_;
-}
-
-inline void
-StackFrame::initDummyFrame(JSContext *cx, JSObject &chain)
-{
-    PodZero(this);
-    flags_ = DUMMY | HAS_PREVPC | HAS_SCOPECHAIN;
-    initPrev(cx);
-    chain.isGlobal();
-    setScopeChainNoCallObj(chain);
-}
-
-inline void
-StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp,
-                               Value *othervp, Value *othersp)
-{
-    JS_ASSERT(vp == (Value *)this - ((Value *)otherfp - othervp));
-    JS_ASSERT(othervp == otherfp->actualArgs() - 2);
-    JS_ASSERT(othersp >= otherfp->slots());
-    JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
-
-    PodCopy(vp, othervp, othersp - othervp);
-    JS_ASSERT(vp == this->actualArgs() - 2);
-
-    /* Catch bad-touching of non-canonical args (e.g., generator_trace). */
-    if (otherfp->hasOverflowArgs())
-        Debug_SetValueRangeToCrashOnTouch(othervp, othervp + 2 + otherfp->numFormalArgs());
-
-    /*
-     * Repoint Call, Arguments, Block and With objects to the new live frame.
-     * Call and Arguments are done directly because we have pointers to them.
-     * Block and With objects are done indirectly through 'liveFrame'. See
-     * js_LiveFrameToFloating comment in jsiter.h.
-     */
-    if (hasCallObj()) {
-        JSObject &obj = callObj();
-        obj.setPrivate(this);
-        otherfp->flags_ &= ~HAS_CALL_OBJ;
-        if (js_IsNamedLambda(fun())) {
-            JSObject *env = obj.getParent();
-            JS_ASSERT(env->getClass() == &js_DeclEnvClass);
-            env->setPrivate(this);
-        }
-    }
-    if (hasArgsObj()) {
-        ArgumentsObject &argsobj = argsObj();
-        if (argsobj.isNormalArguments())
-            argsobj.setPrivate(this);
-        else
-            JS_ASSERT(!argsobj.getPrivate());
-        otherfp->flags_ &= ~HAS_ARGS_OBJ;
-    }
-}
-
 inline Value &
 StackFrame::canonicalActualArg(uintN i) const
 {
     if (i < numFormalArgs())
         return formalArg(i);
     JS_ASSERT(i < numActualArgs());
     return actualArgs()[i];
 }
@@ -667,52 +363,16 @@ StackFrame::markActivationObjectsAsPut()
                           : scopeChain_->getParent();
             flags_ &= ~HAS_CALL_OBJ;
         }
     }
 }
 
 /*****************************************************************************/
 
-JS_ALWAYS_INLINE void
-StackSpace::pushOverride(Value *top, StackOverride *prev)
-{
-    *prev = override_;
-
-    override_.top = top;
-#ifdef DEBUG
-    override_.seg = seg_;
-    override_.frame = seg_->currentFrameOrNull();
-#endif
-
-    JS_ASSERT(prev->top < override_.top);
-}
-
-JS_ALWAYS_INLINE void
-StackSpace::popOverride(const StackOverride &prev)
-{
-    JS_ASSERT(prev.top < override_.top);
-
-    JS_ASSERT_IF(seg_->empty(), override_.frame == NULL);
-    JS_ASSERT_IF(!seg_->empty(), override_.frame == seg_->currentFrame());
-    JS_ASSERT(override_.seg == seg_);
-
-    override_ = prev;
-}
-
-JS_ALWAYS_INLINE Value *
-StackSpace::activeFirstUnused() const
-{
-    JS_ASSERT(seg_->isActive());
-
-    Value *max = Max(seg_->stack().regs().sp, override_.top);
-    JS_ASSERT(max == firstUnused());
-    return max;
-}
-
 #ifdef JS_TRACER
 JS_ALWAYS_INLINE bool
 StackSpace::ensureEnoughSpaceToEnterTrace()
 {
     ptrdiff_t needed = TraceNativeStorage::MAX_NATIVE_STACK_SLOTS +
                        TraceNativeStorage::MAX_CALL_STACK_ENTRIES * VALUES_PER_STACK_FRAME;
 #ifdef XP_WIN
     return ensureSpace(NULL, firstUnused(), needed);
@@ -760,23 +420,16 @@ StackSpace::getStackLimit(JSContext *cx)
     }
 
     return limit;
 }
 
 /*****************************************************************************/
 
 JS_ALWAYS_INLINE bool
-ContextStack::isCurrentAndActive() const
-{
-    assertSegmentsInSync();
-    return seg_ && seg_->isActive() && seg_ == space().currentSegment();
-}
-
-JS_ALWAYS_INLINE bool
 OOMCheck::operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
 {
     return space.ensureSpace(cx, from, nvals);
 }
 
 JS_ALWAYS_INLINE bool
 LimitCheck::operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
 {
@@ -824,50 +477,49 @@ ContextStack::getCallFrame(JSContext *cx
     *flags = StackFrame::Flags(*flags | StackFrame::OVERFLOW_ARGS);
     uintN ncopy = 2 + nformal;
     if (JS_UNLIKELY(!check(cx, space(), firstUnused, ncopy + nvals)))
         return NULL;
 
     Value *dst = firstUnused;
     Value *src = args.base();
     PodCopy(dst, src, ncopy);
-    Debug_SetValueRangeToCrashOnTouch(src, ncopy);
     return reinterpret_cast<StackFrame *>(firstUnused + ncopy);
 }
 
 template <class Check>
 JS_ALWAYS_INLINE bool
 ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                               JSObject &callee, JSFunction *fun, JSScript *script,
                               MaybeConstruct construct, Check check)
 {
-    JS_ASSERT(isCurrentAndActive());
-    JS_ASSERT(&regs == &cx->regs());
+    JS_ASSERT(onTop());
+    JS_ASSERT(&regs == &seg_->regs());
     JS_ASSERT(regs.sp == args.end());
     /* Cannot assert callee == args.callee() since this is called from LeaveTree. */
     JS_ASSERT(callee.getFunctionPrivate() == fun);
     JS_ASSERT(fun->script() == script);
 
     StackFrame::Flags flags = ToFrameFlags(construct);
     StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, check);
     if (!fp)
         return false;
 
     /* Initialize frame, locals, regs. */
     fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
-    regs.prepareToRun(fp, script);
+    regs.prepareToRun(*fp, script);
     return true;
 }
 
 JS_ALWAYS_INLINE StackFrame *
 ContextStack::getFixupFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                             JSFunction *fun, JSScript *script, void *ncode,
                             MaybeConstruct construct, LimitCheck check)
 {
-    JS_ASSERT(isCurrentAndActive());
+    JS_ASSERT(onTop());
     JS_ASSERT(&regs == &cx->regs());
     JS_ASSERT(regs.sp == args.end());
     JS_ASSERT(args.callee().getFunctionPrivate() == fun);
     JS_ASSERT(fun->script() == script);
 
     StackFrame::Flags flags = ToFrameFlags(construct);
     StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, check);
     if (!fp)
@@ -875,153 +527,40 @@ ContextStack::getFixupFrame(JSContext *c
 
     /* Do not init late prologue or regs; this is done by jit code. */
     fp->initJitFrameCallerHalf(cx, flags, ncode);
     fp->initJitFrameEarlyPrologue(fun, args.argc());
     return fp;
 }
 
 JS_ALWAYS_INLINE void
-ContextStack::popInlineFrame()
+ContextStack::popInlineFrame(FrameRegs &regs)
 {
-    JS_ASSERT(isCurrentAndActive());
+    JS_ASSERT(onTop());
+    JS_ASSERT(&regs == &seg_->regs());
 
-    StackFrame *fp = regs_->fp();
+    StackFrame *fp = regs.fp();
     fp->putActivationObjects();
 
     Value *newsp = fp->actualArgs() - 1;
     JS_ASSERT(newsp >= fp->prev()->base());
 
     newsp[-1] = fp->returnValue();
-    regs_->popFrame(newsp);
-}
-
-JS_ALWAYS_INLINE bool
-ContextStack::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *argsGuard)
-{
-    if (!isCurrentAndActive())
-        return pushInvokeArgsSlow(cx, argc, argsGuard);
-
-    Value *start = space().activeFirstUnused();
-    uintN vplen = 2 + argc;
-    if (!space().ensureSpace(cx, start, vplen))
-        return false;
-
-    Value *vp = start;
-    ImplicitCast<CallArgs>(*argsGuard) = CallArgsFromVp(argc, vp);
-
-    /*
-     * Use stack override to root vp until the frame is pushed. Don't need to
-     * MakeRangeGCSafe: the VM stack is conservatively marked.
-     */
-    space().pushOverride(vp + vplen, &argsGuard->prevOverride_);
-
-    argsGuard->stack_ = this;
-    return true;
-}
-
-JS_ALWAYS_INLINE void
-ContextStack::popInvokeArgs(const InvokeArgsGuard &argsGuard)
-{
-    if (argsGuard.seg_) {
-        popInvokeArgsSlow(argsGuard);
-        return;
-    }
-
-    JS_ASSERT(isCurrentAndActive());
-    space().popOverride(argsGuard.prevOverride_);
-}
-
-JS_ALWAYS_INLINE
-InvokeArgsGuard::~InvokeArgsGuard()
-{
-    if (JS_UNLIKELY(!pushed()))
-        return;
-    stack_->popInvokeArgs(*this);
+    regs.popFrame(newsp);
 }
 
-JS_ALWAYS_INLINE bool
-ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args, MaybeConstruct construct,
-                              JSObject &callee, JSFunction *fun, JSScript *script,
-                              InvokeFrameGuard *ifg)
-{
-    JS_ASSERT(callee == args.callee());
-    JS_ASSERT(callee.getFunctionPrivate() == fun);
-    JS_ASSERT(fun->script() == script);
-    JS_ASSERT(args.end() == space().firstUnused());
-
-    /* Get pointers into the stack; check for overflow. */
-    StackFrame::Flags flags = ToFrameFlags(construct);
-    StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, OOMCheck());
-    if (!fp)
-        return false;
-
-    /* Initialize regs, frame, locals. */
-    ifg->regs_.prepareToRun(fp, script);
-    fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
-
-    if (JS_UNLIKELY(space().seg_->empty())) {
-        pushInvokeFrameSlow(ifg);
-        return true;
-    }
-
-    /* Offically push onto the stack. */
-    ifg->prevRegs_ = regs_;
-    regs_ = &ifg->regs_;
-
-    /* Mark as pushed. */
-    ifg->stack_ = this;
-    return true;
-}
-
-JS_ALWAYS_INLINE void
-ContextStack::popInvokeFrame(const InvokeFrameGuard &frameGuard)
+inline void
+ContextStack::popFrameAfterOverflow()
 {
-    JS_ASSERT(isCurrentAndActive());
-    JS_ASSERT(&frameGuard.regs_ == regs_);
-
-    if (JS_UNLIKELY(seg_->initialFrame() == regs_->fp())) {
-        popInvokeFrameSlow(frameGuard);
-        return;
-    }
-
-    regs_->fp()->putActivationObjects();
-    regs_ = frameGuard.prevRegs_;
-}
-
-JS_ALWAYS_INLINE void
-InvokeFrameGuard::pop()
-{
-    JS_ASSERT(pushed());
-    stack_->popInvokeFrame(*this);
-    stack_ = NULL;
+    /* Restore the regs to what they were on entry to JSOP_CALL. */
+    FrameRegs &regs = seg_->regs();
+    StackFrame *fp = regs.fp();
+    regs.popFrame(fp->actualArgsEnd());
 }
 
-JS_ALWAYS_INLINE
-InvokeFrameGuard::~InvokeFrameGuard()
-{
-    if (pushed())
-        pop();
-}
-
-inline StackFrame *
-ContextStack::findFrameAtLevel(uintN targetLevel) const
-{
-    StackFrame *fp = regs_->fp();
-    while (true) {
-        JS_ASSERT(fp && fp->isScriptFrame());
-        if (fp->script()->staticLevel == targetLevel)
-            break;
-        fp = fp->prev();
-    }
-    return fp;
-}
-
-/*****************************************************************************/
-
 namespace detail {
 
 struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
 {
     CopyNonHoleArgsTo(ArgumentsObject *argsobj, Value *dst) : argsobj(*argsobj), dst(dst) {}
     ArgumentsObject &argsobj;
     Value *dst;
     bool operator()(uint32 argi, Value *src) {
@@ -1096,10 +635,9 @@ ArgumentsObject::getElements(uint32 star
         return false;
 
     /* Otherwise, element values are on the stack. */
     JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX);
     return fp->forEachCanonicalActualArg(detail::CopyNonHoleArgsTo(this, vp), start, count);
 }
 
 } /* namespace js */
-
 #endif /* Stack_inl_h__ */
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -64,113 +64,248 @@
 #  endif
 # endif
 #endif
 
 using namespace js;
 
 /*****************************************************************************/
 
+void
+StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
+                             const Value &thisv, JSObject &scopeChain, ExecuteType type)
+{
+    /*
+     * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
+     * script in the context of another frame and the frame type is determined
+     * by the context.
+     */
+    flags_ = type | HAS_SCOPECHAIN | HAS_PREVPC;
+    if (!(flags_ & GLOBAL))
+        flags_ |= (prev->flags_ & (FUNCTION | GLOBAL));
+
+    Value *dstvp = (Value *)this - 2;
+    dstvp[1] = thisv;
+
+    if (isFunctionFrame()) {
+        dstvp[0] = prev->calleev();
+        exec = prev->exec;
+        args.script = script;
+    } else {
+        JS_ASSERT(isGlobalFrame());
+        dstvp[0] = NullValue();
+        exec.script = script;
+#ifdef DEBUG
+        args.script = (JSScript *)0xbad;
+#endif
+    }
+
+    scopeChain_ = &scopeChain;
+    prev_ = prev;
+    prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad;
+
+#ifdef DEBUG
+    ncode_ = (void *)0xbad;
+    Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
+    hookData_ = (void *)0xbad;
+    annotation_ = (void *)0xbad;
+#endif
+
+    if (prev && prev->annotation())
+        setAnnotation(prev->annotation());
+}
+
+void
+StackFrame::initDummyFrame(JSContext *cx, JSObject &chain)
+{
+    PodZero(this);
+    flags_ = DUMMY | HAS_PREVPC | HAS_SCOPECHAIN;
+    initPrev(cx);
+    JS_ASSERT(chain.isGlobal());
+    setScopeChainNoCallObj(chain);
+}
+
+void
+StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp,
+                               Value *othervp, Value *othersp)
+{
+    JS_ASSERT(vp == (Value *)this - ((Value *)otherfp - othervp));
+    JS_ASSERT(othervp == otherfp->actualArgs() - 2);
+    JS_ASSERT(othersp >= otherfp->slots());
+    JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
+
+    PodCopy(vp, othervp, othersp - othervp);
+    JS_ASSERT(vp == this->actualArgs() - 2);
+
+    /* Catch bad-touching of non-canonical args (e.g., generator_trace). */
+    if (otherfp->hasOverflowArgs())
+        Debug_SetValueRangeToCrashOnTouch(othervp, othervp + 2 + otherfp->numFormalArgs());
+
+    /*
+     * Repoint Call, Arguments, Block and With objects to the new live frame.
+     * Call and Arguments are done directly because we have pointers to them.
+     * Block and With objects are done indirectly through 'liveFrame'. See
+     * js_LiveFrameToFloating comment in jsiter.h.
+     */
+    if (hasCallObj()) {
+        JSObject &obj = callObj();
+        obj.setPrivate(this);
+        otherfp->flags_ &= ~HAS_CALL_OBJ;
+        if (js_IsNamedLambda(fun())) {
+            JSObject *env = obj.getParent();
+            JS_ASSERT(env->getClass() == &js_DeclEnvClass);
+            env->setPrivate(this);
+        }
+    }
+    if (hasArgsObj()) {
+        ArgumentsObject &argsobj = argsObj();
+        if (argsobj.isNormalArguments())
+            argsobj.setPrivate(this);
+        else
+            JS_ASSERT(!argsobj.getPrivate());
+        otherfp->flags_ &= ~HAS_ARGS_OBJ;
+    }
+}
+
 #ifdef DEBUG
 JSObject *const StackFrame::sInvalidScopeChain = (JSObject *)0xbeef;
 #endif
 
 jsbytecode *
+StackFrame::pcQuadratic(JSContext *cx) const
+{
+    if (hasImacropc())
+        return imacropc();
+    StackSegment &seg = cx->stack.space().findContainingSegment(this);
+    FrameRegs &regs = seg.regs();
+    if (regs.fp() == this)
+        return regs.pc;
+    return seg.computeNextFrame(this)->prevpc();
+}
+
+jsbytecode *
 StackFrame::prevpcSlow()
 {
     JS_ASSERT(!(flags_ & HAS_PREVPC));
 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
     StackFrame *p = prev();
     mjit::JITScript *jit = p->script()->getJIT(p->isConstructing());
     prevpc_ = jit->nativeToPC(ncode_);
     flags_ |= HAS_PREVPC;
     return prevpc_;
 #else
     JS_NOT_REACHED("Unknown PC for frame");
     return NULL;
 #endif
 }
 
-jsbytecode *
-StackFrame::pcQuadratic(JSContext *cx)
-{
-    StackSegment &seg = cx->stack.space().containingSegment(this);
-    FrameRegs &regs = seg.currentRegs();
+/*****************************************************************************/
 
-    /*
-     * This isn't just an optimization; seg->computeNextFrame(fp) is only
-     * defined if fp != seg->currentFrame.
-     */
-    if (regs.fp() == this)
-        return regs.pc;
-
-    return seg.computeNextFrame(this)->prevpc();
+bool
+StackSegment::contains(const StackFrame *fp) const
+{
+    /* NB: this depends on the continuity of segments in memory. */
+    return (Value *)fp >= slotsBegin() && (Value *)fp <= (Value *)maybefp();
 }
 
-/*****************************************************************************/
+bool
+StackSegment::contains(const FrameRegs *regs) const
+{
+    return regs && contains(regs->fp());
+}
 
-JS_REQUIRES_STACK bool
-StackSegment::contains(const StackFrame *fp) const
+bool
+StackSegment::contains(const CallArgsList *call) const
 {
-    JS_ASSERT(!empty());
-
-    if (fp < initialFrame_)
+    if (!call || !calls_)
         return false;
 
-    StackFrame *start;
-    if (isActive())
-        start = stack_->fp();
-    else
-        start = suspendedRegs_->fp();
-
-    if (fp > start)
-        return false;
+    /* NB: this depends on the continuity of segments in memory. */
+    Value *vp = call->argv();
+    bool ret = vp > slotsBegin() && vp <= calls_->argv();
 
 #ifdef DEBUG
     bool found = false;
-    StackFrame *stop = initialFrame_->prev();
-    for (StackFrame *f = start; !found && f != stop; f = f->prev()) {
-        if (f == fp) {
+    for (CallArgsList *c = maybeCalls(); c->argv() > slotsBegin(); c = c->prev()) {
+        if (c == call) {
             found = true;
             break;
         }
     }
-    JS_ASSERT(found);
+    JS_ASSERT(found == ret);
 #endif
 
-    return true;
+    return ret;
 }
 
 StackFrame *
-StackSegment::computeNextFrame(StackFrame *fp) const
+StackSegment::computeNextFrame(const StackFrame *f) const
 {
-    JS_ASSERT(contains(fp));
-    JS_ASSERT(fp != currentFrame());
+    JS_ASSERT(contains(f) && f != fp());
 
-    StackFrame *next = currentFrame();
+    StackFrame *next = fp();
     StackFrame *prev;
-    while ((prev = next->prev()) != fp)
+    while ((prev = next->prev()) != f)
         next = prev;
     return next;
 }
 
+Value *
+StackSegment::end() const
+{
+    /* NB: this depends on the continuity of segments in memory. */
+    JS_ASSERT_IF(calls_ || regs_, contains(calls_) || contains(regs_));
+    Value *p = calls_
+               ? regs_
+                 ? Max(regs_->sp, calls_->end())
+                 : calls_->end()
+               : regs_
+                 ? regs_->sp
+                 : slotsBegin();
+    JS_ASSERT(p >= slotsBegin());
+    return p;
+}
+
+FrameRegs *
+StackSegment::pushRegs(FrameRegs &regs)
+{
+    JS_ASSERT_IF(contains(regs_), regs.fp()->prev() == regs_->fp());
+    FrameRegs *prev = regs_;
+    regs_ = &regs;
+    return prev;
+}
+
+void
+StackSegment::popRegs(FrameRegs *regs)
+{
+    JS_ASSERT_IF(regs && contains(regs->fp()), regs->fp() == regs_->fp()->prev());
+    regs_ = regs;
+}
+
+void
+StackSegment::pushCall(CallArgsList &callList)
+{
+    callList.prev_ = calls_;
+    calls_ = &callList;
+}
+
+void
+StackSegment::popCall()
+{
+    calls_ = calls_->prev_;
+}
+
 /*****************************************************************************/
 
 StackSpace::StackSpace()
   : base_(NULL),
     commitEnd_(NULL),
     end_(NULL),
     seg_(NULL)
-{
-    override_.top = NULL;
-#ifdef DEBUG
-    override_.seg = NULL;
-    override_.frame = NULL;
-#endif
-}
+{}
 
 bool
 StackSpace::init()
 {
     void *p;
 #ifdef XP_WIN
     p = VirtualAlloc(NULL, CAPACITY_BYTES, MEM_RESERVE, PAGE_READWRITE);
     if (!p)
@@ -212,78 +347,57 @@ StackSpace::~StackSpace()
 #ifdef SOLARIS
     munmap((caddr_t)base_, CAPACITY_BYTES);
 #else
     munmap(base_, CAPACITY_BYTES);
 #endif
 #endif
 }
 
-Value *
-StackSpace::firstUnused() const
+StackSegment &
+StackSpace::findContainingSegment(const StackFrame *target) const
 {
-    if (!seg_) {
-        JS_ASSERT(override_.top == NULL);
-        return base_;
-    }
-    if (!seg_->empty()) {
-        Value *sp = seg_->currentRegs().sp;
-        if (override_.top > sp) {
-            JS_ASSERT(override_.seg == seg_);
-            JS_ASSERT_IF(seg_->isActive(), override_.frame == seg_->stack().fp());
-            JS_ASSERT_IF(!seg_->isActive(), override_.frame == seg_->suspendedFrame());
-            return override_.top;
-        }
-        return sp;
-    }
-    JS_ASSERT(override_.seg == seg_);
-    return override_.top;
-}
-
-StackSegment &
-StackSpace::containingSegment(const StackFrame *target) const
-{
-    for (StackSegment *s = seg_; s; s = s->previousInMemory()) {
+    for (StackSegment *s = seg_; s; s = s->prevInMemory()) {
         if (s->contains(target))
             return *s;
     }
     JS_NOT_REACHED("frame not in stack space");
     return *(StackSegment *)NULL;
 }
 
 void
 StackSpace::mark(JSTracer *trc)
 {
     /*
      * JIT code can leave values in an incoherent (i.e., unsafe for precise
      * marking) state, hence MarkStackRangeConservatively.
      */
-    Value *end = firstUnused();
-    for (StackSegment *seg = seg_; seg; seg = seg->previousInMemory()) {
-        STATIC_ASSERT(ubound(end) >= 0);
-        if (seg->empty()) {
-            /* Mark slots/args trailing off segment. */
-            MarkStackRangeConservatively(trc, seg->valueRangeBegin(), end);
-        } else {
-            /* Mark slots/args trailing off of the last stack frame. */
-            StackFrame *fp = seg->currentFrame();
-            MarkStackRangeConservatively(trc, fp->slots(), end);
 
-            /* Mark stack frames and slots/args between stack frames. */
-            StackFrame *initial = seg->initialFrame();
-            for (StackFrame *f = fp; f != initial; f = f->prev()) {
-                js_TraceStackFrame(trc, f);
-                MarkStackRangeConservatively(trc, f->prev()->slots(), (Value *)f);
-            }
-
-            /* Mark initial stack frame and leading args. */
-            js_TraceStackFrame(trc, initial);
-            MarkStackRangeConservatively(trc, seg->valueRangeBegin(), (Value *)initial);
+    /* NB: this depends on the continuity of segments in memory. */
+    Value *nextSegEnd = firstUnused();
+    for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) {
+        /*
+         * A segment describes a linear region of memory that contains a stack
+         * of native and interpreted calls. For marking purposes, though, we
+         * only need to distinguish between frames and values and mark
+         * accordingly. Since native calls only push values on the stack, we
+         * can effectively lump them together and just iterate over interpreted
+         * calls. Thus, marking can view the stack as the regex:
+         *   (segment slots (frame slots)*)*
+         * which gets marked in reverse order.
+         *
+         */
+        Value *slotsEnd = nextSegEnd;
+        for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev()) {
+            MarkStackRangeConservatively(trc, fp->slots(), slotsEnd);
+            js_TraceStackFrame(trc, fp);
+            slotsEnd = (Value *)fp;
         }
-        end = (Value *)seg;
+        MarkStackRangeConservatively(trc, seg->slotsBegin(), slotsEnd);
+        nextSegEnd = (Value *)seg;
     }
 }
 
 #ifdef XP_WIN
 JS_FRIEND_API(bool)
 StackSpace::bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
 {
     if (end_ - from < nvals) {
@@ -322,51 +436,34 @@ StackSpace::tryBumpLimit(JSContext *mayb
 #ifdef XP_WIN
     *limit = commitEnd_;
 #else
     *limit = end_;
 #endif
     return true;
 }
 
-void
-StackSpace::popSegment()
-{
-    JS_ASSERT(seg_->empty());
-    seg_ = seg_->previousInMemory();
-}
-
-void
-StackSpace::pushSegment(StackSegment &seg)
-{
-    JS_ASSERT(seg.empty());
-    seg.setPreviousInMemory(seg_);
-    seg_ = &seg;
-}
-
 size_t
 StackSpace::committedSize()
 {
     return (commitEnd_ - base_) * sizeof(Value);
 }
 
 /*****************************************************************************/
 
 ContextStack::ContextStack(JSContext *cx)
-  : regs_(NULL),
-    seg_(NULL),
+  : seg_(NULL),
     space_(&JS_THREAD_DATA(cx)->stackSpace),
     cx_(cx)
 {
     threadReset();
 }
 
 ContextStack::~ContextStack()
 {
-    JS_ASSERT(!regs_);
     JS_ASSERT(!seg_);
 }
 
 void
 ContextStack::threadReset()
 {
 #ifdef JS_THREADSAFE
     if (cx_->thread())
@@ -375,384 +472,587 @@ ContextStack::threadReset()
         space_ = NULL;
 #else
     space_ = &JS_THREAD_DATA(cx_)->stackSpace;
 #endif
 }
 
 #ifdef DEBUG
 void
-ContextStack::assertSegmentsInSync() const
-{
-    if (regs_) {
-        JS_ASSERT(seg_->isActive());
-        if (StackSegment *prev = seg_->previousInContext())
-            JS_ASSERT(!prev->isActive());
-    } else {
-        JS_ASSERT_IF(seg_, !seg_->isActive());
-    }
-}
-
-void
 ContextStack::assertSpaceInSync() const
 {
     JS_ASSERT(space_);
     JS_ASSERT(space_ == &JS_THREAD_DATA(cx_)->stackSpace);
 }
-
-bool
-ContextStack::contains(const StackFrame *fp) const
-{
-    return &space().containingSegment(fp).stack() == this;
-}
 #endif
 
-void
-ContextStack::saveActiveSegment()
+bool
+ContextStack::onTop() const
 {
-    JS_ASSERT(regs_);
-    seg_->save(*regs_);
-    regs_ = NULL;
-    cx_->resetCompartment();
+    return seg_ && seg_ == space().seg_;
+}
+
+bool
+ContextStack::containsSlow(const StackFrame *target) const
+{
+    for (StackSegment *s = seg_; s; s = s->prevInContext()) {
+        if (s->contains(target))
+            return true;
+    }
+    return false;
 }
 
-void
-ContextStack::restoreSegment()
+/*
+ * This helper function brings the ContextStack to the top of the thread stack
+ * (so that it can be extended to push a frame and/or arguments) by potentially
+ * pushing a StackSegment. The 'pushedSeg' outparam indicates whether such a
+ * segment was pushed (and hence whether the caller needs to call popSegment).
+ *
+ * Additionally, to minimize calls to ensureSpace, ensureOnTop ensures that
+ * there is space for nvars slots on top of the stack.
+ */
+Value *
+ContextStack::ensureOnTop(JSContext *cx, uintN nvars, MaybeExtend extend, bool *pushedSeg)
 {
-    regs_ = &seg_->suspendedRegs();
-    seg_->restore();
-    cx_->resetCompartment();
-}
+    Value *firstUnused = space().firstUnused();
 
-StackFrame *
-ContextStack::getSegmentAndFrame(JSContext *cx, uintN vplen, uintN nslots,
-                                 FrameGuard *frameGuard) const
-{
-    Value *start = space().firstUnused();
-    uintN nvals = VALUES_PER_STACK_SEGMENT + vplen + VALUES_PER_STACK_FRAME + nslots;
-    if (!space().ensureSpace(cx, start, nvals))
+    if (onTop() && extend) {
+        if (!space().ensureSpace(cx, firstUnused, nvars))
+            return NULL;
+        return firstUnused;
+    }
+
+    if (!space().ensureSpace(cx, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
         return NULL;
 
-    StackSegment *seg = new(start) StackSegment;
-    Value *vp = seg->valueRangeBegin();
-    frameGuard->seg_ = seg;
-    return reinterpret_cast<StackFrame *>(vp + vplen);
+    FrameRegs *regs;
+    CallArgsList *calls;
+    if (seg_ && extend) {
+        regs = seg_->maybeRegs();
+        calls = seg_->maybeCalls();
+    } else {
+        regs = NULL;
+        calls = NULL;
+    }
+
+    seg_ = new(firstUnused) StackSegment(seg_, space().seg_, regs, calls);
+    space().seg_ = seg_;
+    *pushedSeg = true;
+    return seg_->slotsBegin();
 }
 
 void
-ContextStack::pushSegmentAndFrameImpl(FrameRegs &regs, StackSegment &seg)
+ContextStack::popSegment()
 {
-    JS_ASSERT(&seg == space().currentSegment());
+    space().seg_ = seg_->prevInMemory();
+    seg_ = seg_->prevInContext();
 
-    if (regs_)
-        seg_->suspend(*regs_);
-    regs_ = &regs;
-
-    seg.setPreviousInContext(seg_);
-    seg_ = &seg;
-    seg.joinContext(*this, *regs.fp());
+    if (!seg_)
+        cx_->maybeMigrateVersionOverride();
 }
 
-void
-ContextStack::pushSegmentAndFrame(FrameGuard *frameGuard)
+bool
+ContextStack::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *iag)
 {
-    space().pushSegment(*frameGuard->seg_);
-    pushSegmentAndFrameImpl(frameGuard->regs_, *frameGuard->seg_);
-    frameGuard->stack_ = this;
+    uintN nvars = 2 + argc;
+    Value *firstUnused = ensureOnTop(cx, nvars, CAN_EXTEND, &iag->pushedSeg_);
+    if (!firstUnused)
+        return false;
+
+    ImplicitCast<CallArgs>(*iag) = CallArgsFromVp(argc, firstUnused);
+
+    seg_->pushCall(*iag);
+    JS_ASSERT(space().firstUnused() == iag->end());
+    iag->setPushed(*this);
+    return true;
 }
 
 void
-ContextStack::popSegmentAndFrameImpl()
+ContextStack::popInvokeArgs(const InvokeArgsGuard &iag)
 {
-    JS_ASSERT(isCurrentAndActive());
-    JS_ASSERT(&seg_->stack() == this);
-    JS_ASSERT(seg_->initialFrame() == regs_->fp());
-
-    regs_->fp()->putActivationObjects();
+    JS_ASSERT(iag.pushed());
+    JS_ASSERT(onTop());
+    JS_ASSERT(space().firstUnused() == seg_->calls().end());
 
-    seg_->leaveContext();
-    seg_ = seg_->previousInContext();
-    if (seg_) {
-        if (seg_->isSaved()) {
-            regs_ = NULL;
-        } else {
-            regs_ = &seg_->suspendedRegs();
-            seg_->resume();
-        }
-    } else {
-        JS_ASSERT(regs_->fp()->prev() == NULL);
-        regs_ = NULL;
-    }
+    seg_->popCall();
+    if (iag.pushedSeg_)
+        popSegment();
 }
 
-void
-ContextStack::popSegmentAndFrame()
+bool
+ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args,
+                              MaybeConstruct construct, InvokeFrameGuard *ifg)
 {
-    popSegmentAndFrameImpl();
-    space().popSegment();
-    notifyIfNoCodeRunning();
-}
+    JS_ASSERT(onTop());
+    JS_ASSERT(space().firstUnused() == args.end());
+
+    JSObject &callee = args.callee();
+    JSFunction *fun = callee.getFunctionPrivate();
+    JSScript *script = fun->script();
 
-FrameGuard::~FrameGuard()
-{
-    if (!pushed())
-        return;
-    JS_ASSERT(stack_->currentSegment() == seg_);
-    JS_ASSERT(stack_->currentSegment()->currentFrame() == regs_.fp());
-    stack_->popSegmentAndFrame();
+    StackFrame::Flags flags = ToFrameFlags(construct);
+    StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, OOMCheck());
+    if (!fp)
+        return false;
+
+    fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
+    ifg->regs_.prepareToRun(*fp, script);
+
+    ifg->prevRegs_ = seg_->pushRegs(ifg->regs_);
+    JS_ASSERT(space().firstUnused() == ifg->regs_.sp);
+    ifg->setPushed(*this);
+    return true;
 }
 
 bool
 ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
                                JSObject &scopeChain, ExecuteType type,
                                StackFrame *evalInFrame, ExecuteFrameGuard *efg)
 {
-    StackFrame *fp = getSegmentAndFrame(cx, 2, script->nslots, efg);
-    if (!fp)
-        return false;
+    /*
+     * Even though global code and indirect eval do not execute in the context
+     * of the current frame, prev-link these to the current frame so that the
+     * callstack looks right to the debugger (via CAN_EXTEND). This is safe
+     * since the scope chain is what determines name lookup and access, not
+     * prev-links.
+     *
+     * Eval-in-frame is the exception since it prev-links to an arbitrary frame
+     * (possibly in the middle of some previous segment). Thus pass CANT_EXTEND
+     * (to start a new segment) and link the frame and call chain manually
+     * below.
+     */
+    CallArgsList *evalInFrameCalls = NULL;  /* quell overwarning */
+    StackFrame *prev;
+    MaybeExtend extend;
+    if (evalInFrame) {
+        /* Though the prev-frame is given, need to search for prev-call. */
+        StackIter iter(cx, StackIter::GO_THROUGH_SAVED);
+        while (!iter.isScript() || iter.fp() != evalInFrame)
+            ++iter;
+        evalInFrameCalls = iter.calls_;
+        prev = evalInFrame;
+        extend = CANT_EXTEND;
+    } else {
+        prev = maybefp();
+        extend = CAN_EXTEND;
+    }
 
-    /* For the debugger's sake, always prev-link to the current frame. */
-    StackFrame *prev = evalInFrame ? evalInFrame : maybefp();
+    uintN nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots;
+    Value *firstUnused = ensureOnTop(cx, nvars, extend, &efg->pushedSeg_);
+    if (!firstUnused)
+        return NULL;
+
+    StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused + 2);
+    fp->initExecuteFrame(script, prev, seg_->maybeRegs(), thisv, scopeChain, type);
+    SetValueRangeToUndefined(fp->slots(), script->nfixed);
+    efg->regs_.prepareToRun(*fp, script);
+
+    /* pushRegs() below links the prev-frame; manually link the prev-call. */
+    if (evalInFrame && evalInFrameCalls)
+        seg_->pushCall(*evalInFrameCalls);
 
-    /* Initialize regs, frame, global vars (GVAR ops expect NULL). */
-    efg->regs_.prepareToRun(fp, script);
-    SetValueRangeToNull(fp->slots(), script->nfixed);
-    fp->initExecuteFrame(script, prev, thisv, scopeChain, type);
+    efg->prevRegs_ = seg_->pushRegs(efg->regs_);
+    JS_ASSERT(space().firstUnused() == efg->regs_.sp);
+    efg->setPushed(*this);
+    return true;
+}
+
+bool
+ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg)
+{
+    uintN nvars = VALUES_PER_STACK_FRAME;
+    Value *firstUnused = ensureOnTop(cx, nvars, CAN_EXTEND, &dfg->pushedSeg_);
+    if (!firstUnused)
+        return NULL;
+
+    StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused);
+    fp->initDummyFrame(cx, scopeChain);
+    dfg->regs_.initDummyFrame(*fp);
 
-    pushSegmentAndFrame(efg);
+    dfg->prevRegs_ = seg_->pushRegs(dfg->regs_);
+    JS_ASSERT(space().firstUnused() == dfg->regs_.sp);
+    dfg->setPushed(*this);
     return true;
 }
 
+void
+ContextStack::popFrame(const FrameGuard &fg)
+{
+    JS_ASSERT(fg.pushed());
+    JS_ASSERT(onTop());
+    JS_ASSERT(space().firstUnused() == fg.regs_.sp);
+    JS_ASSERT(&fg.regs_ == &seg_->regs());
+
+    fg.regs_.fp()->putActivationObjects();
+
+    seg_->popRegs(fg.prevRegs_);
+    if (fg.pushedSeg_)
+        popSegment();
+
+    /*
+     * NB: this code can call out and observe the stack (e.g., through GC), so
+     * it should only be called from a consistent stack state.
+     */
+    if (!hasfp())
+        cx_->resetCompartment();
+}
+
 bool
 ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg)
 {
     StackFrame *genfp = gen->floatingFrame();
     Value *genvp = gen->floatingStack;
     uintN vplen = (Value *)genfp - genvp;
 
-    StackFrame *stackfp = getSegmentAndFrame(cx, vplen, genfp->numSlots(), gfg);
-    if (!stackfp)
+    uintN nvars = vplen + VALUES_PER_STACK_FRAME + genfp->numSlots();
+    Value *firstUnused = ensureOnTop(cx, nvars, CAN_EXTEND, &gfg->pushedSeg_);
+    if (!firstUnused)
         return false;
 
+    StackFrame *stackfp = reinterpret_cast<StackFrame *>(firstUnused + vplen);
     Value *stackvp = (Value *)stackfp - vplen;
 
-    /* Save all this for popGeneratorFrame. */
+    /* Save this for popGeneratorFrame. */
     gfg->gen_ = gen;
     gfg->stackvp_ = stackvp;
 
     /* Copy from the generator's floating frame to the stack. */
-    gfg->regs_.rebaseFromTo(gen->regs, stackfp);
     stackfp->stealFrameAndSlots(stackvp, genfp, genvp, gen->regs.sp);
     stackfp->resetGeneratorPrev(cx);
     stackfp->unsetFloatingGenerator();
+    gfg->regs_.rebaseFromTo(gen->regs, *stackfp);
 
-    pushSegmentAndFrame(gfg);
+    gfg->prevRegs_ = seg_->pushRegs(gfg->regs_);
+    JS_ASSERT(space().firstUnused() == gfg->regs_.sp);
+    gfg->setPushed(*this);
     return true;
 }
 
 void
-ContextStack::popGeneratorFrame(GeneratorFrameGuard *gfg)
+ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg)
 {
-    JSGenerator *gen = gfg->gen_;
+    JSGenerator *gen = gfg.gen_;
     StackFrame *genfp = gen->floatingFrame();
     Value *genvp = gen->floatingStack;
 
-    FrameRegs &stackRegs = gfg->regs_;
+    const FrameRegs &stackRegs = gfg.regs_;
     StackFrame *stackfp = stackRegs.fp();
-    Value *stackvp = gfg->stackvp_;
+    Value *stackvp = gfg.stackvp_;
 
     /* Copy from the stack to the generator's floating frame. */
-    gen->regs.rebaseFromTo(stackRegs, genfp);
+    gen->regs.rebaseFromTo(stackRegs, *genfp);
     genfp->stealFrameAndSlots(genvp, stackfp, stackvp, stackRegs.sp);
     genfp->setFloatingGenerator();
 
-    /* ~FrameGuard will finish the popping. */
-}
-
-GeneratorFrameGuard::~GeneratorFrameGuard()
-{
-    if (pushed())
-        stack_->popGeneratorFrame(this);
+    /* ~FrameGuard/popFrame will finish the popping. */
+    JS_ASSERT(ImplicitCast<const FrameGuard>(gfg).pushed());
 }
 
 bool
-ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain,
-                             DummyFrameGuard *dfg)
-{
-    StackFrame *fp = getSegmentAndFrame(cx, 0 /*vplen*/, 0 /*nslots*/, dfg);
-    if (!fp)
-        return false;
-
-    fp->initDummyFrame(cx, scopeChain);
-    dfg->regs_.initDummyFrame(fp);
-
-    pushSegmentAndFrame(dfg);
-    return true;
-}
-
-bool
-ContextStack::pushInvokeArgsSlow(JSContext *cx, uintN argc,
-                                 InvokeArgsGuard *argsGuard)
+ContextStack::saveFrameChain()
 {
-    /*
-     * Either there is no code running on this context or its not at the top of
-     * the contiguous stack. Either way, push a new empty segment which will
-     * root the args for invoke and later contain the frame pushed by Invoke.
-     */
-    JS_ASSERT(!isCurrentAndActive());
-
-    Value *start = space().firstUnused();
-    size_t vplen = 2 + argc;
-    ptrdiff_t nvals = VALUES_PER_STACK_SEGMENT + vplen;
-    if (!space().ensureSpace(cx, start, nvals))
+    bool pushedSeg;
+    if (!ensureOnTop(cx_, 0, CANT_EXTEND, &pushedSeg))
         return false;
+    JS_ASSERT(pushedSeg);
+    JS_ASSERT(!hasfp());
+    cx_->resetCompartment();
 
-    StackSegment *seg = new(start) StackSegment;
-    argsGuard->seg_ = seg;
-
-    Value *vp = seg->valueRangeBegin();
-    ImplicitCast<CallArgs>(*argsGuard) = CallArgsFromVp(argc, vp);
-
-    /*
-     * Use stack override to root vp until the frame is pushed. Don't need to
-     * MakeRangeGCSafe: the VM stack is conservatively marked.
-     */
-    space().pushSegment(*seg);
-    space().pushOverride(vp + vplen, &argsGuard->prevOverride_);
-
-    argsGuard->stack_ = this;
+    JS_ASSERT(onTop() && seg_->isEmpty());
     return true;
 }
 
 void
-ContextStack::popInvokeArgsSlow(const InvokeArgsGuard &argsGuard)
+ContextStack::restoreFrameChain()
 {
-    JS_ASSERT(space().currentSegment()->empty());
-
-    space().popOverride(argsGuard.prevOverride_);
-    space().popSegment();
-    notifyIfNoCodeRunning();
-}
-
-void
-ContextStack::pushInvokeFrameSlow(InvokeFrameGuard *ifg)
-{
-    JS_ASSERT(space().seg_->empty());
-    pushSegmentAndFrameImpl(ifg->regs_, *space().seg_);
-    ifg->stack_ = this;
-}
+    JS_ASSERT(onTop() && seg_->isEmpty());
 
-void
-ContextStack::popInvokeFrameSlow(const InvokeFrameGuard &ifg)
-{
-    JS_ASSERT(ifg.regs_.fp() == seg_->initialFrame());
-    popSegmentAndFrameImpl();
-}
-
-/*
- * NB: this function can call out and observe the stack (e.g., through GC), so
- * it should only be called from a consistent stack state.
- */
-void
-ContextStack::notifyIfNoCodeRunning()
-{
-    if (regs_)
-        return;
-
+    popSegment();
     cx_->resetCompartment();
-    cx_->maybeMigrateVersionOverride();
 }
 
 /*****************************************************************************/
 
-FrameRegsIter::FrameRegsIter(JSContext *cx)
-  : cx_(cx)
+void
+StackIter::poisonRegs()
 {
-    LeaveTrace(cx);
-    seg_ = cx->stack.currentSegment();
-    if (!seg_) {
-        fp_ = NULL;
-        sp_ = NULL;
-        pc_ = NULL;
-        return;
-    }
-    if (!seg_->isActive()) {
-        JS_ASSERT(seg_->isSuspended());
-        fp_ = seg_->suspendedFrame();
-        sp_ = seg_->suspendedRegs().sp;
-        pc_ = seg_->suspendedRegs().pc;
-        return;
-    }
-    fp_ = cx->fp();
-    sp_ = cx->regs().sp;
-    pc_ = cx->regs().pc;
-    return;
+    sp_ = (Value *)0xbad;
+    pc_ = (jsbytecode *)0xbad;
 }
 
-FrameRegsIter &
-FrameRegsIter::operator++()
+void
+StackIter::popFrame()
 {
     StackFrame *oldfp = fp_;
+    JS_ASSERT(seg_->contains(oldfp));
     fp_ = fp_->prev();
-    if (!fp_)
-        return *this;
-
-    if (oldfp != seg_->initialFrame()) {
+    if (seg_->contains(fp_)) {
         pc_ = oldfp->prevpc();
-        sp_ = oldfp->formalArgsEnd();
-        return *this;
+
+        /*
+         * If there is a CallArgsList element between oldfp and fp_, then sp_
+         * is ignored, so we only consider the case where there is no
+         * intervening CallArgsList. The stack representation is not optimized
+         * for this operation so we need to do a full case analysis of how
+         * frames are pushed by considering each ContextStack::push*Frame.
+         */
+        if (oldfp->isGeneratorFrame()) {
+            /* Generator's args do not overlap with the caller's expr stack. */
+            sp_ = (Value *)oldfp->actualArgs() - 2;
+        } else if (oldfp->isNonEvalFunctionFrame()) {
+            /*
+             * When Invoke is called from a native, there will be an enclosing
+             * pushInvokeArgs which pushes a CallArgsList element so we can
+             * ignore that case. The other two cases of function call frames are
+             * Invoke called directly from script and pushInlineFrmae. In both
+             * cases, the actual arguments of the callee should be included in
+             * the caller's expr stack.
+             */
+            sp_ = oldfp->actualArgsEnd();
+        } else if (oldfp->isFramePushedByExecute()) {
+            /* pushExecuteFrame pushes exactly (callee, this) before frame. */
+            sp_ = (Value *)oldfp - 2;
+        } else {
+            /* pushDummyFrame pushes exactly 0 slots before frame. */
+            JS_ASSERT(oldfp->isDummyFrame());
+            sp_ = (Value *)oldfp;
+        }
+    } else {
+        poisonRegs();
     }
+}
 
-    JS_ASSERT(oldfp == seg_->initialFrame());
-    JS_ASSERT(fp_ == oldfp->prev());
+void
+StackIter::popCall()
+{
+    CallArgsList *oldCall = calls_;
+    JS_ASSERT(seg_->contains(oldCall));
+    calls_ = calls_->prev();
+    if (seg_->contains(fp_)) {
+        /* pc_ keeps its same value. */
+        sp_ = oldCall->base();
+    } else {
+        poisonRegs();
+    }
+}
 
+void
+StackIter::settleOnNewSegment()
+{
+    if (FrameRegs *regs = seg_->maybeRegs()) {
+        sp_ = regs->sp;
+        pc_ = regs->pc;
+    } else {
+        poisonRegs();
+    }
+}
+
+void
+StackIter::startOnSegment(StackSegment *seg)
+{
+    seg_ = seg;
+    fp_ = seg_->maybefp();
+    calls_ = seg_->maybeCalls();
+    settleOnNewSegment();
+}
+
+void
+StackIter::settleOnNewState()
+{
     /*
-     * Segments from arbitrary context stacks can interleave so we must do a
-     * linear scan over segments in this context stack. Furthermore, 'prev' can
-     * be any frame in the segment (not only the suspendedFrame), so we must
-     * scan each stack frame in each segment. Fortunately, this is not hot code.
+     * There are elements of the calls_ and fp_ chains that we want to skip
+     * over so iterate until we settle on one or until there are no more.
      */
-    seg_ = seg_->previousInContext();
-    sp_ = seg_->suspendedRegs().sp;
-    pc_ = seg_->suspendedRegs().pc;
-    StackFrame *f = seg_->suspendedFrame();
-    while (f != fp_) {
-        if (f == seg_->initialFrame()) {
-            seg_ = seg_->previousInContext();
-            sp_ = seg_->suspendedRegs().sp;
-            pc_ = seg_->suspendedRegs().pc;
-            f = seg_->suspendedFrame();
-        } else {
-            sp_ = f->formalArgsEnd();
-            pc_ = f->prevpc();
-            f = f->prev();
+    while (true) {
+        if (!fp_ && !calls_) {
+            if (savedOption_ == GO_THROUGH_SAVED && seg_->prevInContext()) {
+                startOnSegment(seg_->prevInContext());
+                continue;
+            }
+            state_ = DONE;
+            return;
+        }
+
+        /* Check if popFrame/popCall changed segment. */
+        bool containsFrame = seg_->contains(fp_);
+        bool containsCall = seg_->contains(calls_);
+        while (!containsFrame && !containsCall) {
+            seg_ = seg_->prevInContext();
+            containsFrame = seg_->contains(fp_);
+            containsCall = seg_->contains(calls_);
+
+            /* Eval-in-frame allows jumping into the middle of a segment. */
+            if (containsFrame && seg_->fp() != fp_) {
+                /* Avoid duplicating logic; seg_ contains fp_, so no iloop. */
+                StackIter tmp = *this;
+                tmp.startOnSegment(seg_);
+                while (!tmp.isScript() || tmp.fp() != fp_)
+                    ++tmp;
+                JS_ASSERT(tmp.state_ == SCRIPTED && tmp.seg_ == seg_ && tmp.fp_ == fp_);
+                *this = tmp;
+                return;
+            }
+            /* There is no eval-in-frame equivalent for native calls. */
+            JS_ASSERT_IF(containsCall, &seg_->calls() == calls_);
+            settleOnNewSegment();
         }
+
+        /*
+         * In case of both a scripted frame and call record, use linear memory
+         * ordering to decide which was the most recent.
+         */
+        if (containsFrame && (!containsCall || (Value *)fp_ >= calls_->argv())) {
+            /* Nobody wants to see dummy frames. */
+            if (fp_->isDummyFrame()) {
+                popFrame();
+                continue;
+            }
+
+            /*
+             * As an optimization, there is no CallArgsList element pushed for
+             * natives called directly by a script (compiled or interpreted).
+             * We catch these by inspecting the bytecode and stack. This check
+             * relies on the property that, at a call opcode,
+             *
+             *   regs.sp == vp + 2 + argc
+             *
+             * The mjit Function.prototype.apply optimization breaks this
+             * invariant (see ic::SplatApplyArgs). Thus, for JSOP_FUNAPPLY we
+             * need to (slowly) reconstruct the depth.
+             *
+             * Additionally, the Function.prototype.{call,apply} optimizations
+             * leave no record when 'this' is a native function. Thus, if the
+             * following expression runs and breaks in the debugger, the call
+             * to 'replace' will not appear on the callstack.
+             *
+             *   (String.prototype.replace).call('a',/a/,function(){debugger});
+             *
+             * Function.prototype.call will however appear, hence the debugger
+             * can, by inspecting 'args.thisv', give some useful information.
+             */
+            JSOp op = js_GetOpcode(cx_, fp_->script(), pc_);
+            if (op == JSOP_CALL || op == JSOP_FUNCALL) {
+                uintN argc = GET_ARGC(pc_);
+                DebugOnly<uintN> spoff = sp_ - fp_->base();
+                JS_ASSERT(spoff == js_ReconstructStackDepth(cx_, fp_->script(), pc_));
+                Value *vp = sp_ - (2 + argc);
+
+                if (IsNativeFunction(*vp)) {
+                    state_ = IMPLICIT_NATIVE;
+                    args_ = CallArgsFromVp(argc, vp);
+                    return;
+                }
+            } else if (op == JSOP_FUNAPPLY) {
+                uintN argc = GET_ARGC(pc_);
+                uintN spoff = js_ReconstructStackDepth(cx_, fp_->script(), pc_);
+                Value *sp = fp_->base() + spoff;
+                Value *vp = sp - (2 + argc);
+
+                if (IsNativeFunction(*vp)) {
+                    if (sp_ != sp) {
+                        JS_ASSERT(argc == 2);
+                        JS_ASSERT(vp[0].toObject().getFunctionPrivate()->native() == js_fun_apply);
+                        JS_ASSERT(sp_ >= vp + 3);
+                        argc = sp_ - (vp + 2);
+                    }
+                    state_ = IMPLICIT_NATIVE;
+                    args_ = CallArgsFromVp(argc, vp);
+                    return;
+                }
+            }
+
+            state_ = SCRIPTED;
+            JS_ASSERT(sp_ >= fp_->base() && sp_ <= fp_->slots() + fp_->script()->nslots);
+            DebugOnly<JSScript *> script = fp_->script();
+            JS_ASSERT_IF(!fp_->hasImacropc(),
+                         pc_ >= script->code && pc_ < script->code + script->length);
+            return;
+        }
+
+        /*
+         * A CallArgsList element is pushed for any call to Invoke, regardless
+         * of whether the callee is a scripted function or even a callable
+         * object. Thus, it is necessary to filter calleev for natives.
+         *
+         * Second, stuff can happen after the args are pushed but before/after
+         * the actual call, so only consider "active" calls. (Since Invoke
+         * necessarily clobbers the callee, "active" is also necessary to
+         * ensure that the callee slot is valid.)
+         */
+        if (calls_->active() && IsNativeFunction(calls_->calleev())) {
+            state_ = NATIVE;
+            args_ = *calls_;
+            return;
+        }
+
+        /* Pop the call and keep looking. */
+        popCall();
+    }
+}
+
+StackIter::StackIter(JSContext *cx, SavedOption savedOption)
+  : cx_(cx),
+    savedOption_(savedOption)
+{
+    if (StackSegment *seg = cx->stack.seg_) {
+        startOnSegment(seg);
+        settleOnNewState();
+    } else {
+        state_ = DONE;
+    }
+}
+
+StackIter &
+StackIter::operator++()
+{
+    JS_ASSERT(!done());
+    switch (state_) {
+      case DONE:
+        JS_NOT_REACHED("");
+      case SCRIPTED:
+        popFrame();
+        settleOnNewState();
+        break;
+      case NATIVE:
+        popCall();
+        settleOnNewState();
+        break;
+      case IMPLICIT_NATIVE:
+        state_ = SCRIPTED;
+        break;
     }
     return *this;
 }
 
 bool
-FrameRegsIter::operator==(const FrameRegsIter &rhs) const
+StackIter::operator==(const StackIter &rhs) const
 {
-    return done() == rhs.done() && (done() || fp_ == rhs.fp_);
+    return done() == rhs.done() &&
+           (done() ||
+            (isScript() == rhs.isScript() &&
+             ((isScript() && fp() == rhs.fp()) ||
+              (!isScript() && nativeArgs().base() == rhs.nativeArgs().base()))));
 }
 
 /*****************************************************************************/
 
-AllFramesIter::AllFramesIter(JSContext *cx)
-  : seg_(cx->stack.currentSegment()),
-    fp_(seg_ ? seg_->currentFrame() : NULL)
-{
-}
+AllFramesIter::AllFramesIter(StackSpace &space)
+  : seg_(space.seg_),
+    fp_(seg_ ? seg_->maybefp() : NULL)
+{}
 
 AllFramesIter&
 AllFramesIter::operator++()
 {
     JS_ASSERT(!done());
-    if (fp_ == seg_->initialFrame()) {
-        seg_ = seg_->previousInMemory();
-        fp_ = seg_ ? seg_->currentFrame() : NULL;
-    } else {
-        fp_ = fp_->prev();
+    fp_ = fp_->prev();
+    if (!seg_->contains(fp_)) {
+        seg_ = seg_->prevInMemory();
+        while (seg_) {
+            fp_ = seg_->maybefp();
+            if (fp_)
+                return *this;
+            seg_ = seg_->prevInMemory();
+        }
+        JS_ASSERT(!fp_);
     }
     return *this;
 }
-
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -55,93 +55,116 @@ class ContextStack;
 
 class InvokeArgsGuard;
 class InvokeFrameGuard;
 class FrameGuard;
 class ExecuteFrameGuard;
 class DummyFrameGuard;
 class GeneratorFrameGuard;
 
+class CallIter;
+class FrameRegsIter;
+class AllFramesIter;
+
 class ArgumentsObject;
 
 namespace mjit { struct JITScript; }
 
 /*
  * VM stack layout
  *
  * SpiderMonkey uses a per-thread stack to store the activation records,
  * parameters, locals, and expression temporaries for the stack of actively
  * executing scripts, functions and generators. The per-thread stack is owned
  * by the StackSpace object stored in the thread's ThreadData.
  *
  * The per-thread stack is subdivided into contiguous segments of memory which
  * have a memory layout invariant that allows fixed offsets to be used for stack
- * access (by the JIT) as well as fast call/return. This memory layout is
- * encapsulated by a set of types that describe different regions of memory:
- * StackSegment, StackFrame, FrameRegs and CallArgs. To avoid calling into C++,
- * the JIT compiler generates code that simulates C++ stack operations.
- *
- * The memory layout of a segment looks like:
+ * access (by jit code) as well as fast call/return. This memory layout is
+ * encapsulated by a set of types that describe different regions of memory.
+ * This encapsulation has holes: to avoid calling into C++ from generated code,
+ * JIT compilers generate code that simulates analogous operations in C++.
  *
- *                            current regs
- *     .------------------------------------------------------.
- *     |                  current frame                       |
- *     |   .-------------------------------------.            V
- *     |   |    initial frame                    |        FrameRegs
- *     |   |   .------------.                    |            |
- *     |   |   |            V                    V            V
+ * A sample memory layout of a segment looks like:
+ *
+ *                          regs
+ *       .---------------------------------------------.
+ *       |                                             V
+ *       |                                   fp .--FrameRegs--. sp
+ *       |                                      V             V
  * |StackSegment| slots |StackFrame| slots |StackFrame| slots |
  *                        |      ^           |
  *           ? <----------'      `-----------'
  *                 prev               prev
  *
  * A segment starts with a fixed-size header (js::StackSegment) which logically
  * describes the segment, links it to the rest of the stack, and points to the
- * first and last frames in the segment.
+ * end of the stack.
  *
  * Each script activation (global or function code) is given a fixed-size header
  * (js::StackFrame) which is associated with the values (called "slots") before
  * and after it. The frame contains bookkeeping information about the activation
  * and links to the previous frame.
  *
- * The slots preceeding a (function) StackFrame in memory are the arguments of
- * the call. The slots after a StackFrame in memory are its locals followed
- * by its expression stack. There is no clean line between the arguments of a
+ * The slots preceding a (function) StackFrame in memory are the arguments of
+ * the call. The slots after a StackFrame in memory are its locals followed by
+ * its expression stack. There is no clean line between the arguments of a
  * frame and the expression stack of the previous frame since the top slots of
  * the expression become the arguments of a call. There are also layout
  * invariants concerning the arguments and StackFrame; see "Arguments" comment
  * in StackFrame for more details.
  *
  * The top of a segment's current frame's expression stack is pointed to by the
  * segment's "current regs", which contains the stack pointer 'sp'. In the
  * interpreter, sp is adjusted as individual values are pushed and popped from
  * the stack and the FrameRegs struct (pointed by the StackSegment) is a local
  * var of js::Interpret. JIT code simulates this by lazily updating FrameRegs
  * when calling from JIT code into the VM. Ideally, we'd like to remove all
  * dependence on FrameRegs outside the interpreter.
  *
  * A call to a native (C++) function does not push a frame. Instead, an array
- * of values (possibly from the top of a calling frame's expression stack) is
- * passed to the native. The layout of this array is abstracted by js::CallArgs.
- * Note that, between any two StackFrames there may be any number of native
- * calls, so the meaning of 'prev' is not 'directly called by'.
+ * of values is passed to the native. The layout of this array is abstracted by
+ * js::CallArgs. With respect to the StackSegment layout above, the args to a
+ * native call are inserted anywhere there can be slots. A sample memory layout
+ * looks like:
+ *
+ *                          regs
+ *       .----------------------------------------.
+ *       |                                        V
+ *       |                              fp .--FrameRegs--. sp
+ *       |                                 V             V
+ * |StackSegment| native call | slots |StackFrame| slots | native call |
+ *       |     vp <--argc--> end                        vp <--argc--> end
+ *       |         CallArgs <------------------------------ CallArgs
+ *       |                               prev                  ^
+ *       `-----------------------------------------------------'
+ *                                  calls
+ *
+ * Here there are two native calls on the stack. The start of each native arg
+ * range is recorded by a CallArgs element which is prev-linked like stack
+ * frames. Note that, in full generality, native and scripted calls can
+ * interleave arbitrarily. Thus, the end of a segment is the maximum of its
+ * current frame and its current native call. Similarly, the top of the entire
+ * thread stack is the end of its current segment.
+ *
+ * Note that, between any two StackFrames there may be any number
+ * of native calls, so the meaning of 'prev' is not 'directly called by'.
  *
  * An additional feature (perhaps not for much longer: bug 650361) is that
  * multiple independent "contexts" can interleave (LIFO) on a single contiguous
- * stack. "Independent" here means that neither context sees the other's frames.
- * Concretely, an embedding may enter the JS engine on cx1 and then, from a
- * native called by the JS engine, reenter the VM on cx2. Changing from cx1 to
- * cx2 causes cx1's segment to be "suspended" and a new segment started to be
- * started for cx2. These two segments are linked from the perspective of
+ * stack. "Independent" here means that neither context sees the other's
+ * frames. Concretely, an embedding may enter the JS engine on cx1 and then,
+ * from a native called by the JS engine, reenter the VM on cx2. Changing from
+ * cx1 to cx2 causes a new segment to be started for cx2's stack on top of
+ * cx1's current segment. These two segments are linked from the perspective of
  * StackSpace, since they are adjacent on the thread's stack, but not from the
- * perspective of cx1 and cx2. Thus, each segment has two prev-links:
- * previousInMemory and previousInContext. A context's apparent stack is
- * encapsulated and managed by the js::ContextStack object stored in JSContext.
- * ContextStack is the primary interface to the rest of the engine for pushing
- * and popping args (for js::Invoke calls) and frames.
+ * perspective of cx1 and cx2. Thus, each segment has two links: prevInMemory
+ * and prevInContext. Each independent stack is encapsulated and managed by
+ * the js::ContextStack object stored in JSContext. ContextStack is the primary
+ * interface to the rest of the engine for pushing and popping the stack.
  */
 
 /*****************************************************************************/
 
 class CallReceiver
 {
   protected:
 #ifdef DEBUG
@@ -190,16 +213,17 @@ CallReceiverFromVp(Value *vp)
 {
     return CallReceiverFromArgv(vp + 2);
 }
 
 /*****************************************************************************/
 
 class CallArgs : public CallReceiver
 {
+  protected:
     uintN argc_;
   public:
     friend CallArgs CallArgsFromVp(uintN, Value *);
     friend CallArgs CallArgsFromArgv(uintN, Value *);
     friend CallArgs CallArgsFromSp(uintN, Value *);
     Value &operator[](unsigned i) const { JS_ASSERT(i < argc_); return argv_[i]; }
     Value *argv() const { return argv_; }
     uintN argc() const { return argc_; }
@@ -225,16 +249,52 @@ CallArgsFromVp(uintN argc, Value *vp)
 JS_ALWAYS_INLINE CallArgs
 CallArgsFromSp(uintN argc, Value *sp)
 {
     return CallArgsFromArgv(argc, sp - argc);
 }
 
 /*****************************************************************************/
 
+class CallArgsList : public CallArgs
+{
+    friend class StackSegment;
+    CallArgsList *prev_;
+    bool active_;
+  public:
+    friend CallArgsList CallArgsListFromVp(uintN, Value *, CallArgsList *);
+    friend CallArgsList CallArgsListFromArgv(uintN, Value *, CallArgsList *);
+    CallArgsList *prev() const { return prev_; }
+    bool active() const { return active_; }
+    void setActive() { active_ = true; }
+    void setInactive() { active_ = false; }
+};
+
+JS_ALWAYS_INLINE CallArgsList
+CallArgsListFromArgv(uintN argc, Value *argv, CallArgsList *prev)
+{
+    CallArgsList args;
+#ifdef DEBUG
+    args.usedRval_ = false;
+#endif
+    args.argv_ = argv;
+    args.argc_ = argc;
+    args.prev_ = prev;
+    args.active_ = false;
+    return args;
+}
+
+JS_ALWAYS_INLINE CallArgsList
+CallArgsListFromVp(uintN argc, Value *vp, CallArgsList *prev)
+{
+    return CallArgsListFromArgv(argc, vp + 2, prev);
+}
+
+/*****************************************************************************/
+
 enum MaybeConstruct {
     NO_CONSTRUCT           =          0, /* == false */
     CONSTRUCT              =       0x80  /* == StackFrame::CONSTRUCTING, asserted below */
 };
 
 enum ExecuteType {
     EXECUTE_GLOBAL         =        0x1, /* == StackFrame::GLOBAL */
     EXECUTE_DIRECT_EVAL    =        0x8, /* == StackFrame::EVAL */
@@ -327,18 +387,18 @@ class StackFrame
     void resetCallFrame(JSScript *script);
 
     /* Called by jit stubs and serve as a specification for jit-code. */
     void initJitFrameCallerHalf(JSContext *cx, StackFrame::Flags flags, void *ncode);
     void initJitFrameEarlyPrologue(JSFunction *fun, uint32 nactual);
     void initJitFrameLatePrologue();
 
     /* Used for eval. */
-    void initExecuteFrame(JSScript *script, StackFrame *prev, const Value &thisv,
-                          JSObject &scopeChain, ExecuteType type);
+    void initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
+                          const Value &thisv, JSObject &scopeChain, ExecuteType type);
 
     /* Used when activating generators. */
     void stealFrameAndSlots(Value *vp, StackFrame *otherfp, Value *othervp, Value *othersp);
 
     /* Perhaps one fine day we will remove dummy frames. */
     void initDummyFrame(JSContext *cx, JSObject &chain);
 
     /*
@@ -382,16 +442,20 @@ class StackFrame
      * pushed for an ES5 strict-mode eval().
      */
 
     bool isEvalFrame() const {
         JS_ASSERT_IF(flags_ & EVAL, isScriptFrame());
         return flags_ & EVAL;
     }
 
+    bool isEvalInFunction() const {
+        return (flags_ & (EVAL | FUNCTION)) == (EVAL | FUNCTION);
+    }
+
     bool isNonEvalFunctionFrame() const {
         return (flags_ & (FUNCTION | EVAL)) == FUNCTION;
     }
 
     inline bool isStrictEvalFrame() const {
         return isEvalFrame() && script()->strictModeCode;
     }
 
@@ -457,17 +521,17 @@ class StackFrame
      *     ... fp->pcQuadratic(cx);
      *
      * For such situations, prefer FrameRegsIter; its amortized O(1).
      *
      *   When I get to the bottom I go back to the top of the stack
      *   Where I stop and I turn and I go right back
      *   Till I get to the bottom and I see you again...
      */
-    jsbytecode *pcQuadratic(JSContext *cx);
+    jsbytecode *pcQuadratic(JSContext *cx) const;
 
     jsbytecode *prevpc() {
         if (flags_ & HAS_PREVPC)
             return prevpc_;
         return prevpcSlow();
     }
 
     JSScript *script() const {
@@ -921,20 +985,16 @@ class StackFrame
         JS_STATIC_ASSERT((int)NO_CONSTRUCT == 0);
         return MaybeConstruct(flags_ & CONSTRUCTING);
     }
 
     bool isDebuggerFrame() const {
         return !!(flags_ & DEBUGGER);
     }
 
-    bool isDirectEvalOrDebuggerFrame() const {
-        return (flags_ & (EVAL | DEBUGGER)) && !(flags_ & GLOBAL);
-    }
-
     bool hasOverriddenArgs() const {
         return !!(flags_ & OVERRIDE_ARGS);
     }
 
     bool hasOverflowArgs() const {
         return !!(flags_ & OVERFLOW_ARGS);
     }
 
@@ -1071,106 +1131,199 @@ class FrameRegs
 
     /* For jit use (need constant): */
     static const size_t offsetOfFp = 2 * sizeof(void *);
     static void staticAssert() {
         JS_STATIC_ASSERT(offsetOfFp == offsetof(FrameRegs, fp_));
     }
 
     /* For generator: */
-    void rebaseFromTo(const FrameRegs &from, StackFrame *to) {
-        fp_ = to;
-        sp = to->slots() + (from.sp - from.fp_->slots());
+    void rebaseFromTo(const FrameRegs &from, StackFrame &to) {
+        fp_ = &to;
+        sp = to.slots() + (from.sp - from.fp_->slots());
         pc = from.pc;
+        JS_ASSERT(fp_);
     }
 
     /* For ContextStack: */
     void popFrame(Value *newsp) {
         pc = fp_->prevpc();
         sp = newsp;
         fp_ = fp_->prev();
+        JS_ASSERT(fp_);
     }
 
     /* For FixupArity: */
     void popPartialFrame(Value *newsp) {
         sp = newsp;
         fp_ = fp_->prev();
+        JS_ASSERT(fp_);
     }
 
     /* For stubs::CompileFunction, ContextStack: */
-    void prepareToRun(StackFrame *fp, JSScript *script) {
+    void prepareToRun(StackFrame &fp, JSScript *script) {
         pc = script->code;
-        sp = fp->slots() + script->nfixed;
-        fp_ = fp;
+        sp = fp.slots() + script->nfixed;
+        fp_ = &fp;
+        JS_ASSERT(fp_);
     }
 
     /* For pushDummyFrame: */
-    void initDummyFrame(StackFrame *fp) {
+    void initDummyFrame(StackFrame &fp) {
         pc = NULL;
-        sp = fp->slots();
-        fp_ = fp;
+        sp = fp.slots();
+        fp_ = &fp;
+        JS_ASSERT(fp_);
     }
 };
 
 /*****************************************************************************/
 
-struct StackOverride
+class StackSegment
 {
-    Value         *top;
-#ifdef DEBUG
-    StackSegment  *seg;
-    StackFrame    *frame;
-#endif
+    /* Previous segment within same context stack. */
+    StackSegment *const prevInContext_;
+
+    /* Previous segment sequentially in memory. */
+    StackSegment *const prevInMemory_;
+
+    /* Execution registers for most recent script in this segment (or null). */
+    FrameRegs *regs_;
+
+    /* Call args for most recent native call in this segment (or null). */
+    CallArgsList *calls_;
+
+  public:
+    StackSegment(StackSegment *prevInContext,
+                 StackSegment *prevInMemory,
+                 FrameRegs *regs,
+                 CallArgsList *calls)
+      : prevInContext_(prevInContext),
+        prevInMemory_(prevInMemory),
+        regs_(regs),
+        calls_(calls)
+    {}
+
+    /* A segment is followed in memory by the arguments of the first call. */
+
+    Value *slotsBegin() const {
+        return (Value *)(this + 1);
+    }
+
+    /* Accessors. */
+
+    FrameRegs &regs() const {
+        JS_ASSERT(regs_);
+        return *regs_;
+    }
+
+    FrameRegs *maybeRegs() const {
+        return regs_;
+    }
+
+    StackFrame *fp() const {
+        return regs_->fp();
+    }
+
+    StackFrame *maybefp() const {
+        return regs_ ? regs_->fp() : NULL;
+    }
+
+    CallArgsList &calls() const {
+        JS_ASSERT(calls_);
+        return *calls_;
+    }
+
+    CallArgsList *maybeCalls() const {
+        return calls_;
+    }
+
+    Value *callArgv() const {
+        return calls_->argv();
+    }
+
+    Value *maybeCallArgv() const {
+        return calls_ ? calls_->argv() : NULL;
+    }
+
+    StackSegment *prevInContext() const {
+        return prevInContext_;
+    }
+
+    StackSegment *prevInMemory() const {
+        return prevInMemory_;
+    }
+
+    void repointRegs(FrameRegs *regs) {
+        JS_ASSERT_IF(regs, regs->fp());
+        regs_ = regs;
+    }
+
+    bool isEmpty() const {
+        return !calls_ && !regs_;
+    }
+
+    bool contains(const StackFrame *fp) const;
+    bool contains(const FrameRegs *regs) const;
+    bool contains(const CallArgsList *call) const;
+
+    StackFrame *computeNextFrame(const StackFrame *fp) const;
+
+    Value *end() const;
+
+    FrameRegs *pushRegs(FrameRegs &regs);
+    void popRegs(FrameRegs *regs);
+    void pushCall(CallArgsList &callList);
+    void popCall();
+
+    /* For jit access: */
+
+    static const size_t offsetOfRegs() { return offsetof(StackSegment, regs_); }
 };
 
+static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
+JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
+
 /*****************************************************************************/
 
 class StackSpace
 {
     Value         *base_;
     mutable Value *commitEnd_;
     Value         *end_;
     StackSegment  *seg_;
-    StackOverride override_;
 
     static const size_t CAPACITY_VALS  = 512 * 1024;
     static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
     static const size_t COMMIT_VALS    = 16 * 1024;
     static const size_t COMMIT_BYTES   = COMMIT_VALS * sizeof(Value);
 
     static void staticAsserts() {
         JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
     }
 
 #ifdef XP_WIN
     JS_FRIEND_API(bool) bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
 #endif
 
+    friend class AllFramesIter;
     friend class ContextStack;
+    friend class StackFrame;
     friend class OOMCheck;
     inline bool ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
-    void pushSegment(StackSegment &seg);
-    void popSegment();
-    inline void pushOverride(Value *top, StackOverride *prev);
-    inline void popOverride(const StackOverride &prev);
+    StackSegment &findContainingSegment(const StackFrame *target) const;
 
   public:
     StackSpace();
     bool init();
     ~StackSpace();
 
     /* See stack layout comment above. */
-    StackSegment *currentSegment() const { return seg_; }
-    Value *firstUnused() const;
-
-    /* Optimization of firstUnused when currentSegment() is known active. */
-    inline Value *activeFirstUnused() const;
-
-    /* Get the segment containing the target frame. */
-    StackSegment &containingSegment(const StackFrame *target) const;
+    Value *firstUnused() const { return seg_ ? seg_->end() : base_; }
+    Value *endOfSpace() const { return end_; }
 
 #ifdef JS_TRACER
     /*
      * LeaveTree requires stack allocation to rebuild the stack. There is no
      * good way to handle an OOM for these allocations, so this function checks
      * that OOM cannot occur using the size of the TraceNativeStorage as a
      * conservative upper bound.
      */
@@ -1230,295 +1383,329 @@ class LimitCheck
     LimitCheck(Value **limit) : limit(limit) {}
     bool operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals);
 };
 
 /*****************************************************************************/
 
 class ContextStack
 {
-    FrameRegs *regs_;
     StackSegment *seg_;
     StackSpace *space_;
     JSContext *cx_;
 
     /*
-     * This is the collecting-point for code that wants to know when there is
-     * no JS active. Note that "no JS active" does not mean the stack is empty
-     * because of JS_(Save|Restore)FrameChain. If code really wants to know
-     * when the stack is empty, test |cx->stack.empty()|.
+     * Return whether this ContextStack is at the top of the contiguous stack.
+     * This is a precondition for extending the current segment by pushing
+     * stack frames or overrides etc.
+     *
+     * NB: Just because a stack is onTop() doesn't mean there is necessarily
+     * a frame pushed on the stack. For this, use hasfp().
      */
-    void notifyIfNoCodeRunning();
-
-    /*
-     * Return whether this ContextStack is running code at the top of the
-     * contiguous stack. This is a precondition for extending the current
-     * segment by pushing stack frames or overrides etc.
-     */
-    inline bool isCurrentAndActive() const;
+    bool onTop() const;
 
 #ifdef DEBUG
-    void assertSegmentsInSync() const;
     void assertSpaceInSync() const;
 #else
-    void assertSegmentsInSync() const {}
     void assertSpaceInSync() const {}
 #endif
 
-    friend class FrameGuard;
-    StackFrame *getSegmentAndFrame(JSContext *cx, uintN vplen, uintN nslots,
-                                   FrameGuard *frameGuard) const;
-    void pushSegmentAndFrame(FrameGuard *frameGuard);
-    void pushSegmentAndFrameImpl(FrameRegs &regs, StackSegment &seg);
-    void popSegmentAndFrame();
-    void popSegmentAndFrameImpl();
+    /* Implementation details of push* public interface. */
+    StackSegment *pushSegment(JSContext *cx);
+    enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
+    Value *ensureOnTop(JSContext *cx, uintN nvars, MaybeExtend extend, bool *pushedSeg);
 
-    friend class GeneratorFrameGuard;
-    void popGeneratorFrame(GeneratorFrameGuard *gfg);
-
+    /* Check = { OOMCheck, LimitCheck } */
     template <class Check>
-    inline StackFrame *getCallFrame(JSContext *cx, const CallArgs &args,
-                                    JSFunction *fun, JSScript *script,
-                                    StackFrame::Flags *pflags,
-                                    Check check) const;
+    inline StackFrame *
+    getCallFrame(JSContext *cx, const CallArgs &args, JSFunction *fun, JSScript *script,
+                 StackFrame::Flags *pflags, Check check) const;
 
+    /* Make pop* functions private since only called by guard classes. */
+    void popSegment();
     friend class InvokeArgsGuard;
-    bool pushInvokeArgsSlow(JSContext *cx, uintN argc, InvokeArgsGuard *argsGuard);
-    void popInvokeArgsSlow(const InvokeArgsGuard &argsGuard);
-    inline void popInvokeArgs(const InvokeArgsGuard &argsGuard);
+    void popInvokeArgs(const InvokeArgsGuard &iag);
+    friend class FrameGuard;
+    void popFrame(const FrameGuard &fg);
+    friend class GeneratorFrameGuard;
+    void popGeneratorFrame(const GeneratorFrameGuard &gfg);
 
-    friend class InvokeFrameGuard;
-    void pushInvokeFrameSlow(InvokeFrameGuard *frameGuard);
-    void popInvokeFrameSlow(const InvokeFrameGuard &frameGuard);
-    inline void popInvokeFrame(const InvokeFrameGuard &frameGuard);
+    friend class StackIter;
 
   public:
     ContextStack(JSContext *cx);
     ~ContextStack();
 
+    /*** Stack accessors ***/
+
     /*
      * A context's stack is "empty" if there are no scripts or natives
      * executing. Note that JS_SaveFrameChain does factor into this definition.
      */
-    bool empty() const           { JS_ASSERT_IF(regs_, seg_); return !seg_; }
+    bool empty() const                { return !seg_; }
 
     /*
      * Return whether there has been at least one frame pushed since the most
      * recent call to JS_SaveFrameChain. Note that natives do not have frames
      * and dummy frames are frames that do not represent script execution hence
      * this query has little semantic meaning past "you can call fp()".
      */
-    bool hasfp() const           { JS_ASSERT_IF(regs_, regs_->fp()); return !!regs_; }
-
-    /* Current regs of the current segment (see VM stack layout comment). */
-    FrameRegs &regs() const      { JS_ASSERT(regs_); return *regs_; }
+    bool hasfp() const                { return seg_ && seg_->maybeRegs(); }
 
-    /* Convenience helpers. */
-    FrameRegs *maybeRegs() const { return regs_; }
-    StackFrame *fp() const       { return regs_->fp(); }
-    StackFrame *maybefp() const  { return regs_ ? regs_->fp() : NULL; }
+    /*
+     * Return the most recent script activation's registers with the same
+     * caveat as hasfp regarding JS_SaveFrameChain.
+     */
+    FrameRegs *maybeRegs() const      { return seg_ ? seg_->maybeRegs() : NULL; }
+    StackFrame *maybefp() const       { return seg_ ? seg_->maybefp() : NULL; }
+
+    /* Faster alternatives to maybe* functions. */
+    FrameRegs &regs() const           { JS_ASSERT(hasfp()); return seg_->regs(); }
+    StackFrame *fp() const            { JS_ASSERT(hasfp()); return seg_->fp(); }
 
     /* The StackSpace currently hosting this ContextStack. */
     StackSpace &space() const    { assertSpaceInSync(); return *space_; }
 
-    /*
-     * To avoid indirection, ContextSpace caches a pointers to the StackSpace.
-     * This must be kept coherent with cx->thread->data.space by calling
-     * 'threadReset' whenver cx->thread changes.
-     */
-    void threadReset();
-
-    /*
-     * As an optimization, the interpreter/mjit can operate on a local
-     * FrameRegs instance repoint the ContextStack to this local instance.
-     */
-    void repointRegs(FrameRegs *regs) {
-        JS_ASSERT_IF(regs, regs->fp());
-        regs_ = regs;
-    }
+    /* Return whether the given frame is in this context's stack. */
+    bool containsSlow(const StackFrame *target) const;
 
-    /* Return the current segment, which may or may not be active. */
-    StackSegment *currentSegment() const {
-        assertSegmentsInSync();
-        return seg_;
-    }
-
-    /* Search the call stack for the nearest frame with static level targetLevel. */
-    inline StackFrame *findFrameAtLevel(uintN targetLevel) const;
-
-#ifdef DEBUG
-    /* Return whether the given frame is in this context's stack. */
-    bool contains(const StackFrame *fp) const;
-#endif
-
-    /* Mark the top segment as suspended, without pushing a new one. */
-    void saveActiveSegment();
-
-    /* Undoes calls to suspendActiveSegment. */
-    void restoreSegment();
+    /*** Stack manipulation ***/
 
     /*
      * pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
      * 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.
      */
     bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag);
 
     /* Called by Invoke for a scripted function call. */
-    bool pushInvokeFrame(JSContext *cx, const CallArgs &args, MaybeConstruct,
-                         JSObject &callee, JSFunction *fun, JSScript *script,
-                         InvokeFrameGuard *ifg);
+    bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
+                         MaybeConstruct construct, InvokeFrameGuard *ifg);
 
     /* Called by Execute for execution of eval or global code. */
     bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
                           JSObject &scopeChain, ExecuteType type,
                           StackFrame *evalInFrame, ExecuteFrameGuard *efg);
 
-    /* Called by SendToGenerator to resume a yielded generator. */
+    /*
+     * Called by SendToGenerator to resume a yielded generator. In addition to
+     * pushing a frame onto the VM stack, this function copies over the
+     * floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
+     * will copy the frame back to the floating frame.
+     */
     bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
 
     /* Pushes a "dummy" frame; should be removed one day. */
     bool pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg);
 
     /*
      * An "inline frame" may only be pushed from within the top, active
      * segment. This is the case for calls made inside mjit code and Interpret.
      * For the Check parameter, see OOMCheck et al above.
      */
     template <class Check>
     bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                          JSObject &callee, JSFunction *fun, JSScript *script,
                          MaybeConstruct construct, Check check);
-    void popInlineFrame();
+    void popInlineFrame(FrameRegs &regs);
+
+    /* Pop a partially-pushed frame after hitting the limit before throwing. */
+    void popFrameAfterOverflow();
 
     /*
      * Called by the methodjit for an arity mismatch. Arity mismatch can be
      * hot, so getFixupFrame avoids doing call setup performed by jit code when
      * FixupArity returns. In terms of work done:
      *
      *   getFixupFrame = pushInlineFrame -
      *                   (fp->initJitFrameLatePrologue + regs->prepareToRun)
      */
     StackFrame *getFixupFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                               JSFunction *fun, JSScript *script, void *ncode,
                               MaybeConstruct construct, LimitCheck check);
 
-    /* For jit use: */
-    static size_t offsetOfRegs() { return offsetof(ContextStack, regs_); }
+    bool saveFrameChain();
+    void restoreFrameChain();
+
+    /*
+     * As an optimization, the interpreter/mjit can operate on a local
+     * FrameRegs instance repoint the ContextStack to this local instance.
+     */
+    void repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); }
+
+    /*** For JSContext: ***/
+
+    /*
+     * To avoid indirection, ContextSpace caches a pointer to the StackSpace.
+     * This must be kept coherent with cx->thread->data.space by calling
+     * 'threadReset' whenver cx->thread changes.
+     */
+    void threadReset();
+
+    /*** For jit compiler: ***/
+
+    static size_t offsetOfSeg() { return offsetof(ContextStack, seg_); }
 };
 
 /*****************************************************************************/
 
-class InvokeArgsGuard : public CallArgs
+class InvokeArgsGuard : public CallArgsList
 {
     friend class ContextStack;
-    ContextStack     *stack_;  /* null implies nothing pushed */
-    StackSegment     *seg_;    /* null implies no segment pushed */
-    StackOverride    prevOverride_;
+    ContextStack *stack_;
+    bool pushedSeg_;
+    void setPushed(ContextStack &stack) { JS_ASSERT(!pushed()); stack_ = &stack; }
   public:
-    InvokeArgsGuard() : stack_(NULL), seg_(NULL) {}
-    ~InvokeArgsGuard();
-    bool pushed() const { return stack_ != NULL; }
+    InvokeArgsGuard() : CallArgsList(), stack_(NULL), pushedSeg_(false) {}
+    ~InvokeArgsGuard() { if (pushed()) stack_->popInvokeArgs(*this); }
+    bool pushed() const { return !!stack_; }
+    void pop() { stack_->popInvokeArgs(*this); stack_ = NULL; }
 };
 
-class InvokeFrameGuard
-
-{
-    friend class ContextStack;
-    ContextStack *stack_;  /* null implies nothing pushed */
-    FrameRegs regs_;
-    FrameRegs *prevRegs_;
-  public:
-    InvokeFrameGuard() : stack_(NULL) {}
-    ~InvokeFrameGuard();
-    bool pushed() const { return stack_ != NULL; }
-    void pop();
-    StackFrame *fp() const { return regs_.fp(); }
-};
-
-/* Reusable base; not for direct use. */
 class FrameGuard
 {
   protected:
     friend class ContextStack;
-    ContextStack *stack_;  /* null implies nothing pushed */
-    StackSegment *seg_;
+    ContextStack *stack_;
+    bool pushedSeg_;
     FrameRegs regs_;
+    FrameRegs *prevRegs_;
+    void setPushed(ContextStack &stack) { stack_ = &stack; }
   public:
-    FrameGuard() : stack_(NULL) {}
-    ~FrameGuard();
-    bool pushed() const { return stack_ != NULL; }
+    FrameGuard() : stack_(NULL), pushedSeg_(false) {}
+    ~FrameGuard() { if (pushed()) stack_->popFrame(*this); }
+    bool pushed() const { return !!stack_; }
+    void pop() { stack_->popFrame(*this); stack_ = NULL; }
+
     StackFrame *fp() const { return regs_.fp(); }
 };
 
+class InvokeFrameGuard : public FrameGuard
+{};
+
 class ExecuteFrameGuard : public FrameGuard
 {};
 
 class DummyFrameGuard : public FrameGuard
 {};
 
 class GeneratorFrameGuard : public FrameGuard
 {
     friend class ContextStack;
     JSGenerator *gen_;
     Value *stackvp_;
   public:
-    ~GeneratorFrameGuard();
+    ~GeneratorFrameGuard() { if (pushed()) stack_->popGeneratorFrame(*this); }
 };
 
 /*****************************************************************************/
 
 /*
- * 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 next-frame. This iterator maintains this information when walking
- * a chain of stack frames starting at |cx->fp|.
+ * Iterate through the callstack of the given context. Each element of said
+ * callstack can either be the execution of a script (scripted function call,
+ * global code, eval code, debugger code) or the invocation of a (C++) native.
+ * Example usage:
+ *
+ *   for (Stackiter i(cx); !i.done(); ++i) {
+ *     if (i.isScript()) {
+ *       ... i.fp() ... i.sp() ... i.pc()
+ *     } else {
+ *       JS_ASSERT(i.isNativeCall());
+ *       ... i.args();
+ *     }
  *
- * Usage:
- *   for (FrameRegsIter i(cx); !i.done(); ++i)
- *     ... i.fp() ... i.sp() ... i.pc()
+ * The SavedOption parameter additionally lets the iterator continue through
+ * breaks in the callstack (from JS_SaveFrameChain). The default is to stop.
  */
-class FrameRegsIter
+class StackIter
 {
+    friend class ContextStack;
     JSContext    *cx_;
+  public:
+    enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
+  private:
+    SavedOption  savedOption_;
+
+    enum State { DONE, SCRIPTED, NATIVE, IMPLICIT_NATIVE };
+    State        state_;
+
+    StackFrame   *fp_;
+    CallArgsList *calls_;
+
     StackSegment *seg_;
-    StackFrame   *fp_;
     Value        *sp_;
     jsbytecode   *pc_;
+    CallArgs     args_;
 
-    void initSlow();
-    void incSlow(StackFrame *oldfp);
+    void poisonRegs();
+    void popFrame();
+    void popCall();
+    void settleOnNewSegment();
+    void settleOnNewState();
+    void startOnSegment(StackSegment *seg);
 
   public:
-    FrameRegsIter(JSContext *cx);
+    StackIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
+
+    bool done() const { return state_ == DONE; }
+    StackIter &operator++();
+
+    bool operator==(const StackIter &rhs) const;
+    bool operator!=(const StackIter &rhs) const { return !(*this == rhs); }
 
-    bool done() const { return fp_ == NULL; }
-    FrameRegsIter &operator++();
-    bool operator==(const FrameRegsIter &rhs) const;
-    bool operator!=(const FrameRegsIter &rhs) const { return !(*this == rhs); }
+    bool isScript() const { JS_ASSERT(!done()); return state_ == SCRIPTED; }
+    StackFrame *fp() const { JS_ASSERT(!done() && isScript()); return fp_; }
+    Value      *sp() const { JS_ASSERT(!done() && isScript()); return sp_; }
+    jsbytecode *pc() const { JS_ASSERT(!done() && isScript()); return pc_; }
 
-    StackFrame *fp() const { return fp_; }
-    Value *sp() const { return sp_; }
-    jsbytecode *pc() const { return pc_; }
+    bool isNativeCall() const { JS_ASSERT(!done()); return state_ != SCRIPTED; }
+    CallArgs nativeArgs() const { JS_ASSERT(!done() && isNativeCall()); return args_; }
 };
 
+/* A filtering of the StackIter to only stop at scripts. */
+class FrameRegsIter
+{
+    StackIter iter_;
+
+    void settle() {
+        while (!iter_.done() && !iter_.isScript())
+            ++iter_;
+    }
+
+  public:
+    FrameRegsIter(JSContext *cx) : iter_(cx) { settle(); }
+
+    bool done() const { return iter_.done(); }
+    FrameRegsIter &operator++() { ++iter_; settle(); return *this; }
+
+    bool operator==(const FrameRegsIter &rhs) const { return iter_ == rhs.iter_; }
+    bool operator!=(const FrameRegsIter &rhs) const { return iter_ != rhs.iter_; }
+
+    StackFrame *fp() const { return iter_.fp(); }
+    Value      *sp() const { return iter_.sp(); }
+    jsbytecode *pc() const { return iter_.pc(); }
+};
+
+/*****************************************************************************/
+
 /*
- * Utility class for iteration over all active stack frames.
+ * Blindly iterate over all frames in the current thread's stack. These frames
+ * can be from different contexts and compartments, so beware.
  */
 class AllFramesIter
 {
-public:
-    AllFramesIter(JSContext *cx);
+  public:
+    AllFramesIter(StackSpace &space);
 
     bool done() const { return fp_ == NULL; }
     AllFramesIter& operator++();
 
     StackFrame *fp() const { return fp_; }
 
-private:
+  private:
     StackSegment *seg_;
     StackFrame *fp_;
 };
 
 }  /* namespace js */
 
 #endif /* Stack_h__ */
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp
@@ -254,26 +254,27 @@ static JSFunctionSpec gGlobalFun[] = {
 #endif
     {nsnull,nsnull,0,0}
 };
 
 class JSCLContextHelper
 {
 public:
     JSCLContextHelper(mozJSComponentLoader* loader);
-    ~JSCLContextHelper() { Pop(); }
+    ~JSCLContextHelper();
 
-    JSContext* Pop();
+    void reportErrorAfterPop(char *buf);
 
     operator JSContext*() const {return mContext;}
 
 private:
     JSContext* mContext;
     intN       mContextThread;
     nsIThreadJSContextStack* mContextStack;
+    char*      mBuf;
 
     // prevent copying and assignment
     JSCLContextHelper(const JSCLContextHelper &); // not implemented
     const JSCLContextHelper& operator=(const JSCLContextHelper &); // not implemented
 };
 
 
 class JSCLAutoErrorReporterSetter
@@ -287,62 +288,56 @@ private:
     JSContext* mContext;
     JSErrorReporter mOldReporter;
     // prevent copying and assignment
     JSCLAutoErrorReporterSetter(const JSCLAutoErrorReporterSetter &); // not implemented
     const JSCLAutoErrorReporterSetter& operator=(const JSCLAutoErrorReporterSetter &); // not implemented
 };
 
 static nsresult
-OutputError(JSContext *cx,
-            const char *format,
-            va_list ap)
-{
-    char *buf = JS_vsmprintf(format, ap);
-    if (!buf) {
-        return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    JS_ReportError(cx, buf);
-    JS_smprintf_free(buf);
-
-    return NS_OK;
-}
-
-static nsresult
 ReportOnCaller(nsAXPCNativeCallContext *cc,
                const char *format, ...) {
     if (!cc) {
         return NS_ERROR_FAILURE;
     }
     
     va_list ap;
     va_start(ap, format);
 
     nsresult rv;
     JSContext *callerContext;
     rv = cc->GetJSContext(&callerContext);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    return OutputError(callerContext, format, ap);
+    char *buf = JS_vsmprintf(format, ap);
+    if (!buf) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    JS_ReportError(callerContext, buf);
+    JS_smprintf_free(buf);
+
+    return NS_OK;
 }
 
 static nsresult
 ReportOnCaller(JSCLContextHelper &helper,
                const char *format, ...)
 {
     va_list ap;
     va_start(ap, format);
 
-    JSContext *cx = helper.Pop();
-    if (!cx) {
-        return NS_ERROR_FAILURE;
+    char *buf = JS_vsmprintf(format, ap);
+    if (!buf) {
+        return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    return OutputError(cx, format, ap);
+    helper.reportErrorAfterPop(buf);
+
+    return NS_OK;
 }
 
 static nsresult
 ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
                      JSObject **scriptObj)
 {
     *scriptObj = nsnull;
 
@@ -1634,34 +1629,48 @@ mozJSComponentLoader::ModuleEntry::GetFa
 
     return f.forget();
 }
 
 //----------------------------------------------------------------------
 
 JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
     : mContext(loader->mContext), mContextThread(0),
-      mContextStack(loader->mContextStack)
+      mContextStack(loader->mContextStack),
+      mBuf(nsnull)
 {
     mContextStack->Push(mContext);
     mContextThread = JS_GetContextThread(mContext);
     if (mContextThread) {
         JS_BeginRequest(mContext);
     } 
 }
 
-// Pops the context that was pushed and then returns the context that is now at
-// the top of the stack.
-JSContext*
-JSCLContextHelper::Pop()
+JSCLContextHelper::~JSCLContextHelper()
 {
-    JSContext* cx = nsnull;
     if (mContextStack) {
         if (mContextThread) {
             JS_EndRequest(mContext);
         }
 
         mContextStack->Pop(nsnull);
+
+        JSContext* cx = nsnull;
         mContextStack->Peek(&cx);
+
         mContextStack = nsnull;
+
+        if (cx && mBuf) {
+            JS_ReportError(cx, mBuf);
+        }
     }
-    return cx;
+
+    if (mBuf) {
+        JS_smprintf_free(mBuf);
+    }
 }
+
+void
+JSCLContextHelper::reportErrorAfterPop(char *buf)
+{
+    NS_ASSERTION(!mBuf, "Already called reportErrorAfterPop");
+    mBuf = buf;
+}
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -962,33 +962,16 @@ nsXPConnect::NoteJSContext(JSContext *aJ
 // nsIXPConnect interface methods...
 
 inline nsresult UnexpectedFailure(nsresult rv)
 {
     NS_ERROR("This is not supposed to fail!");
     return rv;
 }
 
-class SaveFrame
-{
-public:
-    SaveFrame(JSContext *cx)
-        : mJSContext(cx) {
-        mFrame = JS_SaveFrameChain(mJSContext);
-    }
-
-    ~SaveFrame() {
-        JS_RestoreFrameChain(mJSContext, mFrame);
-    }
-
-private:
-    JSContext *mJSContext;
-    JSStackFrame *mFrame;
-};
-
 /* void initClasses (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */
 NS_IMETHODIMP
 nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj)
 {
     NS_ASSERTION(aJSContext, "bad param");
     NS_ASSERTION(aGlobalJSObj, "bad param");
 
     // Nest frame chain save/restore in request created by XPCCallContext.
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -3554,24 +3554,23 @@ private:
 
 
 /***************************************************************************/
 // XPCJSContextStack is not actually an xpcom object, but xpcom calls are
 // delegated to it as an implementation detail.
 struct XPCJSContextInfo {
     XPCJSContextInfo(JSContext* aCx) :
         cx(aCx),
-        frame(nsnull),
+        savedFrameChain(false),
         suspendDepth(0)
     {}
     JSContext* cx;
 
-    // Frame to be restored when this JSContext becomes the topmost
-    // one.
-    JSStackFrame* frame;
+    // Whether the frame chain was saved
+    bool savedFrameChain;
 
     // Greater than 0 if a request was suspended.
     jsrefcount suspendDepth;
 };
 
 class XPCJSContextStack
 {
 public:
--- a/js/src/xpconnect/src/xpcthreadcontext.cpp
+++ b/js/src/xpconnect/src/xpcthreadcontext.cpp
@@ -88,45 +88,41 @@ XPCJSContextStack::Peek(JSContext * *_re
 
 /* JSContext pop (); */
 NS_IMETHODIMP
 XPCJSContextStack::Pop(JSContext * *_retval)
 {
     NS_ASSERTION(!mStack.IsEmpty(), "ThreadJSContextStack underflow");
 
     PRUint32 idx = mStack.Length() - 1; // The thing we're popping
-    NS_ASSERTION(!mStack[idx].frame,
-                 "Shouldn't have a pending frame to restore on the context "
-                 "we're popping!");
 
     if(_retval)
         *_retval = mStack[idx].cx;
 
     mStack.RemoveElementAt(idx);
     if(idx > 0)
     {
         --idx; // Advance to new top of the stack
 
         XPCJSContextInfo & e = mStack[idx];
-        NS_ASSERTION(!e.frame || e.cx, "Shouldn't have frame without a cx!");
         NS_ASSERTION(!e.suspendDepth || e.cx, "Shouldn't have suspendDepth without a cx!");
         if(e.cx)
         {
             if(e.suspendDepth)
             {
                 JS_ResumeRequest(e.cx, e.suspendDepth);
                 e.suspendDepth = 0;
             }
 
-            if(e.frame)
+            if(e.savedFrameChain)
             {
                 // Pop() can be called outside any request for e.cx.
                 JSAutoRequest ar(e.cx);
-                JS_RestoreFrameChain(e.cx, e.frame);
-                e.frame = nsnull;
+                JS_RestoreFrameChain(e.cx);
+                e.savedFrameChain = false;
             }
         }
     }
     return NS_OK;
 }
 
 static nsIPrincipal*
 GetPrincipalFromCx(JSContext *cx)
@@ -141,53 +137,55 @@ GetPrincipalFromCx(JSContext *cx)
     return nsnull;
 }
 
 /* void push (in JSContext cx); */
 NS_IMETHODIMP
 XPCJSContextStack::Push(JSContext * cx)
 {
     JS_ASSERT_IF(cx, JS_GetContextThread(cx));
-    if(!mStack.AppendElement(cx))
-        return NS_ERROR_OUT_OF_MEMORY;
-    if(mStack.Length() > 1)
+    if(mStack.Length() > 0)
     {
-        XPCJSContextInfo & e = mStack[mStack.Length() - 2];
+        XPCJSContextInfo & e = mStack[mStack.Length() - 1];
         if(e.cx)
         {
             if(e.cx == cx)
             {
                 nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
                 if(ssm)
                 {
-                    nsIPrincipal* globalObjectPrincipal =
-                        GetPrincipalFromCx(cx);
-                    if(globalObjectPrincipal)
+                    if(nsIPrincipal* globalObjectPrincipal = GetPrincipalFromCx(cx))
                     {
                         nsIPrincipal* subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
                         PRBool equals = PR_FALSE;
                         globalObjectPrincipal->Equals(subjectPrincipal, &equals);
                         if(equals)
                         {
-                            return NS_OK;
+                            goto append;
                         }
                     }
                 }
             }
 
             {
                 // Push() can be called outside any request for e.cx.
                 JSAutoRequest ar(e.cx);
-                e.frame = JS_SaveFrameChain(e.cx);
+                if(!JS_SaveFrameChain(e.cx))
+                    return NS_ERROR_OUT_OF_MEMORY;
+                e.savedFrameChain = true;
             }
 
             if(!cx)
                 e.suspendDepth = JS_SuspendRequest(e.cx);
         }
     }
+
+  append:
+    if(!mStack.AppendElement(cx))
+        return NS_ERROR_OUT_OF_MEMORY;
     return NS_OK;
 }
 
 #ifdef DEBUG
 JSBool 
 XPCJSContextStack::DEBUG_StackHasJSContext(JSContext*  aJSContext)
 {
     for(PRUint32 i = 0; i < mStack.Length(); i++)