Bug 868437 - Add a linked list of activations to JSRuntime and rewrite ScriptFrameIter to use it. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 27 May 2013 11:43:15 +0200
changeset 146141 554597fd45e950061bd8d2bcd4d468b8c964ef7f
parent 146133 22b38f0b80849de065388ef3f418e43a518ce0fa
child 146142 97f46bb7b72b20061e0476c329d089414f1e9366
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs868437
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 868437 - Add a linked list of activations to JSRuntime and rewrite ScriptFrameIter to use it. r=luke
js/src/builtin/Eval.cpp
js/src/gc/RootMarking.cpp
js/src/gc/Verifier.cpp
js/src/ion/Bailouts.cpp
js/src/ion/Bailouts.h
js/src/ion/BaselineBailouts.cpp
js/src/ion/BaselineIC.cpp
js/src/ion/BaselineJIT.cpp
js/src/ion/BaselineJIT.h
js/src/ion/Ion.cpp
js/src/ion/IonCompartment.h
js/src/ion/IonFrameIterator.h
js/src/ion/IonFrames.cpp
js/src/ion/IonFrames.h
js/src/ion/IonMacroAssembler.h
js/src/ion/arm/Bailouts-arm.cpp
js/src/ion/x64/Bailouts-x64.cpp
js/src/ion/x86/Bailouts-x86.cpp
js/src/jit-test/tests/basic/cross-context-stack-1.js
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsdbgapi.cpp
js/src/jsfun.cpp
js/src/jsinferinlines.h
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsscript.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -255,17 +255,17 @@ EvalKernel(JSContext *cx, const CallArgs
     // ES5 15.1.2.1 steps 2-8.
 
     // Per ES5, indirect eval runs in the global scope. (eval is specified this
     // way so that the compiler can make assumptions about what bindings may or
     // may not exist in the current frame if it doesn't see 'eval'.)
     unsigned staticLevel;
     RootedValue thisv(cx);
     if (evalType == DIRECT_EVAL) {
-        JS_ASSERT_IF(caller.isStackFrame(), !caller.asStackFrame()->runningInIon());
+        JS_ASSERT_IF(caller.isStackFrame(), !caller.asStackFrame()->runningInJit());
         staticLevel = caller.script()->staticLevel + 1;
 
         // Direct calls to eval are supposed to see the caller's |this|. If we
         // haven't wrapped that yet, do so now, before we make a copy of it for
         // the eval code to use.
         if (!ComputeThis(cx, caller))
             return false;
         thisv = caller.thisValue();
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -270,25 +270,25 @@ MarkRangeConservatively(JSTracer *trc, c
 
 #ifndef JSGC_USE_EXACT_ROOTING
 static void
 MarkRangeConservativelyAndSkipIon(JSTracer *trc, JSRuntime *rt, const uintptr_t *begin, const uintptr_t *end)
 {
     const uintptr_t *i = begin;
 
 #if JS_STACK_GROWTH_DIRECTION < 0 && defined(JS_ION)
-    // Walk only regions in between Ion activations. Note that non-volatile
-    // registers are spilled to the stack before the entry Ion frame, ensuring
+    // Walk only regions in between JIT activations. Note that non-volatile
+    // registers are spilled to the stack before the entry frame, ensuring
     // that the conservative scanner will still see them.
-    for (ion::IonActivationIterator ion(rt); ion.more(); ++ion) {
-        uintptr_t *ionMin, *ionEnd;
-        ion.ionStackRange(ionMin, ionEnd);
+    for (ion::JitActivationIterator iter(rt); !iter.done(); ++iter) {
+        uintptr_t *jitMin, *jitEnd;
+        iter.jitStackRange(jitMin, jitEnd);
 
-        MarkRangeConservatively(trc, i, ionMin);
-        i = ionEnd;
+        MarkRangeConservatively(trc, i, jitMin);
+        i = jitEnd;
     }
 #endif
 
     // Mark everything after the most recent Ion activation.
     MarkRangeConservatively(trc, i, end);
 }
 
 static JS_NEVER_INLINE void
@@ -737,17 +737,17 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
         /* Mark debug scopes, if present */
         if (c->debugScopes)
             c->debugScopes->mark(trc);
     }
 
     rt->stackSpace.mark(trc);
 
 #ifdef JS_ION
-    ion::MarkIonActivations(rt, trc);
+    ion::MarkJitActivations(rt, trc);
 #endif
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
         c->mark(trc);
 
     /* The embedding can register additional roots here. */
     if (JSTraceDataOp op = rt->gcBlackRootsTraceOp)
         (*op)(trc, rt->gcBlackRootsData);
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -114,36 +114,36 @@ static void
 CheckStackRootsRange(JSRuntime *rt, uintptr_t *begin, uintptr_t *end, Rooter *rbegin, Rooter *rend)
 {
     JS_ASSERT(begin <= end);
     for (uintptr_t *i = begin; i != end; ++i)
         CheckStackRoot(rt, i, rbegin, rend);
 }
 
 static void
-CheckStackRootsRangeAndSkipIon(JSRuntime *rt, uintptr_t *begin, uintptr_t *end, Rooter *rbegin, Rooter *rend)
+CheckStackRootsRangeAndSkipJit(JSRuntime *rt, uintptr_t *begin, uintptr_t *end, Rooter *rbegin, Rooter *rend)
 {
     /*
      * Regions of the stack between Ion activiations are marked exactly through
      * a different mechanism. We need to skip these regions when checking the
      * stack so that we do not poison IonMonkey's things.
      */
     uintptr_t *i = begin;
 
 #if defined(JS_ION)
-    for (ion::IonActivationIterator ion(rt); ion.more(); ++ion) {
-        uintptr_t *ionMin, *ionEnd;
-        ion.ionStackRange(ionMin, ionEnd);
+    for (ion::JitActivationIterator iter(rt); !iter.done(); ++iter) {
+        uintptr_t *jitMin, *jitEnd;
+        iter.jitStackRange(jitMin, jitEnd);
 
-        uintptr_t *upto = Min(ionMin, end);
+        uintptr_t *upto = Min(jitMin, end);
         if (upto > i)
             CheckStackRootsRange(rt, i, upto, rbegin, rend);
         else
             break;
-        i = ionEnd;
+        i = jitEnd;
     }
 #endif
 
     /* The topmost Ion activiation may be beyond our prior top. */
     if (i < end)
         CheckStackRootsRange(rt, i, end, rbegin, rend);
 }
 
@@ -291,17 +291,17 @@ JS::CheckStackRoots(JSContext *cx)
         for (Rooter *p = rooters.begin(); p != rooters.end(); p++) {
             if (p->rooter >= firstScanned) {
                 Swap(*firstToScan, *p);
                 ++firstToScan;
             }
         }
     }
 
-    CheckStackRootsRangeAndSkipIon(rt, stackMin, stackEnd, firstToScan, rooters.end());
+    CheckStackRootsRangeAndSkipJit(rt, stackMin, stackEnd, firstToScan, rooters.end());
     CheckStackRootsRange(rt, cgcd->registerSnapshot.words,
                          ArrayEnd(cgcd->registerSnapshot.words),
                          firstToScan, rooters.end());
 
     // Mark all rooters as scanned.
     for (Rooter *p = rooters.begin(); p != rooters.end(); p++)
         p->rooter->scanned = true;
 }
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -64,19 +64,19 @@ IonBailoutIterator::dump() const
 
 uint32_t
 ion::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
 {
     JS_ASSERT(bailoutInfo);
     JSContext *cx = GetIonContext()->cx;
     // We don't have an exit frame.
     cx->mainThread().ionTop = NULL;
-    IonActivationIterator ionActivations(cx);
-    IonBailoutIterator iter(ionActivations, sp);
-    IonActivation *activation = ionActivations.activation();
+    JitActivationIterator jitActivations(cx->runtime());
+    IonBailoutIterator iter(jitActivations, sp);
+    JitActivation *activation = jitActivations.activation()->asJit();
 
     IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     JS_ASSERT(IsBaselineEnabled(cx));
 
     *bailoutInfo = NULL;
     uint32_t retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo);
     JS_ASSERT(retval == BAILOUT_RETURN_OK ||
@@ -95,19 +95,19 @@ ion::InvalidationBailout(InvalidationBai
                          BaselineBailoutInfo **bailoutInfo)
 {
     sp->checkInvariants();
 
     JSContext *cx = GetIonContext()->cx;
 
     // We don't have an exit frame.
     cx->mainThread().ionTop = NULL;
-    IonActivationIterator ionActivations(cx);
-    IonBailoutIterator iter(ionActivations, sp);
-    IonActivation *activation = ionActivations.activation();
+    JitActivationIterator jitActivations(cx->runtime());
+    IonBailoutIterator iter(jitActivations, sp);
+    JitActivation *activation = jitActivations.activation()->asJit();
 
     IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     // Note: the frame size must be computed before we return from this function.
     *frameSizeOut = iter.topFrameSize();
 
     JS_ASSERT(IsBaselineEnabled(cx));
 
--- a/js/src/ion/Bailouts.h
+++ b/js/src/ion/Bailouts.h
@@ -117,18 +117,18 @@ class InvalidationBailoutStack;
 class IonBailoutIterator : public IonFrameIterator
 {
     MachineState machine_;
     uint32_t snapshotOffset_;
     size_t topFrameSize_;
     IonScript *topIonScript_;
 
   public:
-    IonBailoutIterator(const IonActivationIterator &activations, BailoutStack *sp);
-    IonBailoutIterator(const IonActivationIterator &activations, InvalidationBailoutStack *sp);
+    IonBailoutIterator(const JitActivationIterator &activations, BailoutStack *sp);
+    IonBailoutIterator(const JitActivationIterator &activations, InvalidationBailoutStack *sp);
 
     SnapshotOffset snapshotOffset() const {
         JS_ASSERT(topIonScript_);
         return snapshotOffset_;
     }
     const MachineState &machineState() const {
         return machine_;
     }
--- a/js/src/ion/BaselineBailouts.cpp
+++ b/js/src/ion/BaselineBailouts.cpp
@@ -994,17 +994,17 @@ InitFromBailout(JSContext *cx, HandleScr
     JS_ASSERT(rectReturnAddr);
     if (!builder.writePtr(rectReturnAddr, "ReturnAddr"))
         return false;
 
     return true;
 }
 
 uint32_t
-ion::BailoutIonToBaseline(JSContext *cx, IonActivation *activation, IonBailoutIterator &iter,
+ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
                           bool invalidate, BaselineBailoutInfo **bailoutInfo)
 {
     JS_ASSERT(bailoutInfo != NULL);
     JS_ASSERT(*bailoutInfo == NULL);
 
     // The caller of the top frame must be one of the following:
     //      OptimizedJS - Ion calling into Ion.
     //      BaselineStub - Baseline calling into Ion.
@@ -1209,17 +1209,20 @@ ion::FinishBailoutToBaseline(BaselineBai
     BaselineFrame *topFrame = GetTopBaselineFrame(cx);
     if (topFrame->scopeChain() && !EnsureHasScopeObjects(cx, topFrame))
         return false;
 
     // Create arguments objects for bailed out frames, to maintain the invariant
     // that script->needsArgsObj() implies frame->hasArgsObj().
     RootedScript innerScript(cx, NULL);
     RootedScript outerScript(cx, NULL);
-    IonFrameIterator iter(cx);
+
+    JS_ASSERT(cx->mainThread().currentlyRunningInJit());
+    IonFrameIterator iter(cx->mainThread().ionTop);
+
     uint32_t frameno = 0;
     while (frameno < numFrames) {
         JS_ASSERT(!iter.isOptimizedJS());
 
         if (iter.isBaselineJS()) {
             BaselineFrame *frame = iter.baselineFrame();
 
             // If the frame doesn't even have a scope chain set yet, then it's resuming
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -671,17 +671,19 @@ ICStubCompiler::emitPostWriteBarrierSlot
 #endif // JSGC_GENERATIONAL
 
 //
 // UseCount_Fallback
 //
 static bool
 IsTopFrameConstructing(JSContext *cx)
 {
-    IonFrameIterator iter(cx);
+    JS_ASSERT(cx->mainThread().currentlyRunningInJit());
+    JitActivationIterator activations(cx->runtime());
+    IonFrameIterator iter(activations);
     JS_ASSERT(iter.type() == IonFrame_Exit);
 
     ++iter;
     JS_ASSERT(iter.type() == IonFrame_BaselineStub);
 
     ++iter;
     JS_ASSERT(iter.isBaselineJS());
 
--- a/js/src/ion/BaselineJIT.cpp
+++ b/js/src/ion/BaselineJIT.cpp
@@ -111,32 +111,36 @@ EnterBaseline(JSContext *cx, StackFrame 
 
     // Caller must construct |this| before invoking the Ion function.
     JS_ASSERT_IF(fp->isConstructing(), fp->functionThis().isObject());
 
     RootedValue result(cx, Int32Value(numActualArgs));
     {
         AssertCompartmentUnchanged pcc(cx);
         IonContext ictx(cx, NULL);
-        IonActivation activation(cx, fp);
+        JitActivation activation(cx, fp->isConstructing());
         JSAutoResolveFlags rf(cx, RESOLVE_INFER);
 
+        fp->setRunningInJit();
+
         // Pass the scope chain for global and eval frames.
         JSObject *scopeChain = NULL;
         if (!fp->isNonEvalFunctionFrame())
             scopeChain = fp->scopeChain();
 
         // For OSR, pass the number of locals + stack values.
         uint32_t numStackValues = osr ? fp->script()->nfixed + cx->regs().stackDepth() : 0;
         JS_ASSERT_IF(osr, !IsJSDEnabled(cx));
 
         AutoFlushInhibitor afi(cx->compartment()->ionCompartment());
         // Single transition point from Interpreter to Baseline.
         enter(jitcode, maxArgc, maxArgv, osr ? fp : NULL, calleeToken, scopeChain, numStackValues,
               result.address());
+
+        fp->clearRunningInJit();
     }
 
     JS_ASSERT(fp == cx->fp());
     JS_ASSERT(!cx->runtime()->hasIonReturnOverride());
 
     // The trampoline wrote the return value but did not set the HAS_RVAL flag.
     fp->setReturnValue(result);
 
@@ -808,17 +812,17 @@ ion::ToggleBaselineSPS(JSRuntime *runtim
             if (!script->hasBaselineScript())
                 continue;
             script->baselineScript()->toggleSPS(enable);
         }
     }
 }
 
 static void
-MarkActiveBaselineScripts(JSContext *cx, const IonActivationIterator &activation)
+MarkActiveBaselineScripts(JSContext *cx, const JitActivationIterator &activation)
 {
     for (ion::IonFrameIterator iter(activation); !iter.done(); ++iter) {
         switch (iter.type()) {
           case IonFrame_BaselineJS:
             iter.script()->baselineScript()->setActive();
             break;
           case IonFrame_OptimizedJS: {
             // Keep the baseline script around, since bailouts from the ion
@@ -831,24 +835,24 @@ MarkActiveBaselineScripts(JSContext *cx,
           default:;
         }
     }
 }
 
 void
 ion::MarkActiveBaselineScripts(Zone *zone)
 {
-    // First check if there is an IonActivation on the stack, so that there
+    // First check if there is a JitActivation on the stack, so that there
     // must be a valid IonContext.
-    IonActivationIterator iter(zone->rt);
-    if (!iter.more())
+    JitActivationIterator iter(zone->rt);
+    if (iter.done())
         return;
 
     // If baseline is disabled, there are no baseline scripts on the stack.
     JSContext *cx = GetIonContext()->cx;
     if (!ion::IsBaselineEnabled(cx))
         return;
 
-    for (; iter.more(); ++iter) {
+    for (; !iter.done(); ++iter) {
         if (iter.activation()->compartment()->zone() == zone)
             MarkActiveBaselineScripts(cx, iter);
     }
 }
--- a/js/src/ion/BaselineJIT.h
+++ b/js/src/ion/BaselineJIT.h
@@ -314,17 +314,17 @@ struct BaselineBailoutInfo
     // Number of baseline frames to push on the stack.
     uint32_t numFrames;
 
     // The bailout kind.
     BailoutKind bailoutKind;
 };
 
 uint32_t
-BailoutIonToBaseline(JSContext *cx, IonActivation *activation, IonBailoutIterator &iter,
+BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
                      bool invalidate, BaselineBailoutInfo **bailoutInfo);
 
 // Mark baseline scripts on the stack as active, so that they are not discarded
 // during GC.
 void
 MarkActiveBaselineScripts(Zone *zone);
 
 } // namespace ion
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -403,42 +403,16 @@ IonCompartment::getVMWrapper(const VMFun
     JS_ASSERT(rt->functionWrappers_);
     JS_ASSERT(rt->functionWrappers_->initialized());
     IonRuntime::VMWrapperMap::Ptr p = rt->functionWrappers_->readonlyThreadsafeLookup(&f);
     JS_ASSERT(p);
 
     return p->value;
 }
 
-IonActivation::IonActivation(JSContext *cx, StackFrame *fp)
-  : cx_(cx),
-    compartment_(cx->compartment()),
-    prev_(cx->mainThread().ionActivation),
-    entryfp_(fp),
-    prevIonTop_(cx->mainThread().ionTop),
-    prevIonJSContext_(cx->mainThread().ionJSContext),
-    prevpc_(NULL)
-{
-    if (fp)
-        fp->setRunningInIon();
-    cx->mainThread().ionJSContext = cx;
-    cx->mainThread().ionActivation = this;
-}
-
-IonActivation::~IonActivation()
-{
-    JS_ASSERT(cx_->mainThread().ionActivation == this);
-
-    if (entryfp_)
-        entryfp_->clearRunningInIon();
-    cx_->mainThread().ionActivation = prev();
-    cx_->mainThread().ionTop = prevIonTop_;
-    cx_->mainThread().ionJSContext = prevIonJSContext_;
-}
-
 IonCode *
 IonCode::New(JSContext *cx, uint8_t *code, uint32_t bufferSize, JSC::ExecutablePool *pool)
 {
     IonCode *codeObj = gc::NewGCThing<IonCode, CanGC>(cx, gc::FINALIZE_IONCODE, sizeof(IonCode), gc::DefaultHeap);
     if (!codeObj) {
         pool->release();
         return NULL;
     }
@@ -1831,22 +1805,27 @@ EnterIon(JSContext *cx, StackFrame *fp, 
     }
 
     // Caller must construct |this| before invoking the Ion function.
     JS_ASSERT_IF(fp->isConstructing(), fp->functionThis().isObject());
     RootedValue result(cx, Int32Value(numActualArgs));
     {
         AssertCompartmentUnchanged pcc(cx);
         IonContext ictx(cx, NULL);
-        IonActivation activation(cx, fp);
+        JitActivation activation(cx, fp->isConstructing());
         JSAutoResolveFlags rf(cx, RESOLVE_INFER);
         AutoFlushInhibitor afi(cx->compartment()->ionCompartment());
+
+        fp->setRunningInJit();
+
         // Single transition point from Interpreter to Ion.
         enter(jitcode, maxArgc, maxArgv, fp, calleeToken, /* scopeChain = */ NULL, 0,
               result.address());
+
+        fp->clearRunningInJit();
     }
 
     JS_ASSERT(fp == cx->fp());
     JS_ASSERT(!cx->runtime()->hasIonReturnOverride());
 
     // The trampoline wrote the return value but did not set the HAS_RVAL flag.
     fp->setReturnValue(result);
 
@@ -1896,54 +1875,28 @@ ion::FastInvoke(JSContext *cx, HandleFun
 
     IonScript *ion = fun->nonLazyScript()->ionScript();
     IonCode *code = ion->method();
     void *jitcode = code->raw();
 
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT(!ion->bailoutExpected());
 
-    bool clearCallingIntoIon = false;
-    StackFrame *fp = cx->fp();
-
-    // Two cases we have to handle:
-    //
-    // (1) fp does not begin an Ion activation. This works exactly
-    //     like invoking Ion from JM: entryfp is set to fp and fp
-    //     has the callingIntoIon flag set.
-    //
-    // (2) fp already begins another IonActivation, for instance:
-    //        JM -> Ion -> array_sort -> Ion
-    //     In this cas we use an IonActivation with entryfp == NULL
-    //     and prevpc != NULL.
-    IonActivation activation(cx, NULL);
-    if (!fp->beginsIonActivation()) {
-        fp->setCallingIntoIon();
-        clearCallingIntoIon = true;
-        activation.setEntryFp(fp);
-    } else {
-        JS_ASSERT(!activation.entryfp());
-    }
-
-    activation.setPrevPc(cx->regs().pc);
+    JitActivation activation(cx, /* firstFrameIsConstructing = */false);
 
     EnterIonCode enter = cx->compartment()->ionCompartment()->enterJIT();
     void *calleeToken = CalleeToToken(fun);
 
     RootedValue result(cx, Int32Value(args.length()));
     JS_ASSERT(args.length() >= fun->nargs);
 
     JSAutoResolveFlags rf(cx, RESOLVE_INFER);
-    enter(jitcode, args.length() + 1, args.array() - 1, fp, calleeToken,
+    enter(jitcode, args.length() + 1, args.array() - 1, NULL, calleeToken,
           /* scopeChain = */ NULL, 0, result.address());
 
-    if (clearCallingIntoIon)
-        fp->clearCallingIntoIon();
-
-    JS_ASSERT(fp == cx->fp());
     JS_ASSERT(!cx->runtime()->hasIonReturnOverride());
 
     args.rval().set(result);
 
     JS_ASSERT_IF(result.isMagic(), result.isMagic(JS_ION_ERROR));
     return result.isMagic() ? IonExec_Error : IonExec_Ok;
 }
 
@@ -2078,22 +2031,22 @@ ion::InvalidateAll(FreeOp *fop, Zone *zo
 {
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
         if (!comp->ionCompartment())
             continue;
         CancelOffThreadIonCompile(comp, NULL);
         FinishAllOffThreadCompilations(comp->ionCompartment());
     }
 
-    for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter) {
+    for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) {
         if (iter.activation()->compartment()->zone() == zone) {
             IonContext ictx(zone->rt);
             AutoFlushCache afc("InvalidateAll", zone->rt->ionRuntime());
             IonSpew(IonSpew_Invalidate, "Invalidating all frames for GC");
-            InvalidateActivation(fop, iter.top(), true);
+            InvalidateActivation(fop, iter.jitTop(), true);
         }
     }
 }
 
 
 void
 ion::Invalidate(types::TypeCompartment &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses)
@@ -2121,18 +2074,18 @@ ion::Invalidate(types::TypeCompartment &
         }
     }
 
     if (!anyInvalidation) {
         IonSpew(IonSpew_Invalidate, " No IonScript invalidation.");
         return;
     }
 
-    for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter)
-        InvalidateActivation(fop, iter.top(), false);
+    for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter)
+        InvalidateActivation(fop, iter.jitTop(), false);
 
     // Drop the references added above. If a script was never active, its
     // IonScript will be immediately destroyed. Otherwise, it will be held live
     // until its last invalidated frame is destroyed.
     for (size_t i = 0; i < invalid.length(); i++) {
         types::CompilerOutput &co = *invalid[i].compilerOutput(types);
         ExecutionMode executionMode = SequentialExecution;
         switch (co.kind()) {
--- a/js/src/ion/IonCompartment.h
+++ b/js/src/ion/IonCompartment.h
@@ -23,17 +23,16 @@ enum EnterJitType {
     EnterJitBaseline = 0,
     EnterJitOptimized = 1
 };
 
 typedef void (*EnterIonCode)(void *code, int argc, Value *argv, StackFrame *fp,
                              CalleeToken calleeToken, JSObject *scopeChain,
                              size_t numStackValues, Value *vp);
 
-class IonActivation;
 class IonBuilder;
 
 typedef Vector<IonBuilder*, 0, SystemAllocPolicy> OffThreadCompilationVector;
 
 // ICStubSpace is an abstraction for allocation policy and storage for stub data.
 // There are two kinds of stubs: optimized stubs and fallback stubs (the latter
 // also includes stubs that can make non-tail calls that can GC).
 //
@@ -170,17 +169,17 @@ class IonRuntime
     void setFlusher(AutoFlushCache *fl) {
         if (!flusher_ || !fl)
             flusher_ = fl;
     }
 };
 
 class IonCompartment
 {
-    friend class IonActivation;
+    friend class JitActivation;
 
     // Ion state for the compartment's runtime.
     IonRuntime *rt;
 
     // Any scripts for which off thread compilation has successfully finished,
     // failed, or been cancelled. All off thread compilations which are started
     // will eventually appear in this list asynchronously. Protected by the
     // runtime's analysis lock.
@@ -305,73 +304,16 @@ class IonCompartment
     void setFlusher(AutoFlushCache *fl) {
         rt->setFlusher(fl);
     }
     OptimizedICStubSpace *optimizedStubSpace() {
         return &optimizedStubSpace_;
     }
 };
 
-class IonActivation
-{
-  private:
-    JSContext *cx_;
-    JSCompartment *compartment_;
-    IonActivation *prev_;
-    StackFrame *entryfp_;
-    uint8_t *prevIonTop_;
-    JSContext *prevIonJSContext_;
-
-    // When creating an activation without a StackFrame, this field is used
-    // to communicate the calling pc for ScriptFrameIter.
-    jsbytecode *prevpc_;
-
-  public:
-    IonActivation(JSContext *cx, StackFrame *fp);
-    ~IonActivation();
-
-    StackFrame *entryfp() const {
-        return entryfp_;
-    }
-    IonActivation *prev() const {
-        return prev_;
-    }
-    uint8_t *prevIonTop() const {
-        return prevIonTop_;
-    }
-    jsbytecode *prevpc() const {
-        JS_ASSERT_IF(entryfp_, entryfp_->callingIntoIon());
-        return prevpc_;
-    }
-    void setEntryFp(StackFrame *fp) {
-        JS_ASSERT_IF(fp, !entryfp_);
-        entryfp_ = fp;
-    }
-    void setPrevPc(jsbytecode *pc) {
-        JS_ASSERT_IF(pc, !prevpc_);
-        prevpc_ = pc;
-    }
-    JSCompartment *compartment() const {
-        return compartment_;
-    }
-    bool empty() const {
-        // If we have an entryfp, this activation is active. However, if
-        // FastInvoke is used, entryfp may be NULL and a non-NULL prevpc
-        // indicates this activation is not empty.
-        return !entryfp_ && !prevpc_;
-    }
-
-    static inline size_t offsetOfPrevPc() {
-        return offsetof(IonActivation, prevpc_);
-    }
-    static inline size_t offsetOfEntryFp() {
-        return offsetof(IonActivation, entryfp_);
-    }
-};
-
 // Called from JSCompartment::discardJitCode().
 void InvalidateAll(FreeOp *fop, JS::Zone *zone);
 void FinishInvalidation(FreeOp *fop, JSScript *script);
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_compartment_h__
--- a/js/src/ion/IonFrameIterator.h
+++ b/js/src/ion/IonFrameIterator.h
@@ -10,16 +10,20 @@
 #include "jstypes.h"
 #include "IonCode.h"
 #include "SnapshotReader.h"
 
 class JSFunction;
 class JSScript;
 
 namespace js {
+    class ActivationIterator;
+};
+
+namespace js {
 namespace ion {
 
 enum FrameType
 {
     // A JS frame is analagous to a js::StackFrame, representing one scripted
     // functon activation. OptimizedJS frames are used by the optimizing compiler.
     IonFrame_OptimizedJS,
 
@@ -47,59 +51,58 @@ enum FrameType
     IonFrame_Unwound_BaselineStub,
 
     // An unwound rectifier frame is a rectifier frame signalling that its callee
     // frame has been turned into an exit frame (see EnsureExitFrame).
     IonFrame_Unwound_Rectifier,
 
     // An exit frame is necessary for transitioning from a JS frame into C++.
     // From within C++, an exit frame is always the last frame in any
-    // IonActivation.
+    // JitActivation.
     IonFrame_Exit,
 
     // An OSR frame is added when performing OSR from within a bailout. It
     // looks like a JS frame, but does not push scripted arguments, as OSR
     // reads arguments from a js::StackFrame.
     IonFrame_Osr
 };
 
 class IonCommonFrameLayout;
 class IonJSFrameLayout;
 class IonExitFrameLayout;
 
-class IonActivation;
-class IonActivationIterator;
+class BaselineFrame;
 
-class BaselineFrame;
+class JitActivation;
 
 class IonFrameIterator
 {
   protected:
     uint8_t *current_;
     FrameType type_;
     uint8_t *returnAddressToFp_;
     size_t frameSize_;
 
   private:
     mutable const SafepointIndex *cachedSafepointIndex_;
-    const IonActivation *activation_;
+    const JitActivation *activation_;
 
     void dumpBaseline() const;
 
   public:
     IonFrameIterator(uint8_t *top)
       : current_(top),
         type_(IonFrame_Exit),
         returnAddressToFp_(NULL),
         frameSize_(0),
         cachedSafepointIndex_(NULL),
         activation_(NULL)
     { }
 
-    IonFrameIterator(const IonActivationIterator &activations);
+    IonFrameIterator(const ActivationIterator &activations);
     IonFrameIterator(IonJSFrameLayout *fp);
 
     // Current frame information.
     FrameType type() const {
         return type_;
     }
     uint8_t *fp() const {
         return current_;
@@ -198,42 +201,16 @@ class IonFrameIterator
     template <class Op>
     inline void forEachCanonicalActualArg(Op op, unsigned start, unsigned count) const;
 
     void dump() const;
 
     inline BaselineFrame *baselineFrame() const;
 };
 
-class IonActivationIterator
-{
-    uint8_t *top_;
-    IonActivation *activation_;
-
-  private:
-    void settle();
-
-  public:
-    IonActivationIterator(JSContext *cx);
-    IonActivationIterator(JSRuntime *rt);
-
-    IonActivationIterator &operator++();
-
-    IonActivation *activation() const {
-        return activation_;
-    }
-    uint8_t *top() const {
-        return top_;
-    }
-    bool more() const;
-
-    // Returns the bottom and top addresses of the current activation.
-    void ionStackRange(uintptr_t *&min, uintptr_t *&end);
-};
-
 class IonJSFrameLayout;
 class IonBailoutIterator;
 
 // Reads frame information in snapshot-encoding order (that is, outermost frame
 // to innermost frame).
 class SnapshotIterator : public SnapshotReader
 {
     IonJSFrameLayout *fp_;
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -25,23 +25,23 @@
 #include "ion/IonFrameIterator-inl.h"
 #include "ion/IonFrames-inl.h"
 #include "ion/PcScriptCache-inl.h"
 #include "vm/Probes-inl.h"
 
 namespace js {
 namespace ion {
 
-IonFrameIterator::IonFrameIterator(const IonActivationIterator &activations)
-    : current_(activations.top()),
+IonFrameIterator::IonFrameIterator(const ActivationIterator &activations)
+    : current_(activations.jitTop()),
       type_(IonFrame_Exit),
       returnAddressToFp_(NULL),
       frameSize_(0),
       cachedSafepointIndex_(NULL),
-      activation_(activations.activation())
+      activation_(activations.activation()->asJit())
 {
 }
 
 IonFrameIterator::IonFrameIterator(IonJSFrameLayout *fp)
   : current_((uint8_t *)fp),
     type_(IonFrame_OptimizedJS),
     returnAddressToFp_(fp->returnAddress()),
     frameSize_(fp->prevFrameLocalSize())
@@ -592,55 +592,16 @@ EnsureExitFrame(IonCommonFrameLayout *fr
         frame->changePrevType(IonFrame_Unwound_BaselineStub);
         return;
     }
 
     JS_ASSERT(frame->prevType() == IonFrame_OptimizedJS);
     frame->changePrevType(IonFrame_Unwound_OptimizedJS);
 }
 
-void
-IonActivationIterator::settle()
-{
-    while (activation_ && activation_->empty()) {
-        top_ = activation_->prevIonTop();
-        activation_ = activation_->prev();
-    }
-}
-
-IonActivationIterator::IonActivationIterator(JSContext *cx)
-  : top_(cx->mainThread().ionTop),
-    activation_(cx->mainThread().ionActivation)
-{
-    settle();
-}
-
-IonActivationIterator::IonActivationIterator(JSRuntime *rt)
-  : top_(rt->mainThread.ionTop),
-    activation_(rt->mainThread.ionActivation)
-{
-    settle();
-}
-
-IonActivationIterator &
-IonActivationIterator::operator++()
-{
-    JS_ASSERT(activation_);
-    top_ = activation_->prevIonTop();
-    activation_ = activation_->prev();
-    settle();
-    return *this;
-}
-
-bool
-IonActivationIterator::more() const
-{
-    return !!activation_;
-}
-
 CalleeToken
 MarkCalleeToken(JSTracer *trc, CalleeToken token)
 {
     switch (GetCalleeTokenTag(token)) {
       case CalleeToken_Function:
       {
         JSFunction *fun = CalleeTokenToFunction(token);
         MarkObjectRoot(trc, &fun, "ion-callee");
@@ -785,19 +746,19 @@ MarkBaselineStubFrame(JSTracer *trc, con
 
     if (ICStub *stub = layout->maybeStubPtr()) {
         JS_ASSERT(ICStub::CanMakeCalls(stub->kind()));
         stub->trace(trc);
     }
 }
 
 void
-IonActivationIterator::ionStackRange(uintptr_t *&min, uintptr_t *&end)
+JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end)
 {
-    IonFrameIterator frames(top());
+    IonFrameIterator frames(jitTop());
 
     if (frames.isFakeExitFrame()) {
         min = reinterpret_cast<uintptr_t *>(frames.fp());
     } else {
         IonExitFrameLayout *exitFrame = frames.exitFrame();
         IonExitFooterFrame *footer = exitFrame->footer();
         const VMFunction *f = footer->function();
         if (exitFrame->isWrapperExit() && f->outParam == Type_Handle) {
@@ -964,17 +925,17 @@ MarkIonExitFrame(JSTracer *trc, const Io
           case VMFunction::RootCell:
             gc::MarkGCThingRoot(trc, footer->outParam<void *>(), "ion-vm-out");
             break;
         }
     }
 }
 
 static void
-MarkIonActivation(JSTracer *trc, const IonActivationIterator &activations)
+MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations)
 {
     for (IonFrameIterator frames(activations); !frames.done(); ++frames) {
         switch (frames.type()) {
           case IonFrame_Exit:
             MarkIonExitFrame(trc, frames);
             break;
           case IonFrame_BaselineJS:
             frames.baselineFrame()->trace(trc);
@@ -999,33 +960,32 @@ MarkIonActivation(JSTracer *trc, const I
           default:
             JS_NOT_REACHED("unexpected frame type");
             break;
         }
     }
 }
 
 void
-MarkIonActivations(JSRuntime *rt, JSTracer *trc)
+MarkJitActivations(JSRuntime *rt, JSTracer *trc)
 {
-    for (IonActivationIterator activations(rt); activations.more(); ++activations)
-        MarkIonActivation(trc, activations);
+    for (JitActivationIterator activations(rt); !activations.done(); ++activations)
+        MarkJitActivation(trc, activations);
 }
 
 void
 AutoTempAllocatorRooter::trace(JSTracer *trc)
 {
     for (CompilerRootNode *root = temp->rootList(); root != NULL; root = root->next)
         gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root");
 }
 
 void
 GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
 {
-    JS_ASSERT(cx->fp()->beginsIonActivation());
     IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame.");
 
     JSRuntime *rt = cx->runtime();
 
     // Recover the return address.
     IonFrameIterator it(rt->mainThread.ionTop);
 
     // If the previous frame is a stub frame, skip the exit frame so that
@@ -1395,29 +1355,17 @@ IonFrameIterator::isConstructing() const
             return false;
 
         JS_ASSERT(js_CodeSpec[*pc].format & JOF_INVOKE);
 
         return JSOp(*pc) == JSOP_NEW;
     }
 
     JS_ASSERT(parent.done());
-
-    // If entryfp is not set, we entered Ion via a C++ native, like Array.map,
-    // using FastInvoke. FastInvoke is never used for constructor calls.
-    if (!activation_->entryfp())
-        return false;
-
-    // If callingIntoIon, we either entered Ion from JM or entered Ion from
-    // a C++ native using FastInvoke. In both of these cases we don't handle
-    // constructor calls.
-    if (activation_->entryfp()->callingIntoIon())
-        return false;
-    JS_ASSERT(activation_->entryfp()->runningInIon());
-    return activation_->entryfp()->isConstructing();
+    return activation_->firstFrameIsConstructing();
 }
 
 unsigned
 IonFrameIterator::numActualArgs() const
 {
     if (isScripted())
         return jsFrame()->numActualArgs();
 
--- a/js/src/ion/IonFrames.h
+++ b/js/src/ion/IonFrames.h
@@ -265,17 +265,17 @@ struct ResumeFromException
     uint32_t kind;
 };
 
 void HandleException(ResumeFromException *rfe);
 void HandleParallelFailure(ResumeFromException *rfe);
 
 void EnsureExitFrame(IonCommonFrameLayout *frame);
 
-void MarkIonActivations(JSRuntime *rt, JSTracer *trc);
+void MarkJitActivations(JSRuntime *rt, JSTracer *trc);
 void MarkIonCompilerRoots(JSTracer *trc);
 
 static inline uint32_t
 MakeFrameDescriptor(uint32_t frameSize, FrameType type)
 {
     return (frameSize << FRAMESIZE_SHIFT) | type;
 }
 
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -210,19 +210,20 @@ class MacroAssembler : public MacroAssem
         loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), dest);
         rshiftPtr(Imm32(JSString::LENGTH_SHIFT), dest);
     }
 
     void loadJSContext(const Register &dest) {
         movePtr(ImmWord(GetIonContext()->runtime), dest);
         loadPtr(Address(dest, offsetof(JSRuntime, mainThread.ionJSContext)), dest);
     }
-    void loadIonActivation(const Register &dest) {
+    void loadJitActivation(const Register &dest) {
         movePtr(ImmWord(GetIonContext()->runtime), dest);
-        loadPtr(Address(dest, offsetof(JSRuntime, mainThread.ionActivation)), dest);
+        size_t offset = offsetof(JSRuntime, mainThread) + PerThreadData::offsetOfActivation();
+        loadPtr(Address(dest, offset), dest);
     }
 
     template<typename T>
     void loadTypedOrValue(const T &src, TypedOrValueRegister dest) {
         if (dest.hasValue())
             loadValue(src, dest.valueReg());
         else
             loadUnboxedValue(src, dest.type(), dest.typedReg());
--- a/js/src/ion/arm/Bailouts-arm.cpp
+++ b/js/src/ion/arm/Bailouts-arm.cpp
@@ -107,17 +107,17 @@ class BailoutStack
             return (uint8_t *)this + sizeof(BailoutStack);
         return (uint8_t *)this + offsetof(BailoutStack, snapshotOffset_);
     }
 };
 
 } // namespace ion
 } // namespace js
 
-IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
+IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        BailoutStack *bailout)
   : IonFrameIterator(activations),
     machine_(bailout->machine())
 {
     uint8_t *sp = bailout->parentStackPointer();
     uint8_t *fp = sp + bailout->frameSize();
 
     current_ = fp;
@@ -126,34 +126,34 @@ IonBailoutIterator::IonBailoutIterator(c
     topIonScript_ = script()->ionScript();
 
     if (bailout->frameClass() == FrameSizeClass::None()) {
         snapshotOffset_ = bailout->snapshotOffset();
         return;
     }
 
     // Compute the snapshot offset from the bailout ID.
-    IonActivation *activation = activations.activation();
+    JitActivation *activation = activations.activation()->asJit();
     JSCompartment *jsCompartment = activation->compartment();
     IonCompartment *ionCompartment = jsCompartment->ionCompartment();
     IonCode *code = ionCompartment->getBailoutTable(bailout->frameClass());
     uintptr_t tableOffset = bailout->tableOffset();
     uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw());
 
     JS_ASSERT(tableOffset >= tableStart &&
               tableOffset < tableStart + code->instructionsSize());
     JS_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);
 
     uint32_t bailoutId = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1;
     JS_ASSERT(bailoutId < BAILOUT_TABLE_SIZE);
 
     snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId);
 }
 
-IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
+IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        InvalidationBailoutStack *bailout)
   : IonFrameIterator(activations),
     machine_(bailout->machine())
 {
     returnAddressToFp_ = bailout->osiPointReturnAddress();
     topIonScript_ = bailout->ionScript();
     const OsiIndex *osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_);
 
--- a/js/src/ion/x64/Bailouts-x64.cpp
+++ b/js/src/ion/x64/Bailouts-x64.cpp
@@ -44,32 +44,32 @@ class BailoutStack
 
 } // namespace ion
 } // namespace js
 
 #if defined(_WIN32)
 # pragma pack(pop)
 #endif
 
-IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
+IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        BailoutStack *bailout)
   : IonFrameIterator(activations),
     machine_(bailout->machineState())
 {
     uint8_t *sp = bailout->parentStackPointer();
     uint8_t *fp = sp + bailout->frameSize();
 
     current_ = fp;
     type_ = IonFrame_OptimizedJS;
     topFrameSize_ = current_ - sp;
     topIonScript_ = script()->ionScript();
     snapshotOffset_ = bailout->snapshotOffset();
 }
 
-IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
+IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        InvalidationBailoutStack *bailout)
   : IonFrameIterator(activations),
     machine_(bailout->machine())
 {
     returnAddressToFp_ = bailout->osiPointReturnAddress();
     topIonScript_ = bailout->ionScript();
     const OsiIndex *osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_);
 
--- a/js/src/ion/x86/Bailouts-x86.cpp
+++ b/js/src/ion/x86/Bailouts-x86.cpp
@@ -60,17 +60,17 @@ class BailoutStack
 
 } // namespace ion
 } // namespace js
 
 #if defined(_WIN32)
 # pragma pack(pop)
 #endif
 
-IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
+IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        BailoutStack *bailout)
   : IonFrameIterator(activations),
     machine_(bailout->machine())
 {
     uint8_t *sp = bailout->parentStackPointer();
     uint8_t *fp = sp + bailout->frameSize();
 
     current_ = fp;
@@ -79,34 +79,34 @@ IonBailoutIterator::IonBailoutIterator(c
     topIonScript_ = script()->ionScript();
 
     if (bailout->frameClass() == FrameSizeClass::None()) {
         snapshotOffset_ = bailout->snapshotOffset();
         return;
     }
 
     // Compute the snapshot offset from the bailout ID.
-    IonActivation *activation = activations.activation();
+    JitActivation *activation = activations.activation()->asJit();
     JSCompartment *jsCompartment = activation->compartment();
     IonCompartment *ionCompartment = jsCompartment->ionCompartment();
     IonCode *code = ionCompartment->getBailoutTable(bailout->frameClass());
     uintptr_t tableOffset = bailout->tableOffset();
     uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw());
 
     JS_ASSERT(tableOffset >= tableStart &&
               tableOffset < tableStart + code->instructionsSize());
     JS_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);
 
     uint32_t bailoutId = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1;
     JS_ASSERT(bailoutId < BAILOUT_TABLE_SIZE);
 
     snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId);
 }
 
-IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
+IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        InvalidationBailoutStack *bailout)
   : IonFrameIterator(activations),
     machine_(bailout->machine())
 {
     returnAddressToFp_ = bailout->osiPointReturnAddress();
     topIonScript_ = bailout->ionScript();
     const OsiIndex *osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_);
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/cross-context-stack-1.js
@@ -0,0 +1,17 @@
+// Error().stack (ScriptFrameIter) should not include frames from other contexts.
+function g() {
+    evaluate("function h() {\nstack = Error().stack;\n };\n h();", {newContext: true});
+}
+function f() {
+    g();
+}
+f();
+assertEq(stack,
+    "h@@evaluate:2\n" +
+    "@@evaluate:4\n");
+
+function k() {
+    evaluate("stack = Error().stack", {newContext: true});
+}
+k();
+assertEq(stack, "@@evaluate:1\n");
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -711,17 +711,17 @@ JitSupportsFloatingPoint()
 }
 
 PerThreadData::PerThreadData(JSRuntime *runtime)
   : PerThreadDataFriendFields(),
     runtime_(runtime),
     ionTop(NULL),
     ionJSContext(NULL),
     ionStackLimit(0),
-    ionActivation(NULL),
+    activation_(NULL),
     asmJSActivationStack_(NULL),
     suppressGC(0)
 {}
 
 JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
   : mainThread(this),
     interrupt(0),
 #ifdef JS_THREADSAFE
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1257,16 +1257,19 @@ JSContext::saveFrameChain()
     if (!stack.saveFrameChain())
         return false;
 
     if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_))) {
         stack.restoreFrameChain();
         return false;
     }
 
+    if (Activation *act = mainThread().activation())
+        act->saveFrameChain();
+
     if (defaultCompartmentObject_)
         setCompartment(defaultCompartmentObject_->compartment());
     else
         setCompartment(NULL);
     enterCompartmentDepth_ = 0;
 
     if (isExceptionPending())
         wrapPendingException();
@@ -1277,16 +1280,19 @@ void
 JSContext::restoreFrameChain()
 {
     SavedFrameChain sfc = savedFrameChains_.popCopy();
     setCompartment(sfc.compartment);
     enterCompartmentDepth_ = sfc.enterCompartmentCount;
 
     stack.restoreFrameChain();
 
+    if (Activation *act = mainThread().activation())
+        act->restoreFrameChain();
+
     if (isExceptionPending())
         wrapPendingException();
 }
 
 void
 JSRuntime::setGCMaxMallocBytes(size_t value)
 {
     /*
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -124,17 +124,16 @@ class AutoCycleDetector
 /* Updates references in the cycle detection set if the GC moves them. */
 extern void
 TraceCycleDetectionSet(JSTracer *trc, ObjectSet &set);
 
 class MathCache;
 
 namespace ion {
 class IonRuntime;
-class IonActivation;
 }
 
 class WeakMapBase;
 class InterpreterFrames;
 class WorkerThreadState;
 
 /*
  * GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
@@ -484,46 +483,62 @@ class PerThreadData : public js::PerThre
      */
     uint8_t             *ionTop;
     JSContext           *ionJSContext;
     uintptr_t            ionStackLimit;
 
     inline void setIonStackLimit(uintptr_t limit);
 
     /*
-     * This points to the most recent Ion activation running on the thread.
-     */
-    js::ion::IonActivation  *ionActivation;
-
-    /*
      * asm.js maintains a stack of AsmJSModule activations (see AsmJS.h). This
      * stack is used by JSRuntime::triggerOperationCallback to stop long-
      * running asm.js without requiring dynamic polling operations in the
      * generated code. Since triggerOperationCallback may run on a separate
      * thread than the JSRuntime's owner thread all reads/writes must be
      * synchronized (by rt->operationCallbackLock).
      */
   private:
+    friend class js::Activation;
+    friend class js::ActivationIterator;
     friend class js::AsmJSActivation;
 
+    /*
+     * Points to the most recent activation running on the thread.
+     * See Activation comment in vm/Stack.h.
+     */
+    js::Activation *activation_;
+
     /* See AsmJSActivation comment. Protected by rt->operationCallbackLock. */
     js::AsmJSActivation *asmJSActivationStack_;
 
   public:
+    static unsigned offsetOfActivation() {
+        return offsetof(PerThreadData, activation_);
+    }
     static unsigned offsetOfAsmJSActivationStackReadOnly() {
         return offsetof(PerThreadData, asmJSActivationStack_);
     }
 
     js::AsmJSActivation *asmJSActivationStackFromAnyThread() const {
         return asmJSActivationStack_;
     }
     js::AsmJSActivation *asmJSActivationStackFromOwnerThread() const {
         return asmJSActivationStack_;
     }
 
+    js::Activation *activation() const {
+        return activation_;
+    }
+    bool currentlyRunningInInterpreter() const {
+        return activation_->isInterpreter();
+    }
+    bool currentlyRunningInJit() const {
+        return activation_->isJit();
+    }
+
     /*
      * When this flag is non-zero, any attempt to GC will be skipped. It is used
      * to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in
      * debugging facilities that cannot tolerate a GC and would rather OOM
      * immediately, such as utilities exposed to GDB. Setting this flag is
      * extremely dangerous and should only be used when in an OOM situation or
      * in non-exposed debugging facilities.
      */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -590,31 +590,21 @@ void
 JSCompartment::purge()
 {
     dtoaCache.purge();
 }
 
 bool
 JSCompartment::hasScriptsOnStack()
 {
-    for (AllFramesIter afi(rt); !afi.done(); ++afi) {
-#ifdef JS_ION
-        // If this is an Ion frame, check the IonActivation instead
-        if (afi.isIon())
-            continue;
-#endif
-        if (afi.interpFrame()->script()->compartment() == this)
+    for (ActivationIterator iter(rt); !iter.done(); ++iter) {
+        if (iter.activation()->compartment() == this)
             return true;
     }
-#ifdef JS_ION
-    for (ion::IonActivationIterator iai(rt); iai.more(); ++iai) {
-        if (iai.activation()->compartment() == this)
-            return true;
-    }
-#endif
+
     return false;
 }
 
 bool
 JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeGC &dmgc)
 {
     bool enabledBefore = debugMode();
     bool enabledAfter = (debugModeBits & ~unsigned(DebugFromC)) || b;
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1054,17 +1054,17 @@ FormatFrame(JSContext *cx, const NonBuil
     RootedFunction fun(cx, iter.maybeCallee());
     RootedString funname(cx);
     if (fun)
         funname = fun->atom();
 
     RootedObject callObj(cx);
     AutoPropertyDescArray callProps(cx);
 
-    if (!iter.isIon() && (showArgs || showLocals)) {
+    if (!iter.isJit() && (showArgs || showLocals)) {
         JSAbstractFramePtr frame(Jsvalify(iter.abstractFramePtr()));
         callObj = frame.callObject(cx);
         if (callObj)
             callProps.fetch(callObj);
     }
 
     RootedValue thisVal(cx);
     AutoPropertyDescArray thisProps(cx);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -871,28 +871,25 @@ js_fun_apply(JSContext *cx, unsigned arg
      * GuardFunApplyArgumentsOptimization already called IsOptimizedArguments,
      * so we don't need to here. This is not an optimization: we can't rely on
      * cx->fp (since natives can be called directly from JSAPI).
      */
     if (vp[3].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
         /*
          * Pretend we have been passed the 'arguments' object for the current
          * function and read actuals out of the frame.
-         *
-         * N.B. Changes here need to be propagated to stubs::SplatApplyArgs.
          */
         /* Steps 4-6. */
-        StackFrame *fp = cx->fp();
 
 #ifdef JS_ION
         // We do not want to use ScriptFrameIter to abstract here because this
         // is supposed to be a fast path as opposed to ScriptFrameIter which is
         // doing complex logic to settle on the next frame twice.
-        if (fp->beginsIonActivation()) {
-            ion::IonActivationIterator activations(cx);
+        if (cx->mainThread().currentlyRunningInJit()) {
+            ion::JitActivationIterator activations(cx->runtime());
             ion::IonFrameIterator frame(activations);
             if (frame.isNative()) {
                 // Stop on the next Ion JS Frame.
                 ++frame;
                 if (frame.isOptimizedJS()) {
                     ion::InlineFrameIterator iter(cx, &frame);
 
                     unsigned length = iter.numActualArgs();
@@ -926,16 +923,17 @@ js_fun_apply(JSContext *cx, unsigned arg
                 JS_ASSERT(frame.isBaselineJS());
 
                 if (!PushBaselineFunApplyArguments(cx, frame, args, vp))
                     return false;
             }
         } else
 #endif
         {
+            StackFrame *fp = cx->fp();
             unsigned length = fp->numActualArgs();
             JS_ASSERT(length <= StackSpace::ARGS_LENGTH_MAX);
 
             if (!cx->stack.pushInvokeArgs(cx, length, &args))
                 return false;
 
             /* Push fval, obj, and aobj's elements as args. */
             args.setCallee(fval);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -691,26 +691,16 @@ FixObjectType(JSContext *cx, HandleObjec
 
 /* Interface helpers for JSScript*. */
 extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc,
                               const js::Value &rval);
 extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc,
                               js::types::Type type);
 
 inline bool
-UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
-{
-    if (!fp->isConstructing() || !cx->typeInferenceEnabled() || !fp->prev())
-        return false;
-
-    JSScript *prevScript = fp->prev()->script();
-    return UseNewType(cx, prevScript, fp->prevpc());
-}
-
-inline bool
 UseNewTypeForClone(JSFunction *fun)
 {
     if (!fun->isInterpreted())
         return false;
 
     if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite)
         return true;
 
@@ -976,24 +966,17 @@ TypeScript::MonitorUnknown(JSContext *cx
 {
     if (cx->typeInferenceEnabled())
         TypeDynamicResult(cx, script, pc, Type::UnknownType());
 }
 
 /* static */ inline void
 TypeScript::GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc)
 {
-#ifdef JS_ION
-    if (cx->fp()->beginsIonActivation()) {
-        ion::GetPcScript(cx, script, pc);
-        return;
-    }
-#endif
-    *script = cx->fp()->script();
-    *pc = cx->regs().pc;
+    *script = cx->stack.currentScript(pc);
 }
 
 /* static */ inline void
 TypeScript::MonitorOverflow(JSContext *cx)
 {
     RootedScript script(cx);
     jsbytecode *pc;
     GetPcScript(cx, script.address(), &pc);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5216,29 +5216,29 @@ js_DumpStackFrame(JSContext *cx, StackFr
     /* This should only called during live debugging. */
     ScriptFrameIter i(cx, ScriptFrameIter::GO_THROUGH_SAVED);
     if (!start) {
         if (i.done()) {
             fprintf(stderr, "no stack for cx = %p\n", (void*) cx);
             return;
         }
     } else {
-        while (!i.done() && !i.isIon() && i.interpFrame() != start)
+        while (!i.done() && !i.isJit() && i.interpFrame() != start)
             ++i;
 
         if (i.done()) {
             fprintf(stderr, "fp = %p not found in cx = %p\n",
                     (void *)start, (void *)cx);
             return;
         }
     }
 
     for (; !i.done(); ++i) {
-        if (i.isIon())
-            fprintf(stderr, "IonFrame\n");
+        if (i.isJit())
+            fprintf(stderr, "JIT frame\n");
         else
             fprintf(stderr, "StackFrame at %p\n", (void *) i.interpFrame());
 
         if (i.isFunctionFrame()) {
             fprintf(stderr, "callee fun: ");
             dumpValue(i.calleev());
         } else {
             fprintf(stderr, "global frame, no callee");
@@ -5247,35 +5247,35 @@ js_DumpStackFrame(JSContext *cx, StackFr
 
         fprintf(stderr, "file %s line %u\n",
                 i.script()->filename(), (unsigned) i.script()->lineno);
 
         if (jsbytecode *pc = i.pc()) {
             fprintf(stderr, "  pc = %p\n", pc);
             fprintf(stderr, "  current op: %s\n", js_CodeName[*pc]);
         }
-        if (!i.isIon())
+        if (!i.isJit())
             MaybeDumpObject("blockChain", i.interpFrame()->maybeBlockChain());
         MaybeDumpValue("this", i.thisv());
-        if (!i.isIon()) {
+        if (!i.isJit()) {
             fprintf(stderr, "  rval: ");
             dumpValue(i.interpFrame()->returnValue());
             fputc('\n', stderr);
         }
 
         fprintf(stderr, "  flags:");
         if (i.isConstructing())
             fprintf(stderr, " constructing");
-        if (!i.isIon() && i.interpFrame()->isDebuggerFrame())
+        if (!i.isJit() && i.interpFrame()->isDebuggerFrame())
             fprintf(stderr, " debugger");
         if (i.isEvalFrame())
             fprintf(stderr, " eval");
-        if (!i.isIon() && i.interpFrame()->isYielding())
+        if (!i.isJit() && i.interpFrame()->isYielding())
             fprintf(stderr, " yielding");
-        if (!i.isIon() && i.interpFrame()->isGeneratorFrame())
+        if (!i.isJit() && i.interpFrame()->isGeneratorFrame())
             fprintf(stderr, " generator");
         fputc('\n', stderr);
 
         fprintf(stderr, "  scopeChain: (JSObject *) %p\n", (void *) i.scopeChain());
 
         fputc('\n', stderr);
     }
 }
@@ -5288,17 +5288,17 @@ js_DumpBacktrace(JSContext *cx)
     Sprinter sprinter(cx);
     sprinter.init();
     size_t depth = 0;
     for (ScriptFrameIter i(cx); !i.done(); ++i, ++depth) {
         const char *filename = JS_GetScriptFilename(cx, i.script());
         unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
         JSScript *script = i.script();
         sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
-                        depth, (i.isIon() ? 0 : i.interpFrame()), filename, line,
+                        depth, (i.isJit() ? 0 : i.interpFrame()), filename, line,
                         script, i.pc() - script->code);
     }
     fprintf(stdout, "%s", sprinter.string());
 }
 void
 JSObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, JS::ObjectsExtraSizes *sizes)
 {
     if (hasDynamicSlots())
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1457,20 +1457,20 @@ FindStartPC(JSContext *cx, ScriptFrameIt
         return true;
 
     /*
      * Fall back on *valuepc as start pc if this frame is calling .apply and
      * the methodjit has "splatted" the arguments array, bumping the caller's
      * stack pointer and skewing it from what static analysis in pcstack.init
      * would compute.
      *
-     * FIXME: also fall back if iter.isIonOptimizedJS(), since the stack snapshot
-     * may be for the previous pc (see bug 831120).
+     * FIXME: also fall back if iter.isIon(), since the stack snapshot may be
+     * for the previous pc (see bug 831120).
      */
-    if (iter.isIonOptimizedJS())
+    if (iter.isIon())
         return true;
 
     *valuepc = NULL;
 
     PCStack pcstack;
     if (!pcstack.init(cx, iter.script(), current))
         return false;
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2880,27 +2880,27 @@ JSScript::argumentsOptimizationFailed(JS
      * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are
      * three things that need fixup:
      *  - there may be any number of activations of this script that don't have
      *    an argsObj that now need one.
      *  - jit code compiled (and possible active on the stack) with the static
      *    assumption of !script->needsArgsObj();
      *  - type inference data for the script assuming script->needsArgsObj
      */
-    for (AllFramesIter i(cx->runtime()); !i.done(); ++i) {
+    for (AllFramesIter i(cx); !i.done(); ++i) {
         /*
          * We cannot reliably create an arguments object for Ion activations of
          * this script.  To maintain the invariant that "script->needsArgsObj
          * implies fp->hasArgsObj", the Ion bail mechanism will create an
          * arguments object right after restoring the StackFrame and before
          * entering the interpreter (in ion::ThunkToInterpreter).  This delay is
-         * safe since the engine avoids any observation of a StackFrame when it
-         * beginsIonActivation (see ScriptFrameIter::interpFrame comment).
+         * safe since the engine avoids any observation of a StackFrame when it's
+         * runningInJit (see ScriptFrameIter::interpFrame comment).
          */
-        if (i.isIonOptimizedJS())
+        if (i.isIon())
             continue;
         AbstractFramePtr frame = i.abstractFramePtr();
         if (frame.isFunctionFrame() && frame.script() == script) {
             ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, frame);
             if (!argsobj) {
                 /*
                  * We can't leave stack frames with script->needsArgsObj but no
                  * arguments object. It is, however, safe to leave frames with
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -23,17 +23,17 @@
 #endif
 
 using namespace js;
 using namespace js::gc;
 
 static void
 CopyStackFrameArguments(const AbstractFramePtr frame, HeapValue *dst, unsigned totalArgs)
 {
-    JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInIon());
+    JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInJit());
 
     JS_ASSERT(Max(frame.numActualArgs(), frame.numFormalArgs()) == totalArgs);
 
     /* Copy arguments. */
     Value *src = frame.argv();
     Value *end = src + totalArgs;
     while (src != end)
         (dst++)->init(*src++);
@@ -133,17 +133,17 @@ struct CopyScriptFrameIterArgs
 {
     ScriptFrameIter &iter_;
 
     CopyScriptFrameIterArgs(ScriptFrameIter &iter)
       : iter_(iter)
     { }
 
     void copyArgs(JSContext *cx, HeapValue *dstBase, unsigned totalArgs) const {
-        if (!iter_.isIon()) {
+        if (!iter_.isJit()) {
             CopyStackFrameArguments(iter_.abstractFramePtr(), dstBase, totalArgs);
             return;
         }
 
         /* Copy actual arguments. */
         iter_.ionForEachCanonicalActualArg(cx, CopyToHeap(dstBase));
 
         /* Define formals which are not part of the actuals. */
@@ -160,17 +160,17 @@ struct CopyScriptFrameIterArgs
         }
     }
 
     /*
      * Ion frames are copying every argument onto the stack, other locations are
      * invalid.
      */
     void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) {
-        if (!iter_.isIon())
+        if (!iter_.isJit())
             ArgumentsObject::MaybeForwardToCallObject(iter_.abstractFramePtr(), obj, data);
     }
 };
 
 template <typename CopyArgs>
 /* static */ ArgumentsObject *
 ArgumentsObject::create(JSContext *cx, HandleScript script, HandleFunction callee, unsigned numActuals,
                         CopyArgs &copy)
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2024,30 +2024,27 @@ Debugger::getDebuggees(JSContext *cx, un
     return true;
 }
 
 JSBool
 Debugger::getNewestFrame(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "getNewestFrame", args, dbg);
 
-    /*
-     * cx->fp() would return the topmost frame in the current context.
-     * Since there may be multiple contexts, use AllFramesIter instead.
-     */
-    for (AllFramesIter i(cx->runtime()); !i.done(); ++i) {
+    /* Since there may be multiple contexts, use AllFramesIter. */
+    for (AllFramesIter i(cx); !i.done(); ++i) {
         /*
          * Debug-mode currently disables Ion compilation in the compartment of
          * the debuggee.
          */
-        if (i.isIonOptimizedJS())
+        if (i.isIon())
             continue;
         if (dbg->observesFrame(i.abstractFramePtr())) {
-            ScriptFrameIter iter(i.seg()->cx(), ScriptFrameIter::GO_THROUGH_SAVED);
-            while (iter.isIonOptimizedJS() || iter.abstractFramePtr() != i.abstractFramePtr())
+            ScriptFrameIter iter(i.activation()->cx(), ScriptFrameIter::GO_THROUGH_SAVED);
+            while (iter.isIon() || iter.abstractFramePtr() != i.abstractFramePtr())
                 ++iter;
             return dbg->getScriptFrame(cx, iter, args.rval());
         }
     }
     args.rval().setNull();
     return true;
 }
 
@@ -3850,17 +3847,17 @@ DebuggerFrame_getThis(JSContext *cx, uns
 
 static JSBool
 DebuggerFrame_getOlder(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_FRAME(cx, argc, vp, "get this", args, thisobj, iter);
     Debugger *dbg = Debugger::fromChildJSObject(thisobj);
 
     for (++iter; !iter.done(); ++iter) {
-        if (iter.isIonOptimizedJS())
+        if (iter.isIon())
             continue;
         if (dbg->observesFrame(iter.abstractFramePtr()))
             return dbg->getScriptFrame(cx, iter, args.rval());
     }
     args.rval().setNull();
     return true;
 }
 
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -72,17 +72,17 @@ ComputeImplicitThis(JSContext *cx, Handl
 
     vp.setObject(*nobj);
     return true;
 }
 
 inline bool
 ComputeThis(JSContext *cx, AbstractFramePtr frame)
 {
-    JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInIon());
+    JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInJit());
     if (frame.thisValue().isObject())
         return true;
     RootedValue thisv(cx, frame.thisValue());
     if (frame.isFunctionFrame()) {
         if (frame.fun()->strict() || frame.fun()->isSelfHostedBuiltin())
             return true;
         /*
          * Eval function frames have their own |this| slot, which is a copy of the function's
@@ -727,17 +727,17 @@ ToIdOperation(JSContext *cx, HandleScrip
 }
 
 static JS_ALWAYS_INLINE bool
 GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObject,
                           HandleValue rref, MutableHandleValue res)
 {
     do {
         // Don't call GetPcScript (needed for analysis) from inside Ion since it's expensive.
-        bool analyze = !cx->fp()->beginsIonActivation();
+        bool analyze = cx->mainThread().currentlyRunningInInterpreter();
 
         uint32_t index;
         if (IsDefinitelyIndex(rref, &index)) {
             if (analyze && !objArg->isNative() && !objArg->isTypedArray()) {
                 JSScript *script = NULL;
                 jsbytecode *pc = NULL;
                 types::TypeScript::GetPcScript(cx, &script, &pc);
 
@@ -888,17 +888,17 @@ SetObjectElementOperation(JSContext *cx,
     if (obj->isNative() && JSID_IS_INT(id)) {
         uint32_t length = obj->getDenseInitializedLength();
         int32_t i = JSID_TO_INT(id);
         if ((uint32_t)i >= length) {
             // In an Ion activation, GetPcScript won't work.  For non-baseline activations,
             // that's ok, because optimized ion doesn't generate analysis info.  However,
             // baseline must generate this information, so it passes the script and pc in
             // as arguments.
-            if (script || !cx->fp()->beginsIonActivation()) {
+            if (script || cx->mainThread().currentlyRunningInInterpreter()) {
                 JS_ASSERT(!!script == !!pc);
                 if (!script)
                     types::TypeScript::GetPcScript(cx, script.address(), &pc);
 
                 if (script->hasAnalysis())
                     script->analysis()->getCode(pc).arrayWriteHole = true;
             }
         }
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -292,17 +292,16 @@ js::RunScript(JSContext *cx, StackFrame 
     JS_CHECK_RECURSION(cx, return false);
 
     // Check to see if useNewType flag should be set for this frame.
     if (fp->isFunctionFrame() && fp->isConstructing() && !fp->isGeneratorFrame() &&
         cx->typeInferenceEnabled())
     {
         ScriptFrameIter iter(cx);
         if (!iter.done()) {
-            ++iter;
             JSScript *script = iter.script();
             jsbytecode *pc = iter.pc();
             if (UseNewType(cx, script, pc))
                 fp->setUseNewType();
         }
     }
 
 #ifdef DEBUG
@@ -1103,16 +1102,18 @@ js::Interpret(JSContext *cx, StackFrame 
     RootedId rootId0(cx);
     RootedShape rootShape0(cx);
     RootedScript rootScript0(cx);
     DebugOnly<uint32_t> blockDepth;
 
     if (!entryFrame)
         entryFrame = regs.fp();
 
+    InterpreterActivation activation(cx, entryFrame, regs);
+
 #if JS_HAS_GENERATORS
     if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) {
         JS_ASSERT(size_t(regs.pc - script->code) <= script->length);
         JS_ASSERT(regs.stackDepth() <= script->nslots);
 
         /*
          * To support generator_throw and to catch ignored exceptions,
          * fail if cx->isExceptionPending() is true.
@@ -1428,17 +1429,17 @@ BEGIN_CASE(JSOP_STOP)
         else
             Probes::exitScript(cx, script, script->function(), regs.fp());
 
         /* The JIT inlines the epilogue. */
 #if defined(JS_ION)
   jit_return:
 #endif
 
-        /* The results of lowered call/apply frames need to be shifted. */
+        activation.popFrame(regs.fp());
         cx->stack.popInlineFrame(regs);
         SET_SCRIPT(regs.fp()->script());
 
         JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_INVOKE);
 
         /* Resume execution in the calling frame. */
         if (JS_LIKELY(interpReturnOK)) {
             TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
@@ -2221,16 +2222,18 @@ BEGIN_CASE(JSOP_FUNCALL)
     TypeMonitorCall(cx, args, construct);
 
     InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
     bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
     funScript = fun->nonLazyScript();
     if (!cx->stack.pushInlineFrame(cx, regs, args, fun, funScript, initial))
         goto error;
 
+    activation.pushFrame(regs.fp());
+
     if (newType)
         regs.fp()->setUseNewType();
 
     SET_SCRIPT(regs.fp()->script());
 
 #ifdef JS_ION
     if (!newType && ion::IsEnabled(cx)) {
         ion::MethodStatus status = ion::CanEnter(cx, script, AbstractFramePtr(regs.fp()),
@@ -3218,17 +3221,17 @@ js::Lambda(JSContext *cx, HandleFunction
     if (!clone)
         return NULL;
 
     if (fun->isArrow()) {
         // Note that this will assert if called from Ion code. Ion can't yet
         // emit code for a bound arrow function (bug 851913).
         AbstractFramePtr frame = cx->fp();
 #ifdef JS_ION
-        if (cx->fp()->runningInIon())
+        if (cx->mainThread().currentlyRunningInJit())
             frame = ion::GetTopBaselineFrame(cx);
 #endif
 
         if (!ComputeThis(cx, frame))
             return NULL;
 
         RootedValue thisval(cx, frame.thisValue());
         clone = js_fun_bind(cx, clone, thisval, NULL, 0);
@@ -3256,17 +3259,17 @@ js::DefFunOperation(JSContext *cx, Handl
      */
     RootedFunction fun(cx, funArg);
     if (fun->isNative() || fun->environment() != scopeChain) {
         fun = CloneFunctionObjectIfNotSingleton(cx, fun, scopeChain);
         if (!fun)
             return false;
     } else {
         JS_ASSERT(script->compileAndGo);
-        JS_ASSERT_IF(!cx->fp()->beginsIonActivation(),
+        JS_ASSERT_IF(cx->mainThread().currentlyRunningInInterpreter(),
                      cx->fp()->isGlobalFrame() || cx->fp()->isEvalInFunction());
     }
 
     /*
      * We define the function as a property of the variable object and not the
      * current scope chain even for the case of function expression statements
      * and functions defined by eval inside let or with blocks.
      */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1074,29 +1074,29 @@ ScopeIter::settle()
     if (frame_.isNonEvalFunctionFrame() && !frame_.fun()->isHeavyweight()) {
         if (block_) {
             type_ = Block;
             hasScopeObject_ = block_->needsClone();
         } else {
             type_ = Call;
             hasScopeObject_ = false;
         }
-    } else if (frame_.isNonStrictDirectEvalFrame() && cur_ == frame_.evalPrevScopeChain(cx->runtime())) {
+    } else if (frame_.isNonStrictDirectEvalFrame() && cur_ == frame_.evalPrevScopeChain(cx)) {
         if (block_) {
             JS_ASSERT(!block_->needsClone());
             type_ = Block;
             hasScopeObject_ = false;
         } else {
             frame_ = NullFramePtr();
         }
     } else if (frame_.isNonEvalFunctionFrame() && !frame_.hasCallObj()) {
         JS_ASSERT(cur_ == frame_.fun()->environment());
         frame_ = NullFramePtr();
     } else if (frame_.isStrictEvalFrame() && !frame_.hasCallObj()) {
-        JS_ASSERT(cur_ == frame_.evalPrevScopeChain(cx->runtime()));
+        JS_ASSERT(cur_ == frame_.evalPrevScopeChain(cx));
         frame_ = NullFramePtr();
     } else if (cur_->isWith()) {
         JS_ASSERT_IF(frame_.isFunctionFrame(), frame_.fun()->isHeavyweight());
         JS_ASSERT_IF(block_, block_->needsClone());
         JS_ASSERT_IF(block_, block_->stackDepth() < cur_->asWith().stackDepth());
         type_ = With;
         hasScopeObject_ = true;
     } else if (block_) {
@@ -1951,22 +1951,22 @@ DebugScopes::updateLiveScopes(JSContext 
      * change the scope chain since we were last called. The fp->prevUpToDate()
      * flag indicates whether the scopes of frames older than fp are already
      * included in liveScopes. It might seem simpler to have fp instead carry a
      * flag indicating whether fp itself is accurately described, but then we
      * would need to clear that flag whenever fp ran code. By storing the 'up
      * to date' bit for fp->prev() in fp, simply popping fp effectively clears
      * the flag for us, at exactly the time when execution resumes fp->prev().
      */
-    for (AllFramesIter i(cx->runtime()); !i.done(); ++i) {
+    for (AllFramesIter i(cx); !i.done(); ++i) {
         /*
          * Debug-mode currently disables Ion compilation in the compartment of
          * the debuggee.
          */
-        if (i.isIonOptimizedJS())
+        if (i.isIon())
             continue;
 
         AbstractFramePtr frame = i.abstractFramePtr();
         if (frame.scopeChain()->compartment() != cx->compartment())
             continue;
 
         for (ScopeIter si(frame, cx); !si.done(); ++si) {
             if (si.hasScopeObject()) {
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -330,32 +330,40 @@ ContextStack::popInlineFrame(FrameRegs &
 
 inline JSScript *
 ContextStack::currentScript(jsbytecode **ppc,
                             MaybeAllowCrossCompartment allowCrossCompartment) const
 {
     if (ppc)
         *ppc = NULL;
 
-    if (!hasfp())
+    Activation *act = cx_->mainThread().activation();
+    while (act && act->cx() != cx_)
+        act = act->prev();
+
+    if (!act)
         return NULL;
 
-    FrameRegs &regs = this->regs();
-    StackFrame *fp = regs.fp();
+    JS_ASSERT(act->cx() == cx_);
 
 #ifdef JS_ION
-    if (fp->beginsIonActivation()) {
+    if (act->isJit()) {
         JSScript *script = NULL;
         ion::GetPcScript(cx_, &script, ppc);
         if (!allowCrossCompartment && script->compartment() != cx_->compartment())
             return NULL;
         return script;
     }
 #endif
 
+    JS_ASSERT(act->isInterpreter());
+
+    StackFrame *fp = act->asInterpreter()->current();
+    JS_ASSERT(!fp->runningInJit());
+
     JSScript *script = fp->script();
     if (!allowCrossCompartment && script->compartment() != cx_->compartment())
         return NULL;
 
     if (ppc)
         *ppc = fp->pcQuadratic(*this);
     return script;
 }
@@ -365,17 +373,17 @@ ContextStack::currentScriptedScopeChain(
 {
     return fp()->scopeChain();
 }
 
 template <class Op>
 inline void
 ScriptFrameIter::ionForEachCanonicalActualArg(JSContext *cx, Op op)
 {
-    JS_ASSERT(isIon());
+    JS_ASSERT(isJit());
 #ifdef JS_ION
     if (data_.ionFrames_.isOptimizedJS()) {
         ionInlineFrames_.forEachCanonicalActualArg(cx, op, 0, -1);
     } else {
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         data_.ionFrames_.forEachCanonicalActualArg(op, 0, -1);
     }
 #endif
@@ -863,10 +871,37 @@ inline void
 AbstractFramePtr::popWith(JSContext *cx) const
 {
     if (isStackFrame())
         asStackFrame()->popWith(cx);
     else
         JS_NOT_REACHED("Invalid frame");
 }
 
+Activation::Activation(JSContext *cx, Kind kind)
+  : cx_(cx),
+    compartment_(cx->compartment()),
+    prev_(cx->mainThread().activation_),
+    savedFrameChain_(0),
+    kind_(kind)
+{
+    cx->mainThread().activation_ = this;
+}
+
+Activation::~Activation()
+{
+    JS_ASSERT(cx_->mainThread().activation_ == this);
+    cx_->mainThread().activation_ = prev_;
+}
+
+InterpreterActivation::InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs &regs)
+  : Activation(cx, Interpreter),
+    entry_(entry),
+    current_(entry),
+    regs_(regs)
+{}
+
+// Define destructor explicitly to silence GCC used-but-never-defined warning.
+InterpreterActivation::~InterpreterActivation()
+{}
+
 } /* namespace js */
 #endif /* Stack_inl_h__ */
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -49,23 +49,16 @@ using mozilla::PodCopy;
 
 /*****************************************************************************/
 
 void
 StackFrame::initExecuteFrame(JSScript *script, StackFrame *prevLink, AbstractFramePtr prev,
                              FrameRegs *regs, const Value &thisv, JSObject &scopeChain,
                              ExecuteType type)
 {
-     /*
-     * If |prev| is an interpreter frame, we can always prev-link to it.
-     * If |prev| is a baseline JIT frame, we prev-link to its entry frame.
-     */
-    JS_ASSERT_IF(prev.isStackFrame(), prev.asStackFrame() == prevLink);
-    JS_ASSERT_IF(prev, prevLink != NULL);
-
     /*
      * 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_BLOCKCHAIN | HAS_PREVPC;
     if (!(flags_ & GLOBAL)) {
         JS_ASSERT(prev.isFunctionFrame() || prev.isGlobalFrame());
@@ -88,21 +81,19 @@ StackFrame::initExecuteFrame(JSScript *s
 #endif
     }
 
     scopeChain_ = &scopeChain;
     prev_ = prevLink;
     prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad;
     blockChain_ = NULL;
 
-#ifdef JS_ION
-    /* Set prevBaselineFrame_ if this is an eval frame. */
+    /* Set evalInFramePrev_ if this is an eval-in-frame. */
     JS_ASSERT_IF(isDebuggerFrame(), isEvalFrame());
-    prevBaselineFrame_ = (isEvalFrame() && prev.isBaselineFrame()) ? prev.asBaselineFrame() : NULL;
-#endif
+    evalInFramePrev_ = isDebuggerFrame() ? prev : (StackFrame *)NULL;
 
 #ifdef DEBUG
     Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
     hookData_ = (void *)0xbad;
 #endif
 }
 
 template <StackFrame::TriggerPostBarriers doPostBarrier>
@@ -766,22 +757,22 @@ StackSpace::sizeOf()
 }
 
 #ifdef DEBUG
 bool
 StackSpace::containsSlow(StackFrame *fp)
 {
     if (!seg_)
         return false;
-    for (AllFramesIter i(seg_->cx()->runtime()); !i.done(); ++i) {
+    for (AllFramesIter i(seg_->cx()); !i.done(); ++i) {
         /*
          * Debug-mode currently disables Ion compilation in the compartment of
          * the debuggee.
          */
-        if (i.isIon())
+        if (i.isJit())
             continue;
         if (i.interpFrame() == fp)
             return true;
     }
     return false;
 }
 #endif
 
@@ -939,42 +930,26 @@ ContextStack::pushExecuteFrame(JSContext
      * (to start a new segment) and link the frame and call chain manually
      * below. If |evalInFrame| is a baseline JIT frame, prev-link to its entry
      * frame.
      */
     MaybeExtend extend;
     StackFrame *prevLink;
     AbstractFramePtr prev = NullFramePtr();
     if (evalInFrame) {
-        /* First, find the right segment. */
-        AllFramesIter frameIter(cx->runtime());
-        while (frameIter.isIonOptimizedJS() || frameIter.abstractFramePtr() != evalInFrame)
-            ++frameIter;
-        JS_ASSERT(frameIter.abstractFramePtr() == evalInFrame);
-
-        StackSegment &seg = *frameIter.seg();
-
-        ScriptFrameIter iter(cx->runtime(), seg);
-        /* Debug-mode currently disables Ion compilation. */
-        JS_ASSERT_IF(evalInFrame.isStackFrame(), !evalInFrame.asStackFrame()->runningInIon());
-        JS_ASSERT_IF(evalInFrame.compartment() == iter.compartment(), !iter.isIonOptimizedJS());
-        while (iter.isIonOptimizedJS() || iter.abstractFramePtr() != evalInFrame) {
-            ++iter;
-            JS_ASSERT_IF(evalInFrame.compartment() == iter.compartment(), !iter.isIonOptimizedJS());
-        }
-        JS_ASSERT(iter.abstractFramePtr() == evalInFrame);
-        prevLink = iter.data_.fp_;
+        JS_ASSERT_IF(evalInFrame.isStackFrame(), !evalInFrame.asStackFrame()->runningInJit());
+        prevLink = NULL;
         prev = evalInFrame;
         extend = CANT_EXTEND;
     } else {
         prevLink = maybefp();
         extend = CAN_EXTEND;
         if (maybefp()) {
             ScriptFrameIter iter(cx);
-            prev = iter.isIonOptimizedJS() ? maybefp() : iter.abstractFramePtr();
+            prev = iter.isIon() ? maybefp() : iter.abstractFramePtr();
         }
     }
 
     unsigned nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots;
     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, extend, &efg->pushedSeg_);
     if (!firstUnused)
         return false;
 
@@ -1096,339 +1071,257 @@ ContextStack::restoreFrameChain()
     JS_ASSERT(seg_->isEmpty());
 
     popSegment();
 }
 
 /*****************************************************************************/
 
 void
-ScriptFrameIter::poisonRegs()
-{
-    data_.pc_ = (jsbytecode *)0xbad;
-}
-
-void
-ScriptFrameIter::popFrame()
+ScriptFrameIter::popActivation()
 {
-    StackFrame *oldfp = data_.fp_;
-    JS_ASSERT(data_.seg_->contains(oldfp));
-    data_.fp_ = data_.fp_->prev();
-
-    if (data_.seg_->contains(data_.fp_))
-        data_.pc_ = oldfp->prevpc();
-    else
-        poisonRegs();
-}
-
-void
-ScriptFrameIter::settleOnNewSegment()
-{
-    if (FrameRegs *regs = data_.seg_->maybeRegs())
-        data_.pc_ = regs->pc;
-    else
-        poisonRegs();
+    ++data_.activations_;
+    settleOnActivation();
 }
 
 void
-ScriptFrameIter::startOnSegment(StackSegment *seg)
+ScriptFrameIter::popInterpreterFrame()
 {
-    data_.seg_ = seg;
-    data_.fp_ = data_.seg_->maybefp();
-    settleOnNewSegment();
+    JS_ASSERT(data_.state_ == SCRIPTED);
+
+    ++data_.interpFrames_;
+
+    if (data_.interpFrames_.done())
+        popActivation();
+    else
+        data_.pc_ = data_.interpFrames_.pc();
 }
 
-/*
- * Given the iterator's current value of fp_ (initialized on construction or
- * after operator++ popped the previous call), "settle" the iterator on a new
- * ScriptFrameIter::State value. The goal is to present the client a simple
- * linear sequence of scripted calls while covering up unpleasant stack
- * implementation details:
- *  - The frame chain can be "saved" and "restored" (see JS_SaveFrameChain).
- *    This artificially cuts the call chain and the ScriptFrameIter client may
- *    want to continue through this cut to the previous frame by passing
- *    GO_THROUGH_SAVED.
- *  - fp->prev can be in a different contiguous segment from fp. In this case,
- *    the current values of sp/pc after calling popFrame are incorrect and
- *    should be recovered from fp->prev's segment.
- */
-/* PGO causes xpcshell startup crashes with VS2010. */
-#if defined(_MSC_VER)
-# pragma optimize("g", off)
-#endif
 void
-ScriptFrameIter::settleOnNewState()
+ScriptFrameIter::settleOnActivation()
 {
-    /*
-     * There are elements of the fp_ chain that we want to skip over so iterate
-     * until we settle on one or until there are no more.
-     */
     while (true) {
-        if (!data_.fp_) {
-            if (data_.savedOption_ == GO_THROUGH_SAVED && data_.seg_->prevInContext()) {
-                startOnSegment(data_.seg_->prevInContext());
-                continue;
-            }
+        if (data_.activations_.done()) {
+            data_.state_ = DONE;
+            return;
+        }
+
+        Activation *activation = data_.activations_.activation();
+
+        // If JS_SaveFrameChain was called, stop iterating here (unless
+        // GO_THROUGH_SAVED is set).
+        if (data_.savedOption_ == STOP_AT_SAVED && activation->hasSavedFrameChain()) {
             data_.state_ = DONE;
             return;
         }
 
-        /* Check if popFrame changed segment. */
-        bool containsFrame = data_.seg_->contains(data_.fp_);
-        while (!containsFrame) {
-            /* Eval-in-frame can cross contexts, so use prevInMemory. */
-            data_.seg_ = data_.seg_->prevInMemory();
-            containsFrame = data_.seg_->contains(data_.fp_);
-
-            /* Eval-in-frame allows jumping into the middle of a segment. */
-            if (containsFrame && data_.seg_->fp() != data_.fp_) {
-                /* Avoid duplicating logic; seg_ contains fp_, so no iloop. */
-                ScriptFrameIter tmp = *this;
-                tmp.startOnSegment(data_.seg_);
-                tmp.settleOnNewState();
-                while (tmp.data_.fp_ != data_.fp_)
-                    ++tmp;
-                JS_ASSERT(!tmp.done() &&
-                          tmp.data_.seg_ == data_.seg_ &&
-                          tmp.data_.fp_ == data_.fp_);
-                *this = tmp;
-                return;
-            }
-
-            settleOnNewSegment();
+        // Skip activations from another context if needed.
+        JS_ASSERT(activation->cx());
+        JS_ASSERT(data_.cx_);
+        if (data_.contextOption_ == CURRENT_CONTEXT && activation->cx() != data_.cx_) {
+            ++data_.activations_;
+            continue;
         }
 
 #ifdef JS_ION
-        if (data_.fp_->beginsIonActivation()) {
-            /*
-             * Eval-in-frame can link to an arbitrary frame on the stack.
-             * Skip any IonActivation's until we reach the one for the
-             * current StackFrame. Treat activations with NULL entryfp
-             * (pushed by FastInvoke) as belonging to the previous
-             * activation.
-             */
-            while (true) {
-                ion::IonActivation *act = data_.ionActivations_.activation();
-                while (!act->entryfp())
-                    act = act->prev();
-                if (act->entryfp() == data_.fp_)
-                    break;
+        if (activation->isJit()) {
+            data_.ionFrames_ = ion::IonFrameIterator(data_.activations_);
 
-                ++data_.ionActivations_;
-            }
-
-            data_.ionFrames_ = ion::IonFrameIterator(data_.ionActivations_);
-
+            // Stop at the first scripted frame.
             while (!data_.ionFrames_.isScripted() && !data_.ionFrames_.done())
                 ++data_.ionFrames_;
 
-            // When invoked from JM, we don't re-use the entryfp, so we
-            // may have an empty Ion activation.
+            // It's possible to have an JitActivation with no scripted frames,
+            // for instance if we hit an over-recursion during bailout.
             if (data_.ionFrames_.done()) {
-                data_.state_ = SCRIPTED;
-                return;
+                ++data_.activations_;
+                continue;
             }
 
-            data_.state_ = ION;
-            nextIonFrame();
+            nextJitFrame();
+            data_.state_ = JIT;
             return;
         }
-#endif /* JS_ION */
+#endif
+
+        JS_ASSERT(activation->isInterpreter());
+
+        InterpreterActivation *interpAct = activation->asInterpreter();
+        data_.interpFrames_ = InterpreterFrameIterator(interpAct);
 
+        // If we OSR'ed into JIT code, skip the interpreter frame so that
+        // the same frame is not reported twice.
+        if (data_.interpFrames_.frame()->runningInJit()) {
+            ++data_.interpFrames_;
+            if (data_.interpFrames_.done()) {
+                ++data_.activations_;
+                continue;
+            }
+        }
+
+        JS_ASSERT(!data_.interpFrames_.frame()->runningInJit());
+        data_.pc_ = data_.interpFrames_.pc();
         data_.state_ = SCRIPTED;
         return;
     }
 }
-#if defined(_MSC_VER)
-# pragma optimize("", on)
-#endif
 
-ScriptFrameIter::Data::Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption)
+ScriptFrameIter::Data::Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption,
+                            ContextOption contextOption)
   : perThread_(perThread),
     cx_(cx),
-    savedOption_(savedOption)
+    savedOption_(savedOption),
+    contextOption_(contextOption),
+    pc_(NULL),
+    interpFrames_(NULL),
+    activations_(cx->runtime())
 #ifdef JS_ION
-    , ionActivations_(cx),
-    ionFrames_((uint8_t *)NULL)
-#endif
-{
-}
-
-ScriptFrameIter::Data::Data(JSContext *cx, JSRuntime *rt, StackSegment *seg)
-  : perThread_(&rt->mainThread),
-    cx_(cx),
-    savedOption_(STOP_AT_SAVED)
-#ifdef JS_ION
-    , ionActivations_(rt),
-    ionFrames_((uint8_t *)NULL)
+  , ionFrames_((uint8_t *)NULL)
 #endif
 {
 }
 
 ScriptFrameIter::Data::Data(const ScriptFrameIter::Data &other)
   : perThread_(other.perThread_),
     cx_(other.cx_),
     savedOption_(other.savedOption_),
+    contextOption_(other.contextOption_),
     state_(other.state_),
-    fp_(other.fp_),
-    seg_(other.seg_),
-    pc_(other.pc_)
+    pc_(other.pc_),
+    interpFrames_(other.interpFrames_),
+    activations_(other.activations_)
 #ifdef JS_ION
-    , ionActivations_(other.ionActivations_),
-    ionFrames_(other.ionFrames_)
+  , ionFrames_(other.ionFrames_)
 #endif
 {
 }
 
 ScriptFrameIter::ScriptFrameIter(JSContext *cx, SavedOption savedOption)
-  : data_(cx, &cx->runtime()->mainThread, savedOption)
+  : data_(cx, &cx->runtime()->mainThread, savedOption, CURRENT_CONTEXT)
 #ifdef JS_ION
     , ionInlineFrames_(cx, (js::ion::IonFrameIterator*) NULL)
 #endif
 {
-    if (StackSegment *seg = cx->stack.seg_) {
-        startOnSegment(seg);
-        settleOnNewState();
-    } else {
-        data_.state_ = DONE;
-    }
+    settleOnActivation();
 }
 
-ScriptFrameIter::ScriptFrameIter(JSRuntime *rt, StackSegment &seg)
-  : data_(seg.cx(), rt, &seg)
+ScriptFrameIter::ScriptFrameIter(JSContext *cx, ContextOption contextOption, SavedOption savedOption)
+  : data_(cx, &cx->runtime()->mainThread, savedOption, contextOption)
 #ifdef JS_ION
-    , ionInlineFrames_(seg.cx(), (js::ion::IonFrameIterator*) NULL)
+    , ionInlineFrames_(cx, (js::ion::IonFrameIterator*) NULL)
 #endif
 {
-    startOnSegment(&seg);
-    settleOnNewState();
+    settleOnActivation();
 }
 
 ScriptFrameIter::ScriptFrameIter(const ScriptFrameIter &other)
   : data_(other.data_)
 #ifdef JS_ION
-    , ionInlineFrames_(other.data_.seg_->cx(),
+    , ionInlineFrames_(other.data_.cx_,
                        data_.ionFrames_.isScripted() ? &other.ionInlineFrames_ : NULL)
 #endif
 {
 }
 
 ScriptFrameIter::ScriptFrameIter(const Data &data)
   : data_(data)
 #ifdef JS_ION
     , ionInlineFrames_(data.cx_, data_.ionFrames_.isOptimizedJS() ? &data_.ionFrames_ : NULL)
 #endif
 {
     JS_ASSERT(data.cx_);
 }
 
 #ifdef JS_ION
 void
-ScriptFrameIter::nextIonFrame()
+ScriptFrameIter::nextJitFrame()
 {
     if (data_.ionFrames_.isOptimizedJS()) {
         ionInlineFrames_.resetOn(&data_.ionFrames_);
         data_.pc_ = ionInlineFrames_.pc();
     } else {
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         data_.ionFrames_.baselineScriptAndPc(NULL, &data_.pc_);
     }
 }
 
 void
-ScriptFrameIter::popIonFrame()
+ScriptFrameIter::popJitFrame()
 {
-    // Keep fp which describes all ion frames.
-    poisonRegs();
+    JS_ASSERT(data_.state_ == JIT);
+
     if (data_.ionFrames_.isOptimizedJS() && ionInlineFrames_.more()) {
         ++ionInlineFrames_;
         data_.pc_ = ionInlineFrames_.pc();
-    } else {
-        ++data_.ionFrames_;
-        while (!data_.ionFrames_.done() && !data_.ionFrames_.isScripted())
-            ++data_.ionFrames_;
-
-        if (!data_.ionFrames_.done()) {
-            nextIonFrame();
-            return;
-        }
+        return;
+    }
 
-        // The activation has no other frames. If entryfp is NULL, it was invoked
-        // by a native written in C++, using FastInvoke, on top of another activation.
-        ion::IonActivation *activation = data_.ionActivations_.activation();
-        if (!activation->entryfp()) {
-            JS_ASSERT(activation->prevpc());
-            JS_ASSERT(data_.fp_->beginsIonActivation());
-            ++data_.ionActivations_;
-            settleOnNewState();
-            return;
-        }
+    ++data_.ionFrames_;
+    while (!data_.ionFrames_.done() && !data_.ionFrames_.isScripted())
+        ++data_.ionFrames_;
 
-        if (data_.fp_->runningInIon()) {
-            ++data_.ionActivations_;
-            popFrame();
-            settleOnNewState();
-        } else {
-            JS_ASSERT(data_.fp_->callingIntoIon());
-            data_.state_ = SCRIPTED;
-            data_.pc_ = data_.ionActivations_.activation()->prevpc();
-            ++data_.ionActivations_;
-        }
+    if (!data_.ionFrames_.done()) {
+        nextJitFrame();
+        return;
     }
-}
 
-void
-ScriptFrameIter::popBaselineDebuggerFrame()
-{
-    ion::BaselineFrame *prevBaseline = data_.fp_->prevBaselineFrame();
-
-    popFrame();
-    settleOnNewState();
-
-    JS_ASSERT(data_.state_ == ION);
-    while (!data_.ionFrames_.isBaselineJS() || data_.ionFrames_.baselineFrame() != prevBaseline)
-        popIonFrame();
+    popActivation();
 }
 #endif
 
 ScriptFrameIter &
 ScriptFrameIter::operator++()
 {
     switch (data_.state_) {
       case DONE:
         JS_NOT_REACHED("Unexpected state");
       case SCRIPTED:
+        if (interpFrame()->isDebuggerFrame()) {
+            AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
+            JS_ASSERT(eifPrev);
+
+            // Eval-in-frame can cross contexts and works across saved frame
+            // chains.
+            ContextOption prevContextOption = data_.contextOption_;
+            SavedOption prevSavedOption = data_.savedOption_;
+            data_.contextOption_ = ALL_CONTEXTS;
+            data_.savedOption_ = GO_THROUGH_SAVED;
+
+            popInterpreterFrame();
+
+            while (isIon() || abstractFramePtr() != eifPrev) {
+                if (data_.state_ == JIT) {
 #ifdef JS_ION
-        if (data_.fp_->isDebuggerFrame() && data_.fp_->prevBaselineFrame()) {
-            /* Eval-in-frame with a baseline JIT frame. */
-            popBaselineDebuggerFrame();
+                    popJitFrame();
+#else
+                    JS_NOT_REACHED("Invalid state");
+#endif
+                } else {
+                    popInterpreterFrame();
+                }
+            }
+
+            data_.contextOption_ = prevContextOption;
+            data_.savedOption_ = prevSavedOption;
+            data_.cx_ = data_.activations_.activation()->cx();
             break;
         }
-#endif
-        popFrame();
-        settleOnNewState();
+        popInterpreterFrame();
         break;
-      case ION:
+      case JIT:
 #ifdef JS_ION
-        popIonFrame();
+        popJitFrame();
         break;
 #else
         JS_NOT_REACHED("Unexpected state");
 #endif
     }
     return *this;
 }
 
-bool
-ScriptFrameIter::operator==(const ScriptFrameIter &rhs) const
-{
-    return done() == rhs.done() &&
-           (done() || data_.fp_ == rhs.data_.fp_);
-}
-
 ScriptFrameIter::Data *
 ScriptFrameIter::copyData() const
 {
 #ifdef JS_ION
     /*
      * This doesn't work for optimized Ion frames since ionInlineFrames_ is
      * not copied.
      */
@@ -1439,37 +1332,32 @@ ScriptFrameIter::copyData() const
 
 JSCompartment *
 ScriptFrameIter::compartment() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
-        return data_.fp_->compartment();
-      case ION:
-#ifdef  JS_ION
-        return data_.ionActivations_.activation()->compartment();
-#else
-        break;
-#endif
+      case JIT:
+        return data_.activations_.activation()->compartment();
     }
     JS_NOT_REACHED("Unexpected state");
     return NULL;
 }
 
 bool
 ScriptFrameIter::isFunctionFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isFunctionFrame();
-      case ION:
+      case JIT:
 #ifdef JS_ION
         JS_ASSERT(data_.ionFrames_.isScripted());
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.isFunctionFrame();
         return ionInlineFrames_.isFunctionFrame();
 #else
         break;
 #endif
@@ -1481,17 +1369,17 @@ ScriptFrameIter::isFunctionFrame() const
 bool
 ScriptFrameIter::isGlobalFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isGlobalFrame();
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.baselineFrame()->isGlobalFrame();
         JS_ASSERT(!script()->isForEval());
         return !script()->function();
 #else
         break;
 #endif
@@ -1503,17 +1391,17 @@ ScriptFrameIter::isGlobalFrame() const
 bool
 ScriptFrameIter::isEvalFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isEvalFrame();
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.baselineFrame()->isEvalFrame();
         JS_ASSERT(!script()->isForEval());
         return false;
 #else
         break;
 #endif
@@ -1526,45 +1414,45 @@ bool
 ScriptFrameIter::isNonEvalFunctionFrame() const
 {
     JS_ASSERT(!done());
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isNonEvalFunctionFrame();
-      case ION:
+      case JIT:
         return !isEvalFrame() && isFunctionFrame();
     }
     JS_NOT_REACHED("Unexpected state");
     return false;
 }
 
 bool
 ScriptFrameIter::isGeneratorFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isGeneratorFrame();
-      case ION:
+      case JIT:
         return false;
     }
     JS_NOT_REACHED("Unexpected state");
     return false;
 }
 
 bool
 ScriptFrameIter::isConstructing() const
 {
     switch (data_.state_) {
       case DONE:
         break;
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.isConstructing();
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.isConstructing();
 #else
         break;
 #endif        
@@ -1576,17 +1464,17 @@ ScriptFrameIter::isConstructing() const
 }
 
 AbstractFramePtr
 ScriptFrameIter::abstractFramePtr() const
 {
     switch (data_.state_) {
       case DONE:
         break;
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.baselineFrame();
 #endif
         break;
       case SCRIPTED:
         JS_ASSERT(interpFrame());
         return AbstractFramePtr(interpFrame());
@@ -1599,30 +1487,30 @@ void
 ScriptFrameIter::updatePcQuadratic()
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         data_.pc_ = interpFrame()->pcQuadratic(data_.cx_);
         return;
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS()) {
             ion::BaselineFrame *frame = data_.ionFrames_.baselineFrame();
-            ion::IonActivation *activation = data_.ionActivations_.activation();
+            ion::JitActivation *activation = data_.activations_.activation()->asJit();
 
-            // IonActivationIterator::top may be invalid, so create a new
+            // ActivationIterator::ionTop_ may be invalid, so create a new
             // activation iterator.
-            data_.ionActivations_ = ion::IonActivationIterator(data_.cx_);
-            while (data_.ionActivations_.activation() != activation)
-                ++data_.ionActivations_;
+            data_.activations_ = ActivationIterator(data_.cx_->runtime());
+            while (data_.activations_.activation() != activation)
+                ++data_.activations_;
 
             // Look for the current frame.
-            data_.ionFrames_ = ion::IonFrameIterator(data_.ionActivations_);
+            data_.ionFrames_ = ion::IonFrameIterator(data_.activations_);
             while (!data_.ionFrames_.isBaselineJS() || data_.ionFrames_.baselineFrame() != frame)
                 ++data_.ionFrames_;
 
             // Update the pc.
             JS_ASSERT(data_.ionFrames_.baselineFrame() == frame);
             data_.ionFrames_.baselineScriptAndPc(NULL, &data_.pc_);
             return;
         }
@@ -1636,17 +1524,17 @@ JSFunction *
 ScriptFrameIter::callee() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         JS_ASSERT(isFunctionFrame());
         return &interpFrame()->callee();
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.callee();
         JS_ASSERT(data_.ionFrames_.isOptimizedJS());
         return ionInlineFrames_.callee();
 #else
         break;
 #endif
@@ -1659,17 +1547,17 @@ Value
 ScriptFrameIter::calleev() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         JS_ASSERT(isFunctionFrame());
         return interpFrame()->calleev();
-      case ION:
+      case JIT:
 #ifdef JS_ION
         return ObjectValue(*callee());
 #else
         break;
 #endif
     }
     JS_NOT_REACHED("Unexpected state");
     return Value();
@@ -1679,17 +1567,17 @@ unsigned
 ScriptFrameIter::numActualArgs() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         JS_ASSERT(isFunctionFrame());
         return interpFrame()->numActualArgs();
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.numActualArgs();
 
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.numActualArgs();
 #else
         break;
@@ -1702,17 +1590,17 @@ ScriptFrameIter::numActualArgs() const
 Value
 ScriptFrameIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->unaliasedActual(i, checkAliasing);
-      case ION:
+      case JIT:
 #ifdef JS_ION
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.baselineFrame()->unaliasedActual(i, checkAliasing);
 #else
         break;
 #endif
     }
     JS_NOT_REACHED("Unexpected state");
@@ -1720,17 +1608,17 @@ ScriptFrameIter::unaliasedActual(unsigne
 }
 
 JSObject *
 ScriptFrameIter::scopeChain() const
 {
     switch (data_.state_) {
       case DONE:
         break;
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.scopeChain();
         return data_.ionFrames_.baselineFrame()->scopeChain();
 #else
         break;
 #endif
       case SCRIPTED:
@@ -1754,17 +1642,17 @@ ScriptFrameIter::callObj() const
 bool
 ScriptFrameIter::hasArgsObj() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->hasArgsObj();
-      case ION:
+      case JIT:
 #ifdef JS_ION
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.baselineFrame()->hasArgsObj();
 #else
         break;
 #endif
     }
     JS_NOT_REACHED("Unexpected state");
@@ -1774,17 +1662,17 @@ ScriptFrameIter::hasArgsObj() const
 ArgumentsObject &
 ScriptFrameIter::argsObj() const
 {
     JS_ASSERT(hasArgsObj());
 
     switch (data_.state_) {
       case DONE:
         break;
-      case ION:
+      case JIT:
 #ifdef JS_ION
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.baselineFrame()->argsObj();
 #else
         break;
 #endif
       case SCRIPTED:
         return interpFrame()->argsObj();
@@ -1792,30 +1680,30 @@ ScriptFrameIter::argsObj() const
     JS_NOT_REACHED("Unexpected state");
     return interpFrame()->argsObj();
 }
 
 bool
 ScriptFrameIter::computeThis() const
 {
     JS_ASSERT(!done());
-    if (!isIonOptimizedJS()) {
+    if (!isIon()) {
         JS_ASSERT(data_.cx_);
         return ComputeThis(data_.cx_, abstractFramePtr());
     }
     return true;
 }
 
 Value
 ScriptFrameIter::thisv() const
 {
     switch (data_.state_) {
       case DONE:
         break;
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS())
             return ObjectValue(*ionInlineFrames_.thisObject());
         return data_.ionFrames_.baselineFrame()->thisValue();
 #else
         break;
 #endif
       case SCRIPTED:
@@ -1826,17 +1714,17 @@ ScriptFrameIter::thisv() const
 }
 
 Value
 ScriptFrameIter::returnValue() const
 {
     switch (data_.state_) {
       case DONE:
         break;
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return *data_.ionFrames_.baselineFrame()->returnValue();
 #endif
         break;
       case SCRIPTED:
         return interpFrame()->returnValue();
     }
@@ -1845,17 +1733,17 @@ ScriptFrameIter::returnValue() const
 }
 
 void
 ScriptFrameIter::setReturnValue(const Value &v)
 {
     switch (data_.state_) {
       case DONE:
         break;
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS()) {
             data_.ionFrames_.baselineFrame()->setReturnValue(v);
             return;
         }
 #endif
         break;
       case SCRIPTED:
@@ -1866,17 +1754,17 @@ ScriptFrameIter::setReturnValue(const Va
 }
 
 size_t
 ScriptFrameIter::numFrameSlots() const
 {
     switch (data_.state_) {
       case DONE:
         break;
-     case ION: {
+     case JIT: {
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.snapshotIterator().slots() - ionInlineFrames_.script()->nfixed;
         ion::BaselineFrame *frame = data_.ionFrames_.baselineFrame();
         return frame->numValueSlots() - data_.ionFrames_.script()->nfixed;
 #else
         break;
 #endif
@@ -1891,17 +1779,17 @@ ScriptFrameIter::numFrameSlots() const
 }
 
 Value
 ScriptFrameIter::frameSlotValue(size_t index) const
 {
     switch (data_.state_) {
       case DONE:
         break;
-      case ION:
+      case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS()) {
             ion::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
             index += ionInlineFrames_.script()->nfixed;
             return si.maybeReadSlotByIndex(index);
         }
 
         index += data_.ionFrames_.script()->nfixed;
@@ -1913,139 +1801,22 @@ ScriptFrameIter::frameSlotValue(size_t i
           return interpFrame()->base()[index];
     }
     JS_NOT_REACHED("Unexpected state");
     return NullValue();
 }
 
 /*****************************************************************************/
 
-AllFramesIter::AllFramesIter(JSRuntime *rt)
-  : seg_(rt->stackSpace.seg_),
-    fp_(seg_ ? seg_->maybefp() : NULL)
-#ifdef JS_ION
-    , ionActivations_(rt),
-    ionFrames_((uint8_t *)NULL)
-#endif
-{
-    settleOnNewState();
-}
-
-#ifdef JS_ION
-void
-AllFramesIter::popIonFrame()
-{
-    JS_ASSERT(state_ == ION);
-
-    ++ionFrames_;
-    while (!ionFrames_.done() && !ionFrames_.isScripted())
-        ++ionFrames_;
-
-    if (!ionFrames_.done())
-        return;
-
-    // The activation has no other frames. If entryfp is NULL, it was invoked
-    // by a native written in C++, using FastInvoke, on top of another activation.
-    ion::IonActivation *activation = ionActivations_.activation();
-    if (!activation->entryfp()) {
-        JS_ASSERT(activation->prevpc());
-        JS_ASSERT(fp_->beginsIonActivation());
-        ++ionActivations_;
-        settleOnNewState();
-        return;
-    }
-
-    if (fp_->runningInIon()) {
-        ++ionActivations_;
-        fp_ = fp_->prev();
-        settleOnNewState();
-    } else {
-        JS_ASSERT(fp_->callingIntoIon());
-        state_ = SCRIPTED;
-        ++ionActivations_;
-    }
-}
-#endif
-
-AllFramesIter&
-AllFramesIter::operator++()
+JSObject *
+AbstractFramePtr::evalPrevScopeChain(JSContext *cx) const
 {
-    switch (state_) {
-      case SCRIPTED:
-        fp_ = fp_->prev();
-        settleOnNewState();
-        break;
-#ifdef JS_ION
-      case ION:
-        popIonFrame();
-        break;
-#endif
-      case DONE:
-      default:
-        JS_NOT_REACHED("Unexpeced state");
-    }
-    return *this;
-}
-
-void
-AllFramesIter::settleOnNewState()
-{
-    while (seg_ && (!fp_ || !seg_->contains(fp_))) {
-        seg_ = seg_->prevInMemory();
-        fp_ = seg_ ? seg_->maybefp() : NULL;
-    }
-
-    JS_ASSERT(!!seg_ == !!fp_);
-    JS_ASSERT_IF(fp_, seg_->contains(fp_));
-
-#ifdef JS_ION
-    if (fp_ && fp_->beginsIonActivation()) {
-        // Start at the first scripted frame.
-        ionFrames_ = ion::IonFrameIterator(ionActivations_);
-        while (!ionFrames_.isScripted() && !ionFrames_.done())
-            ++ionFrames_;
-
-        state_ = ionFrames_.done() ? SCRIPTED : ION;
-        return;
-    }
-#endif
-
-    state_ = fp_ ? SCRIPTED : DONE;
-}
-
-AbstractFramePtr
-AllFramesIter::abstractFramePtr() const
-{
-    switch (state_) {
-      case SCRIPTED:
-        return AbstractFramePtr(interpFrame());
-      case ION:
-#ifdef JS_ION
-        if (ionFrames_.isBaselineJS())
-            return ionFrames_.baselineFrame();
-#endif
-        break;
-      case DONE:
-        break;
-    }
-    JS_NOT_REACHED("Unexpected state");
-    return NullFramePtr();
-}
-
-JSObject *
-AbstractFramePtr::evalPrevScopeChain(JSRuntime *rt) const
-{
-    /* Find the stack segment containing this frame. */
-    AllFramesIter alliter(rt);
-    while (alliter.isIonOptimizedJS() || alliter.abstractFramePtr() != *this)
-        ++alliter;
-
-    /* Eval frames are not compiled by Ion, though their caller might be. */
-    ScriptFrameIter iter(rt, *alliter.seg());
-    while (iter.isIonOptimizedJS() || iter.abstractFramePtr() != *this)
+    // Eval frames are not compiled by Ion, though their caller might be.
+    AllFramesIter iter(cx);
+    while (iter.isIon() || iter.abstractFramePtr() != *this)
         ++iter;
     ++iter;
     return iter.scopeChain();
 }
 
 #ifdef DEBUG
 void
 js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
@@ -2063,8 +1834,47 @@ js::CheckLocalUnaliased(MaybeCheckAliasi
             if (b->containsVarAtDepth(depth)) {
                 JS_ASSERT(!b->isAliased(depth - b->stackDepth()));
                 break;
             }
         }
     }
 }
 #endif
+
+ion::JitActivation::JitActivation(JSContext *cx, bool firstFrameIsConstructing)
+  : Activation(cx, Jit),
+    prevIonTop_(cx->mainThread().ionTop),
+    prevIonJSContext_(cx->mainThread().ionJSContext),
+    firstFrameIsConstructing_(firstFrameIsConstructing)
+{
+    cx->mainThread().ionJSContext = cx;
+}
+
+ion::JitActivation::~JitActivation()
+{
+    cx_->mainThread().ionTop = prevIonTop_;
+    cx_->mainThread().ionJSContext = prevIonJSContext_;
+}
+
+InterpreterFrameIterator &
+InterpreterFrameIterator::operator++()
+{
+    JS_ASSERT(!done());
+    pc_ = fp_->prevpc();
+    fp_ = (fp_ != activation_->entry_) ? fp_->prev() : NULL;
+    return *this;
+}
+
+ActivationIterator::ActivationIterator(JSRuntime *rt)
+  : jitTop_(rt->mainThread.ionTop),
+    activation_(rt->mainThread.activation_)
+{ }
+
+ActivationIterator &
+ActivationIterator::operator++()
+{
+    JS_ASSERT(activation_);
+    if (activation_->isJit())
+        jitTop_ = activation_->asJit()->prevIonTop();
+    activation_ = activation_->prev();
+    return *this;
+}
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -83,36 +83,35 @@ struct ScopeCoordinate;
  * 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. JM 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.
+ * var of js::Interpret. Ideally, we'd like to remove all dependence on FrameRegs
+ * outside the interpreter.
  *
  * 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 each context has its own callstack.
  * Note, though, that eval-in-frame allows one context's callstack to join
  * another context's callstack. Thus, in general, the structure of calls in a
  * StackSpace is a forest.
  *
  * More 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 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.
+ * perspective of cx1 and cx2. 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.
  */
 
 /*****************************************************************************/
 
 enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
 
 /*****************************************************************************/
 
@@ -222,17 +221,17 @@ class AbstractFramePtr
     inline Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
     inline Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
     inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
     inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
 
     inline bool prevUpToDate() const;
     inline void setPrevUpToDate() const;
 
-    JSObject *evalPrevScopeChain(JSRuntime *rt) const;
+    JSObject *evalPrevScopeChain(JSContext *cx) const;
 
     inline void *maybeHookData() const;
     inline void setHookData(void *data) const;
     inline Value returnValue() const;
     inline void setReturnValue(const Value &rval) const;
 
     inline bool hasPushedSPSFrame() const;
 
@@ -296,22 +295,24 @@ class StackFrame
         HAS_BLOCKCHAIN     =     0x4000,  /* frame has blockChain_ set */
 
         /* Debugger state */
         PREV_UP_TO_DATE    =     0x8000,  /* see DebugScopes::updateLiveScopes */
 
         /* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */
         HAS_PUSHED_SPS_FRAME =  0x10000,  /* SPS was notified of enty */
 
-        /* Ion frame state */
-        RUNNING_IN_ION     =    0x20000,  /* frame is running in Ion */
-        CALLING_INTO_ION   =    0x40000,  /* frame is calling into Ion */
+        /*
+         * If set, we entered one of the JITs and ScriptFrameIter should skip
+         * this frame.
+         */
+        RUNNING_IN_JIT     =    0x20000,
 
         /* Miscellaneous state. */
-        USE_NEW_TYPE       =    0x80000   /* Use new type for constructed |this| object. */
+        USE_NEW_TYPE       =    0x40000   /* Use new type for constructed |this| object. */
     };
 
   private:
     mutable uint32_t    flags_;         /* bits described by Flags */
     union {                             /* describes what code is executing in a */
         JSScript        *script;        /*   global frame */
         JSFunction      *fun;           /*   function frame, pre GetScopeChain */
     } exec;
@@ -321,20 +322,17 @@ class StackFrame
     } u;
     mutable JSObject    *scopeChain_;   /* if HAS_SCOPECHAIN, current scope chain */
     StackFrame          *prev_;         /* if HAS_PREVPC, previous cx->regs->fp */
     Value               rval_;          /* if HAS_RVAL, return value of the frame */
     StaticBlockObject   *blockChain_;   /* if HAS_BLOCKCHAIN, innermost let block */
     ArgumentsObject     *argsObj_;      /* if HAS_ARGS_OBJ, the call's arguments object */
     jsbytecode          *prevpc_;       /* if HAS_PREVPC, pc of previous frame*/
     void                *hookData_;     /* if HAS_HOOK_DATA, closure returned by call hook */
-#ifdef JS_ION
-    ion::BaselineFrame  *prevBaselineFrame_; /* for an eval/debugger frame, the baseline frame
-                                              * to use as prev. */
-#endif
+    AbstractFramePtr    evalInFramePrev_; /* for an eval/debugger frame, the prev frame */
 
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(StackFrame, rval_) % sizeof(Value) == 0);
         JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(Value) == 0);
     }
 
     inline void initPrev(JSContext *cx);
     void writeBarrierPost();
@@ -472,28 +470,20 @@ class StackFrame
      * 'prev' has little semantic meaning and basically just tells the VM what
      * to set cx->regs->fp to when this frame is popped.
      */
 
     StackFrame *prev() const {
         return prev_;
     }
 
-#ifdef JS_ION
-    /*
-     * To handle eval-in-frame with a baseline JIT frame, |prev_| points to the
-     * entry frame and prevBaselineFrame_ to the actual BaselineFrame. This is
-     * done so that ScriptFrameIter can skip JIT frames pushed on top of the
-     * baseline frame (these frames should not appear in stack traces).
-     */
-    ion::BaselineFrame *prevBaselineFrame() const {
+    AbstractFramePtr evalInFramePrev() const {
         JS_ASSERT(isEvalFrame());
-        return prevBaselineFrame_;
+        return evalInFramePrev_;
     }
-#endif
 
     inline void resetGeneratorPrev(JSContext *cx);
 
     /*
      * (Unaliased) locals and arguments
      *
      * Only non-eval function frames have arguments. The arguments pushed by
      * the caller are the 'actual' arguments. The declared arguments of the
@@ -981,39 +971,25 @@ class StackFrame
 
     static size_t offsetOfFixed(unsigned i) {
         return sizeof(StackFrame) + i * sizeof(Value);
     }
 
   public:
     void mark(JSTracer *trc);
 
-    // Entered IonMonkey from the interpreter.
-    bool runningInIon() const {
-        return !!(flags_ & RUNNING_IN_ION);
-    }
-    // Entered IonMonkey from JaegerMonkey.
-    bool callingIntoIon() const {
-        return !!(flags_ & CALLING_INTO_ION);
-    }
-    // Entered IonMonkey in any way.
-    bool beginsIonActivation() const {
-        return !!(flags_ & (RUNNING_IN_ION | CALLING_INTO_ION));
+    // Entered Baseline/Ion from the interpreter.
+    bool runningInJit() const {
+        return !!(flags_ & RUNNING_IN_JIT);
     }
-    void setRunningInIon() {
-        flags_ |= RUNNING_IN_ION;
-    }
-    void setCallingIntoIon() {
-        flags_ |= CALLING_INTO_ION;
+    void setRunningInJit() {
+        flags_ |= RUNNING_IN_JIT;
     }
-    void clearRunningInIon() {
-        flags_ &= ~RUNNING_IN_ION;
-    }
-    void clearCallingIntoIon() {
-        flags_ &= ~CALLING_INTO_ION;
+    void clearRunningInJit() {
+        flags_ &= ~RUNNING_IN_JIT;
     }
 };
 
 static const size_t VALUES_PER_STACK_FRAME = sizeof(StackFrame) / sizeof(Value);
 
 static inline StackFrame::Flags
 ToFrameFlags(InitialFrameFlags initial)
 {
@@ -1068,28 +1044,16 @@ class FrameRegs
     /* 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 InternalInterpret: */
-    void restorePartialFrame(Value *newfp) {
-        fp_ = (StackFrame *) newfp;
-    }
-
     /* For stubs::CompileFunction, ContextStack: */
     void prepareToRun(StackFrame &fp, JSScript *script) {
         pc = script->code;
         sp = fp.slots() + script->nfixed;
         fp_ = &fp;
     }
 
     void setToEndOfScript() {
@@ -1523,16 +1487,235 @@ struct DefaultHasher<AbstractFramePtr> {
 
     static bool match(const AbstractFramePtr &k, const Lookup &l) {
         return k == l;
     }
 };
 
 /*****************************************************************************/
 
+class InterpreterActivation;
+
+namespace ion {
+    class JitActivation;
+};
+
+// A JSRuntime's stack consists of a linked list of activations. Every activation
+// contains a number of scripted frames that are either running in the interpreter
+// (InterpreterActivation) or JIT code (JitActivation). The frames inside a single
+// activation are contiguous: whenever C++ calls back into JS, a new activation is
+// pushed.
+//
+// Every activation is tied to a single JSContext and JSCompartment. This means we
+// can construct a given context's stack by skipping activations belonging to other
+// contexts.
+class Activation
+{
+  protected:
+    JSContext *cx_;
+    JSCompartment *compartment_;
+    Activation *prev_;
+
+    // Counter incremented by JS_SaveFrameChain on the top-most activation and
+    // decremented by JS_RestoreFrameChain. If > 0, ScriptFrameIter should stop
+    // iterating when it reaches this activation (if GO_THROUGH_SAVED is not
+    // set).
+    size_t savedFrameChain_;
+
+    enum Kind { Interpreter, Jit };
+    Kind kind_;
+
+    inline Activation(JSContext *cx, Kind kind_);
+    inline ~Activation();
+
+  public:
+    JSContext *cx() const {
+        return cx_;
+    }
+    JSCompartment *compartment() const {
+        return compartment_;
+    }
+    Activation *prev() const {
+        return prev_;
+    }
+
+    bool isInterpreter() const {
+        return kind_ == Interpreter;
+    }
+    bool isJit() const {
+        return kind_ == Jit;
+    }
+
+    InterpreterActivation *asInterpreter() const {
+        JS_ASSERT(isInterpreter());
+        return (InterpreterActivation *)this;
+    }
+    ion::JitActivation *asJit() const {
+        JS_ASSERT(isJit());
+        return (ion::JitActivation *)this;
+    }
+
+    void saveFrameChain() {
+        savedFrameChain_++;
+    }
+    void restoreFrameChain() {
+        JS_ASSERT(savedFrameChain_ > 0);
+        savedFrameChain_--;
+    }
+    bool hasSavedFrameChain() const {
+        return savedFrameChain_ > 0;
+    }
+
+  private:
+    Activation(const Activation &other) MOZ_DELETE;
+    void operator=(const Activation &other) MOZ_DELETE;
+};
+
+class InterpreterFrameIterator;
+
+class InterpreterActivation : public Activation
+{
+    friend class js::InterpreterFrameIterator;
+
+    StackFrame *const entry_; // Entry frame for this activation.
+    StackFrame *current_;     // The most recent frame.
+    FrameRegs &regs_;
+
+  public:
+    inline InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs &regs);
+    inline ~InterpreterActivation();
+
+    void pushFrame(StackFrame *frame) {
+        JS_ASSERT(frame->script()->compartment() == compartment_);
+        current_ = frame;
+    }
+    void popFrame(StackFrame *frame) {
+        JS_ASSERT(current_ == frame);
+        JS_ASSERT(current_ != entry_);
+
+        current_ = frame->prev();
+        JS_ASSERT(current_);
+    }
+    StackFrame *current() const {
+        JS_ASSERT(current_);
+        return current_;
+    }
+};
+
+// Iterates over a runtime's activation list.
+class ActivationIterator
+{
+    uint8_t *jitTop_;
+
+  protected:
+    Activation *activation_;
+
+  public:
+    explicit ActivationIterator(JSRuntime *rt);
+
+    ActivationIterator &operator++();
+
+    Activation *activation() const {
+        return activation_;
+    }
+    uint8_t *jitTop() const {
+        JS_ASSERT(activation_->isJit());
+        return jitTop_;
+    }
+    bool done() const {
+        return activation_ == NULL;
+    }
+};
+
+namespace ion {
+
+// A JitActivation is used for frames running in Baseline or Ion.
+class JitActivation : public Activation
+{
+    uint8_t *prevIonTop_;
+    JSContext *prevIonJSContext_;
+    bool firstFrameIsConstructing_;
+
+  public:
+    JitActivation(JSContext *cx, bool firstFrameIsConstructing);
+    ~JitActivation();
+
+    uint8_t *prevIonTop() const {
+        return prevIonTop_;
+    }
+    JSCompartment *compartment() const {
+        return compartment_;
+    }
+    bool firstFrameIsConstructing() const {
+        return firstFrameIsConstructing_;
+    }
+};
+
+// A filtering of the ActivationIterator to only stop at JitActivations.
+class JitActivationIterator : public ActivationIterator
+{
+    void settle() {
+        while (!done() && !activation_->isJit())
+            ActivationIterator::operator++();
+    }
+
+  public:
+    explicit JitActivationIterator(JSRuntime *rt)
+      : ActivationIterator(rt)
+    {
+        settle();
+    }
+
+    JitActivationIterator &operator++() {
+        ActivationIterator::operator++();
+        settle();
+        return *this;
+    }
+
+    // Returns the bottom and top addresses of the current JitActivation.
+    void jitStackRange(uintptr_t *&min, uintptr_t *&end);
+};
+
+} // namespace ion
+
+// Iterates over the frames of a single InterpreterActivation.
+class InterpreterFrameIterator
+{
+    InterpreterActivation *activation_;
+    StackFrame *fp_;
+    jsbytecode *pc_;
+
+  public:
+    explicit InterpreterFrameIterator(InterpreterActivation *activation)
+      : activation_(activation),
+        fp_(NULL),
+        pc_(NULL)
+    {
+        if (activation) {
+            fp_ = activation->current();
+            pc_ = activation->regs_.pc;
+        }
+    }
+
+    StackFrame *frame() const {
+        JS_ASSERT(!done());
+        return fp_;
+    }
+    jsbytecode *pc() const {
+        JS_ASSERT(!done());
+        return pc_;
+    }
+
+    InterpreterFrameIterator &operator++();
+
+    bool done() const {
+        return fp_ == NULL;
+    }
+};
+
 /*
  * Iterate through the callstack (following fp->prev) 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()) {
@@ -1545,109 +1728,104 @@ struct DefaultHasher<AbstractFramePtr> {
  *
  * The SavedOption parameter additionally lets the iterator continue through
  * breaks in the callstack (from JS_SaveFrameChain). The default is to stop.
  */
 class ScriptFrameIter
 {
   public:
     enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
-    enum State { DONE, SCRIPTED, ION };
+    enum ContextOption { CURRENT_CONTEXT, ALL_CONTEXTS };
+    enum State { DONE, SCRIPTED, JIT };
 
     /*
      * Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on
      * the heap, so this structure should not contain any GC things.
      */
     struct Data
     {
         PerThreadData *perThread_;
         JSContext    *cx_;
         SavedOption  savedOption_;
+        ContextOption contextOption_;
 
         State        state_;
 
-        StackFrame   *fp_;
-
-        StackSegment *seg_;
         jsbytecode   *pc_;
 
+        InterpreterFrameIterator interpFrames_;
+        ActivationIterator activations_;
+
 #ifdef JS_ION
-        ion::IonActivationIterator ionActivations_;
         ion::IonFrameIterator ionFrames_;
 #endif
 
-        Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption);
-        Data(JSContext *cx, JSRuntime *rt, StackSegment *seg);
+        Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption,
+             ContextOption contextOption);
         Data(const Data &other);
     };
 
     friend class ContextStack;
     friend class ::JSBrokenFrameIterator;
   private:
     Data data_;
 #ifdef JS_ION
     ion::InlineFrameIterator ionInlineFrames_;
 #endif
 
-    void poisonRegs();
-    void popFrame();
+    void popActivation();
+    void popInterpreterFrame();
 #ifdef JS_ION
-    void nextIonFrame();
-    void popIonFrame();
-    void popBaselineDebuggerFrame();
+    void nextJitFrame();
+    void popJitFrame();
 #endif
-    void settleOnNewSegment();
-    void settleOnNewState();
-    void startOnSegment(StackSegment *seg);
+    void settleOnActivation();
 
   public:
     ScriptFrameIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
-    ScriptFrameIter(JSRuntime *rt, StackSegment &seg);
+    ScriptFrameIter(JSContext *cx, ContextOption, SavedOption);
     ScriptFrameIter(const ScriptFrameIter &iter);
     ScriptFrameIter(const Data &data);
 
     bool done() const { return data_.state_ == DONE; }
     ScriptFrameIter &operator++();
 
     Data *copyData() const;
 
-    bool operator==(const ScriptFrameIter &rhs) const;
-    bool operator!=(const ScriptFrameIter &rhs) const { return !(*this == rhs); }
-
     JSCompartment *compartment() const;
 
     JSScript *script() const {
         JS_ASSERT(!done());
         if (data_.state_ == SCRIPTED)
             return interpFrame()->script();
 #ifdef JS_ION
-        JS_ASSERT(data_.state_ == ION);
+        JS_ASSERT(data_.state_ == JIT);
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.script();
         return data_.ionFrames_.script();
 #else
         return NULL;
 #endif
     }
-    bool isIon() const {
+    bool isJit() const {
         JS_ASSERT(!done());
-        return data_.state_ == ION;
+        return data_.state_ == JIT;
     }
 
-    bool isIonOptimizedJS() const {
+    bool isIon() const {
 #ifdef JS_ION
-        return isIon() && data_.ionFrames_.isOptimizedJS();
+        return isJit() && data_.ionFrames_.isOptimizedJS();
 #else
         return false;
 #endif
     }
 
-    bool isIonBaselineJS() const {
+    bool isBaseline() const {
 #ifdef JS_ION
-        return isIon() && data_.ionFrames_.isBaselineJS();
+        return isJit() && data_.ionFrames_.isBaselineJS();
 #else
         return false;
 #endif
     }
 
     bool isFunctionFrame() const;
     bool isGlobalFrame() const;
     bool isEvalFrame() const;
@@ -1660,17 +1838,22 @@ class ScriptFrameIter
     AbstractFramePtr abstractFramePtr() const;
 
     /*
      * When entering IonMonkey, the top interpreter frame (pushed by the caller)
      * is kept on the stack as bookkeeping (with runningInIon() set). The
      * contents of the frame are ignored by Ion code (and GC) and thus
      * immediately become garbage and must not be touched directly.
      */
-    StackFrame *interpFrame() const { JS_ASSERT(data_.state_ == SCRIPTED); return data_.fp_; }
+    StackFrame *interpFrame() const {
+        JS_ASSERT(data_.state_ == SCRIPTED);
+        return data_.interpFrames_.frame();
+    }
+
+    Activation *activation() const { return data_.activations_.activation(); }
 
     jsbytecode *pc() const { JS_ASSERT(!done()); return data_.pc_; }
     void        updatePcQuadratic();
     JSFunction *callee() const;
     Value       calleev() const;
     unsigned    numActualArgs() const;
     unsigned    numFormalArgs() const { return script()->function()->nargs; }
     Value       unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;
@@ -1705,65 +1888,31 @@ class NonBuiltinScriptFrameIter : public
 {
     void settle() {
         while (!done() && script()->selfHosted)
             ScriptFrameIter::operator++();
     }
 
   public:
     NonBuiltinScriptFrameIter(JSContext *cx, ScriptFrameIter::SavedOption opt = ScriptFrameIter::STOP_AT_SAVED)
-        : ScriptFrameIter(cx, opt) { settle(); }
+      : ScriptFrameIter(cx, opt) { settle(); }
 
     NonBuiltinScriptFrameIter(const ScriptFrameIter::Data &data)
       : ScriptFrameIter(data)
     {}
 
     NonBuiltinScriptFrameIter &operator++() { ScriptFrameIter::operator++(); settle(); return *this; }
 };
 
-/*****************************************************************************/
-
 /*
  * Blindly iterate over all frames in the current thread's stack. These frames
- * can be from different contexts and compartments, so beware. Iterates over
- * Ion frames, but does not handle inlined frames.
+ * can be from different contexts and compartments, so beware.
  */
-class AllFramesIter
+class AllFramesIter : public ScriptFrameIter
 {
   public:
-    AllFramesIter(JSRuntime *rt);
-
-    bool done() const { return state_ == DONE; }
-    AllFramesIter& operator++();
-
-    bool isIon() const { return state_ == ION; }
-    bool isIonOptimizedJS() const {
-#ifdef JS_ION
-        return isIon() && ionFrames_.isOptimizedJS();
-#else
-        return false;
-#endif
-    }
-    StackFrame *interpFrame() const { JS_ASSERT(state_ == SCRIPTED); return fp_; }
-    StackSegment *seg() const { return seg_; }
-
-    AbstractFramePtr abstractFramePtr() const;
-
-  private:
-    enum State { DONE, SCRIPTED, ION };
-
-#ifdef JS_ION
-    void popIonFrame();
-#endif
-    void settleOnNewState();
-
-    StackSegment *seg_;
-    StackFrame *fp_;
-    State state_;
-
-#ifdef JS_ION
-    ion::IonActivationIterator ionActivations_;
-    ion::IonFrameIterator ionFrames_;
-#endif
+    AllFramesIter(JSContext *cx)
+      : ScriptFrameIter(cx, ScriptFrameIter::ALL_CONTEXTS, ScriptFrameIter::GO_THROUGH_SAVED)
+    {}
 };
 
 }  /* namespace js */
 #endif /* Stack_h__ */