Bug 625199 - kill dummy frames (r=mrbkap)
authorLuke Wagner <luke@mozilla.com>
Fri, 17 Aug 2012 18:05:06 -0700
changeset 106377 986c07b3f3e68af098d7e85fd507a7d4139b4eb3
parent 106376 ea32388d45a805c72f179375dc2b763c75454f6d
child 106378 0d61ae018d9f97407a37e327ee526fb018619f62
push id1989
push userakeybl@mozilla.com
push dateTue, 28 Aug 2012 00:20:43 +0000
treeherdermozilla-aurora@a8e95ae10ea7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs625199
milestone17.0a1
Bug 625199 - kill dummy frames (r=mrbkap)
content/base/src/nsContentUtils.cpp
dom/base/nsJSEnvironment.cpp
js/jsd/jsd_obj.c
js/jsd/jsd_stak.c
js/src/builtin/Eval.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsgc.cpp
js/src/jsinferinlines.h
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jswrapper.cpp
js/src/jswrapper.h
js/src/jsxml.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/ScopeObject.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/src/vm/String.cpp
js/xpconnect/src/XPCStack.cpp
js/xpconnect/wrappers/AccessCheck.cpp
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -5968,24 +5968,24 @@ nsContentUtils::CanAccessNativeAnon()
     return true;
   }
   JSStackFrame* fp;
   nsIPrincipal* principal =
     sSecurityManager->GetCxSubjectPrincipalAndFrame(cx, &fp);
   NS_ENSURE_TRUE(principal, false);
 
   JSScript *script = nullptr;
-  if (!fp) {
+  if (fp) {
+    script = JS_GetFrameScript(cx, fp);
+  } else {
     if (!JS_DescribeScriptedCaller(cx, &script, nullptr)) {
       // No code at all is running. So we must be arriving here as the result
       // of C++ code asking us to do something. Allow access.
       return true;
     }
-  } else if (JS_IsScriptFrame(cx, fp)) {
-    script = JS_GetFrameScript(cx, fp);
   }
 
   bool privileged;
   if (NS_SUCCEEDED(sSecurityManager->IsSystemPrincipal(principal, &privileged)) &&
       privileged) {
     // Chrome things are allowed to touch us.
     return true;
   }
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1901,18 +1901,17 @@ nsJSContext::CallEventHandler(nsISupport
   if (NS_SUCCEEDED(rv)) {
     // Convert args to jsvals.
     uint32_t argc = 0;
     jsval *argv = nullptr;
 
     JSObject *funobj = aHandler;
     jsval funval = OBJECT_TO_JSVAL(funobj);
     JSAutoEnterCompartment ac;
-    js::ForceFrame ff(mContext, funobj);
-    if (!ac.enter(mContext, funobj) || !ff.enter() ||
+    if (!ac.enter(mContext, funobj) ||
         !JS_WrapObject(mContext, &target)) {
       ReportPendingException();
       return NS_ERROR_FAILURE;
     }
 
     Maybe<nsRootedJSValueArray> tempStorage;
 
     // Use |target| as the scope for wrapping the arguments, since aScope is
--- a/js/jsd/jsd_obj.c
+++ b/js/jsd/jsd_obj.c
@@ -108,17 +108,17 @@ jsd_Constructing(JSDContext* jsdc, JSCon
     JSScript* script;
     JSDScript* jsdscript;
     const char* ctorURL;
     JSString* ctorNameStr;
     const char* ctorName;
 
     JSD_LOCK_OBJECTS(jsdc);
     jsdobj = jsd_GetJSDObjectForJSObject(jsdc, obj);
-    if( jsdobj && !jsdobj->ctorURL && JS_IsScriptFrame(cx, fp) )
+    if( jsdobj && !jsdobj->ctorURL )
     {
         script = JS_GetFrameScript(cx, fp);
         if( script )
         {
             ctorURL = JS_GetScriptFilename(cx, script);
             if( ctorURL )
                 jsdobj->ctorURL = jsd_AddAtom(jsdc, ctorURL);
 
--- a/js/jsd/jsd_stak.c
+++ b/js/jsd/jsd_stak.c
@@ -29,31 +29,28 @@ static JSDStackFrameInfo*
              JSDThreadState*    jsdthreadstate,
              JSScript*          script,
              uintptr_t          pc,
              JSStackFrame*      fp)
 {
     JSDStackFrameInfo* jsdframe;
     JSDScript*         jsdscript = NULL;
 
-    if (JS_IsScriptFrame(jsdthreadstate->context, fp))
+    JSD_LOCK_SCRIPTS(jsdc);
+    jsdscript = jsd_FindJSDScript(jsdc, script);
+    JSD_UNLOCK_SCRIPTS(jsdc);
+    if (!jsdscript || (jsdc->flags & JSD_HIDE_DISABLED_FRAMES &&
+                       !JSD_IS_DEBUG_ENABLED(jsdc, jsdscript)))
     {
-        JSD_LOCK_SCRIPTS(jsdc);
-        jsdscript = jsd_FindJSDScript(jsdc, script);
-        JSD_UNLOCK_SCRIPTS(jsdc);
-        if (!jsdscript || (jsdc->flags & JSD_HIDE_DISABLED_FRAMES &&
-                           !JSD_IS_DEBUG_ENABLED(jsdc, jsdscript)))
-        {
-            return NULL;
-        }
+        return NULL;
+    }
 
-        if (!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript))
-            jsdthreadstate->flags |= TS_HAS_DISABLED_FRAME;
-    }
-    
+    if (!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript))
+        jsdthreadstate->flags |= TS_HAS_DISABLED_FRAME;
+
     jsdframe = (JSDStackFrameInfo*) calloc(1, sizeof(JSDStackFrameInfo));
     if( ! jsdframe )
         return NULL;
 
     jsdframe->jsdthreadstate = jsdthreadstate;
     jsdframe->jsdscript      = jsdscript;
     jsdframe->pc             = pc;
     jsdframe->fp             = fp;
@@ -96,19 +93,17 @@ jsd_NewThreadState(JSDContext* jsdc, JSC
         uintptr_t  pc = (uintptr_t) JS_GetFramePC(cx, fp);
         jsval dummyThis;
 
         /*
          * don't construct a JSDStackFrame for dummy frames (those without a
          * |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES
          * isn't set.
          */
-        if (JS_GetFrameThis(cx, fp, &dummyThis) &&
-            ((jsdc->flags & JSD_INCLUDE_NATIVE_FRAMES) ||
-             JS_IsScriptFrame(cx, fp)))
+        if (JS_GetFrameThis(cx, fp, &dummyThis))
         {
             JSDStackFrameInfo *frame;
 
             frame = _addNewFrame( jsdc, jsdthreadstate, script, pc, fp );
 
             if ((jsdthreadstate->stackDepth == 0 && !frame) ||
                 (jsdthreadstate->stackDepth == 1 && frame &&
                  frame->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frame->jsdscript)))
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -319,17 +319,16 @@ js::IndirectEval(JSContext *cx, unsigned
     return EvalKernel(cx, args, INDIRECT_EVAL, NULL, global);
 }
 
 bool
 js::DirectEval(JSContext *cx, const CallArgs &args)
 {
     // Direct eval can assume it was called from an interpreted frame.
     StackFrame *caller = cx->fp();
-    JS_ASSERT(caller->isScriptFrame());
     JS_ASSERT(IsBuiltinEvalForScope(caller->scopeChain(), args.calleev()));
     JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL);
 
     if (!WarnOnTooManyArgs(cx, args))
         return false;
 
     return EvalKernel(cx, args, DIRECT_EVAL, caller, caller->scopeChain());
 }
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -123,17 +123,17 @@ frontend::CompileScript(JSContext *cx, H
     JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
 
     BytecodeEmitter bce(/* parent = */ NULL, &parser, &sc, script, callerFrame, !!globalScope,
                         options.lineno, options.selfHostingMode);
     if (!bce.init())
         return NULL;
 
     /* If this is a direct call to eval, inherit the caller's strictness.  */
-    if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode)
+    if (callerFrame && callerFrame->script()->strictModeCode)
         sc.strictModeState = StrictMode::STRICT;
 
     if (options.compileAndGo) {
         if (source) {
             /*
              * Save eval program source in script->atoms[0] for the
              * eval cache (see EvalCacheLookup in jsobj.cpp).
              */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -120,17 +120,16 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
     constMap(sc->context),
     constList(sc->context),
     typesetCount(0),
     hasSingletons(false),
     inForInit(false),
     hasGlobalScope(hasGlobalScope),
     selfHostingMode(selfHostingMode)
 {
-    JS_ASSERT_IF(callerFrame, callerFrame->isScriptFrame());
     memset(&prolog, 0, sizeof prolog);
     memset(&main, 0, sizeof main);
     current = &main;
     firstLine = prolog.currentLine = main.currentLine = lineno;
 }
 
 bool
 BytecodeEmitter::init()
@@ -1297,18 +1296,16 @@ BindNameToSlot(JSContext *cx, BytecodeEm
 
             /*
              * Don't generate upvars on the left side of a for loop. See
              * bug 470758.
              */
             if (bce->inForInit)
                 return true;
 
-            JS_ASSERT(caller->isScriptFrame());
-
             /*
              * If this is an eval in the global scope, then unbound variables
              * must be globals, so try to use GNAME ops.
              */
             if (caller->isGlobalFrame() && TryConvertToGname(bce, pn, &op)) {
                 pn->setOp(op);
                 pn->pn_dflags |= PND_BOUND;
                 return true;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1431,107 +1431,91 @@ JS_SetWrapObjectCallbacks(JSRuntime *rt,
 {
     JSWrapObjectCallback old = rt->wrapObjectCallback;
     rt->wrapObjectCallback = callback;
     rt->sameCompartmentWrapObjectCallback = sccallback;
     rt->preWrapObjectCallback = precallback;
     return old;
 }
 
+struct JSCrossCompartmentCall
+{
+    JSContext *context;
+    JSCompartment *oldCompartment;
+};
+
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCall(JSContext *cx, JSRawObject target)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
-    JS_ASSERT(target);
-    AutoCompartment *call = cx->new_<AutoCompartment>(cx, target);
+    JSCrossCompartmentCall *call = OffTheBooks::new_<JSCrossCompartmentCall>();
     if (!call)
         return NULL;
-    if (!call->enter()) {
-        Foreground::delete_(call);
-        return NULL;
-    }
-    return reinterpret_cast<JSCrossCompartmentCall *>(call);
+
+    call->context = cx;
+    call->oldCompartment = cx->compartment;
+
+    cx->enterCompartment(target->compartment());
+    return call;
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    GlobalObject *global = target->compartment()->maybeGlobal();
-    return global ? JS_EnterCrossCompartmentCall(cx, global) : NULL;
+    GlobalObject &global = target->global();
+    return JS_EnterCrossCompartmentCall(cx, &global);
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCallStackFrame(JSContext *cx, JSStackFrame *target)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-
     HandleObject global(HandleObject::fromMarkedLocation((JSObject**) &Valueify(target)->global()));
     return JS_EnterCrossCompartmentCall(cx, global);
 }
 
 JS_PUBLIC_API(void)
 JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call)
 {
-    AutoCompartment *realcall = reinterpret_cast<AutoCompartment *>(call);
-    AssertHeapIsIdle(realcall->context);
-    CHECK_REQUEST(realcall->context);
-    realcall->leave();
-    Foreground::delete_(realcall);
+    AssertHeapIsIdle(call->context);
+    CHECK_REQUEST(call->context);
+    call->context->leaveCompartment(call->oldCompartment);
+    Foreground::delete_(call);
 }
 
 bool
 JSAutoEnterCompartment::enter(JSContext *cx, JSRawObject target)
 {
-    AssertHeapIsIdleOrIterating(cx);
-    JS_ASSERT(state == STATE_UNENTERED);
-    if (cx->compartment == target->compartment()) {
-        state = STATE_SAME_COMPARTMENT;
-        return true;
-    }
-
-    JS_STATIC_ASSERT(sizeof(bytes) == sizeof(AutoCompartment));
-    CHECK_REQUEST(cx);
-    AutoCompartment *call = new (bytes) AutoCompartment(cx, target);
-    if (call->enter()) {
-        state = STATE_OTHER_COMPARTMENT;
-        return true;
-    }
-    return false;
+    enterAndIgnoreErrors(cx, target);
+    return true;
 }
 
 void
 JSAutoEnterCompartment::enterAndIgnoreErrors(JSContext *cx, JSRawObject target)
 {
-    (void) enter(cx, target);
+    AssertHeapIsIdleOrIterating(cx);
+    JS_ASSERT(!entered_);
+    cx_ = cx;
+    oldCompartment_ = cx->compartment;
+    cx->enterCompartment(target->compartment());
+    entered_ = true;
 }
 
 void
 JSAutoEnterCompartment::leave()
 {
-    JS_ASSERT(entered());
-    if (state == STATE_OTHER_COMPARTMENT) {
-        AutoCompartment* ac = getAutoCompartment();
-        CHECK_REQUEST(ac->context);
-        ac->~AutoCompartment();
-    }
-    state = STATE_UNENTERED;
-}
-
-JSAutoEnterCompartment::~JSAutoEnterCompartment()
-{
-    if (entered())
-        leave();
-}
-
-namespace JS {
+    JS_ASSERT(entered_);
+    cx_->leaveCompartment(oldCompartment_);
+    entered_ = false;
+}
 
 bool
 AutoEnterScriptCompartment::enter(JSContext *cx, JSScript *target)
 {
     JS_ASSERT(!call);
     if (cx->compartment == target->compartment()) {
         call = reinterpret_cast<JSCrossCompartmentCall*>(1);
         return true;
@@ -1547,18 +1531,16 @@ AutoEnterFrameCompartment::enter(JSConte
     if (cx->compartment == Valueify(target)->scopeChain()->compartment()) {
         call = reinterpret_cast<JSCrossCompartmentCall*>(1);
         return true;
     }
     call = JS_EnterCrossCompartmentCallStackFrame(cx, target);
     return call != NULL;
 }
 
-} /* namespace JS */
-
 JS_PUBLIC_API(void)
 JS_SetCompartmentPrivate(JSCompartment *compartment, void *data)
 {
     compartment->data = data;
 }
 
 JS_PUBLIC_API(void *)
 JS_GetCompartmentPrivate(JSCompartment *compartment)
@@ -5961,36 +5943,33 @@ JS_PUBLIC_API(void)
 JS_TriggerOperationCallback(JSRuntime *rt)
 {
     rt->triggerOperationCallback();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsRunning(JSContext *cx)
 {
-    StackFrame *fp = cx->maybefp();
-    while (fp && fp->isDummyFrame())
-        fp = fp->prev();
-    return fp != NULL;
+    return cx->hasfp();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SaveFrameChain(JSContext *cx)
 {
     AssertHeapIsIdleOrIterating(cx);
     CHECK_REQUEST(cx);
-    return cx->stack.saveFrameChain();
+    return cx->saveFrameChain();
 }
 
 JS_PUBLIC_API(void)
 JS_RestoreFrameChain(JSContext *cx)
 {
     AssertHeapIsIdleOrIterating(cx);
     CHECK_REQUEST(cx);
-    cx->stack.restoreFrameChain();
+    cx->restoreFrameChain();
 }
 
 #ifdef MOZ_TRACE_JSCALLS
 JS_PUBLIC_API(void)
 JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb)
 {
     cx->functionCallback = fcb;
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3315,63 +3315,41 @@ JS_RefreshCrossCompartmentWrappers(JSCon
 JS_END_EXTERN_C
 
 namespace js {
 class AutoCompartment;
 }
 
 class JS_PUBLIC_API(JSAutoEnterCompartment)
 {
-    /*
-     * This is a poor man's Maybe<AutoCompartment>, because we don't have
-     * access to the AutoCompartment definition here.  We statically assert in
-     * jsapi.cpp that we have the right size here.
-     *
-     * In practice, 32-bit Windows and Android get 16-word |bytes|, while other
-     * platforms get 12-word |bytes|.
-     */
-    void* bytes[sizeof(void*) == 4 && MOZ_ALIGNOF(uint64_t) == 8 ? 16 : 12];
-
-  protected:
-    js::AutoCompartment *getAutoCompartment() {
-        JS_ASSERT(state == STATE_OTHER_COMPARTMENT);
-        return reinterpret_cast<js::AutoCompartment*>(bytes);
-    }
-
-    /*
-     * This object may be in one of three states.  If enter() or
-     * enterAndIgnoreErrors() hasn't been called, it's in STATE_UNENTERED.
-     * Otherwise, if we were asked to enter into the current compartment, our
-     * state is STATE_SAME_COMPARTMENT.  If we actually created an
-     * AutoCompartment and entered another compartment, our state is
-     * STATE_OTHER_COMPARTMENT.
-     */
-    enum State {
-        STATE_UNENTERED,
-        STATE_SAME_COMPARTMENT,
-        STATE_OTHER_COMPARTMENT
-    } state;
+    JSContext *cx_;
+    JSCompartment *oldCompartment_;
+    bool entered_;
 
   public:
-    JSAutoEnterCompartment() : state(STATE_UNENTERED) {}
+    JSAutoEnterCompartment() : entered_(false) {}
+
+    JSAutoEnterCompartment(JSContext *cx, JSRawObject target)
+      : entered_(false)
+    { enter(cx, target); }
 
     bool enter(JSContext *cx, JSRawObject target);
 
     void enterAndIgnoreErrors(JSContext *cx, JSRawObject target);
 
-    bool entered() const { return state != STATE_UNENTERED; }
+    bool entered() const { return entered_; }
 
     /*
      * In general, consumers should try to avoid calling leave() explicitly,
      * and defer to the destructor by scoping the JSAutoEnterCompartment
      * appropriately. Sometimes, though, it's unavoidable.
      */
     void leave();
 
-    ~JSAutoEnterCompartment();
+    ~JSAutoEnterCompartment() { if (entered_) leave(); }
 };
 
 JS_BEGIN_EXTERN_C
 #endif
 
 typedef void (*JSIterateCompartmentCallback)(JSRuntime *rt, void *data, JSCompartment *compartment);
 
 /*
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -277,25 +277,24 @@ AtomizeInline(JSContext *cx, const jscha
     AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length));
 
     if (p) {
         JSAtom *atom = p->asPtr();
         p->setTagged(bool(ib));
         return atom;
     }
 
-    SwitchToCompartment sc(cx, cx->runtime->atomsCompartment);
-
-    JSFixedString *key;
+    AutoEnterAtomsCompartment ac(cx);
 
     SkipRoot skip(cx, &chars);
 
     /* Workaround for hash values in AddPtr being inadvertently poisoned. */
     SkipRoot skip2(cx, &p);
 
+    JSFixedString *key;
     if (ocb == TakeCharOwnership) {
         key = js_NewString(cx, const_cast<jschar *>(chars), length);
         if (!key)
             return NULL;
         *pchars = NULL; /* Called should not free *pchars. */
     } else {
         JS_ASSERT(ocb == CopyChars);
         key = js_NewStringCopyN(cx, chars, length);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1055,16 +1055,18 @@ JSContext::JSContext(JSRuntime *rt)
     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
     localeCallbacks(NULL),
     resolvingList(NULL),
     generatingError(false),
 #ifdef DEBUG
     rootingUnnecessary(false),
 #endif
     compartment(NULL),
+    enterCompartmentDepth_(0),
+    savedFrameChains_(),
     defaultCompartmentObject_(NULL),
     stack(thisDuringConstruction()),
     parseMapPool_(NULL),
     sharpObjectMap(thisDuringConstruction()),
     argumentFormatMap(NULL),
     lastMessage(NULL),
     errorReporter(NULL),
     operationCallback(NULL),
@@ -1074,17 +1076,16 @@ JSContext::JSContext(JSRuntime *rt)
     outstandingRequests(0),
 #endif
     resolveFlags(0),
     rngSeed(0),
     iterValue(MagicValue(JS_NO_ITER_VALUE)),
 #ifdef JS_METHODJIT
     methodJitEnabled(false),
 #endif
-    inferenceEnabled(false),
 #ifdef MOZ_TRACE_JSCALLS
     functionCallback(NULL),
 #endif
     enumerators(NULL),
     innermostGenerator_(NULL),
 #ifdef DEBUG
     stackIterAssertionEnabled(true),
 #endif
@@ -1138,43 +1139,16 @@ JS_FRIEND_API(bool)
 RelaxRootChecksForContext(JSContext *cx)
 {
     return cx->runtime->relaxRootChecks;
 }
 
 } /* namespace JS */
 #endif
 
-void
-JSContext::resetCompartment()
-{
-    if (stack.hasfp()) {
-        compartment = fp()->scopeChain()->compartment();
-    } else {
-        if (!defaultCompartmentObject_)
-            goto error;
-        compartment = defaultCompartmentObject_->compartment();
-    }
-
-    inferenceEnabled = compartment->types.inferenceEnabled;
-
-    if (isExceptionPending())
-        wrapPendingException();
-    updateJITEnabled();
-    return;
-
-error:
-
-    /*
-     * If we try to use the context without a selected compartment,
-     * we will crash.
-     */
-    compartment = NULL;
-}
-
 /*
  * Since this function is only called in the context of a pending exception,
  * the caller must subsequently take an error path. If wrapping fails, it will
  * set a new (uncatchable) exception to be used in place of the original.
  */
 void
 JSContext::wrapPendingException()
 {
@@ -1203,16 +1177,48 @@ JSContext::leaveGenerator(JSGenerator *g
 
 
 bool
 JSContext::runningWithTrustedPrincipals() const
 {
     return !compartment || compartment->principals == runtime->trustedPrincipals();
 }
 
+bool
+JSContext::saveFrameChain()
+{
+    if (!stack.saveFrameChain())
+        return false;
+
+    if (!savedFrameChains_.append(SavedFrameChain(compartment, enterCompartmentDepth_))) {
+        stack.restoreFrameChain();
+        return false;
+    }
+
+    compartment = defaultCompartmentObject_->compartment();
+    enterCompartmentDepth_ = 0;
+
+    if (isExceptionPending())
+        wrapPendingException();
+    return true;
+}
+
+void
+JSContext::restoreFrameChain()
+{
+    SavedFrameChain sfc = savedFrameChains_.popCopy();
+    compartment = sfc.compartment;
+    enterCompartmentDepth_ = sfc.enterCompartmentCount;
+
+    stack.restoreFrameChain();
+
+    if (isExceptionPending())
+        wrapPendingException();
+}
+
 void
 JSRuntime::setGCMaxMallocBytes(size_t value)
 {
     /*
      * For compatibility treat any value that exceeds PTRDIFF_T_MAX to
      * mean that value.
      */
     gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1156,20 +1156,56 @@ struct JSContext : js::ContextFriendFiel
 
     /* True if generating an error, to prevent runaway recursion. */
     bool                generatingError;
 
 #ifdef DEBUG
     bool                rootingUnnecessary;
 #endif
 
-    /* GC heap compartment. */
+    /* The current compartment. */
     JSCompartment       *compartment;
 
-    inline void setCompartment(JSCompartment *compartment);
+    inline void setCompartment(JSCompartment *c) { compartment = c; }
+
+    /*
+     * "Entering" a compartment changes cx->compartment (which changes
+     * cx->global). Note that this does not push any StackFrame which means
+     * that it is possible for cx->fp()->compartment() != cx->compartment.
+     * This is not a problem since, in general, most places in the VM cannot
+     * know that they were called from script (e.g., they may have been called
+     * through the JSAPI via JS_CallFunction) and thus cannot expect fp.
+     *
+     * Compartments should be entered/left in a LIFO fasion. The depth of this
+     * enter/leave stack is maintained by enterCompartmentDepth_ and queried by
+     * hasEnteredCompartment.
+     *
+     * To enter a compartment, code should prefer using AutoCompartment over
+     * manually calling cx->enterCompartment/leaveCompartment.
+     */
+  private:
+    unsigned            enterCompartmentDepth_;
+  public:
+    inline bool hasEnteredCompartment() const;
+    inline void enterCompartment(JSCompartment *c);
+    inline void leaveCompartment(JSCompartment *c);
+
+    /* See JS_SaveFrameChain/JS_RestoreFrameChain. */
+  private:
+    struct SavedFrameChain {
+        SavedFrameChain(JSCompartment *comp, unsigned count)
+          : compartment(comp), enterCompartmentCount(count) {}
+        JSCompartment *compartment;
+        unsigned enterCompartmentCount;
+    };
+    typedef js::Vector<SavedFrameChain, 1, js::SystemAllocPolicy> SaveStack;
+    SaveStack           savedFrameChains_;
+  public:
+    bool saveFrameChain();
+    void restoreFrameChain();
 
     /*
      * When no compartments have been explicitly entered, the context's
      * compartment will be set to the compartment of the "default compartment
      * object".
      */
   private:
     JSObject *defaultCompartmentObject_;
@@ -1186,19 +1222,16 @@ struct JSContext : js::ContextFriendFiel
 
     /* ContextStack convenience functions */
     inline bool hasfp() const               { return stack.hasfp(); }
     inline js::StackFrame* fp() const       { return stack.fp(); }
     inline js::StackFrame* maybefp() const  { return stack.maybefp(); }
     inline js::FrameRegs& regs() const      { return stack.regs(); }
     inline js::FrameRegs* maybeRegs() const { return stack.maybeRegs(); }
 
-    /* Set cx->compartment based on the current scope chain. */
-    void resetCompartment();
-
     /* Wrap cx->exception for the current compartment. */
     void wrapPendingException();
 
   private:
     /* Lazily initialized pool of maps used during parse/emit. */
     js::frontend::ParseMapPool *parseMapPool_;
 
   public:
@@ -1326,19 +1359,17 @@ struct JSContext : js::ContextFriendFiel
     js::Value           iterValue;
 
 #ifdef JS_METHODJIT
     bool                 methodJitEnabled;
 
     js::mjit::JaegerRuntime &jaegerRuntime() { return runtime->jaegerRuntime(); }
 #endif
 
-    bool                 inferenceEnabled;
-
-    bool typeInferenceEnabled() { return inferenceEnabled; }
+    inline bool typeInferenceEnabled() const;
 
     /* Caller must be holding runtime->gcLock. */
     void updateJITEnabled();
 
 #ifdef MOZ_TRACE_JSCALLS
     /* Function entry/exit debugging callback. */
     JSFunctionCallback    functionCallback;
 
@@ -1401,18 +1432,18 @@ struct JSContext : js::ContextFriendFiel
     js::Value getPendingException() {
         JS_ASSERT(throwing);
         return exception;
     }
 
     void setPendingException(js::Value v);
 
     void clearPendingException() {
-        this->throwing = false;
-        this->exception.setUndefined();
+        throwing = false;
+        exception.setUndefined();
     }
 
 #ifdef DEBUG
     /*
      * Controls whether a quadratic-complexity assertion is performed during
      * stack iteration; defaults to true.
      */
     bool stackIterAssertionEnabled;
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -473,24 +473,18 @@ CallSetter(JSContext *cx, HandleObject o
 }  /* namespace js */
 
 inline JSVersion
 JSContext::findVersion() const
 {
     if (hasVersionOverride)
         return versionOverride;
 
-    if (stack.hasfp()) {
-        /* There may be a scripted function somewhere on the stack! */
-        js::StackFrame *f = fp();
-        while (f && !f->isScriptFrame())
-            f = f->prev();
-        if (f)
-            return f->script()->getVersion();
-    }
+    if (stack.hasfp())
+        return fp()->script()->getVersion();
 
     return defaultVersion;
 }
 
 inline bool
 JSContext::canSetDefaultVersion() const
 {
     return !stack.hasfp() && !hasVersionOverride;
@@ -562,23 +556,71 @@ JSContext::ensureParseMapPool()
 }
 
 inline js::PropertyTree&
 JSContext::propertyTree()
 {
     return compartment->propertyTree;
 }
 
+inline bool
+JSContext::hasEnteredCompartment() const
+{
+    return enterCompartmentDepth_ > 0;
+}
+
+inline void
+JSContext::enterCompartment(JSCompartment *c)
+{
+    enterCompartmentDepth_++;
+    compartment = c;
+    if (throwing)
+        wrapPendingException();
+}
+
+inline void
+JSContext::leaveCompartment(JSCompartment *oldCompartment)
+{
+    JS_ASSERT(hasEnteredCompartment());
+    enterCompartmentDepth_--;
+
+    /*
+     * Before we entered the current compartment, 'compartment' was
+     * 'oldCompartment', so we might want to simply set it back. However, we
+     * currently have this terrible scheme whereby defaultCompartmentObject_
+     * can be updated while enterCompartmentDepth_ > 0. In this case,
+     * oldCompartment != defaultCompartmentObject_->compartment and we must
+     * ignore oldCompartment.
+     */
+    if (hasEnteredCompartment() || !defaultCompartmentObject_)
+        compartment = oldCompartment;
+    else
+        compartment = defaultCompartmentObject_->compartment();
+
+    if (throwing)
+        wrapPendingException();
+}
+
 inline void
 JSContext::setDefaultCompartmentObject(JSObject *obj)
 {
     defaultCompartmentObject_ = obj;
 
-    if (!hasfp())
-        resetCompartment();
+    if (!hasEnteredCompartment()) {
+        /*
+         * If JSAPI callers want to JS_SetGlobalObject while code is running,
+         * they must have entered a compartment (otherwise there will be no
+         * final leaveCompartment call to set the context's compartment back to
+         * defaultCompartmentObject->compartment()).
+         */
+        JS_ASSERT(!hasfp());
+        compartment = obj ? obj->compartment() : NULL;
+        if (throwing)
+            wrapPendingException();
+    }
 }
 
 inline void
 JSContext::setDefaultCompartmentObjectIfUnset(JSObject *obj)
 {
     if (!defaultCompartmentObject_)
         setDefaultCompartmentObject(obj);
 }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -640,18 +640,17 @@ JSCompartment::onTooMuchMalloc()
     TriggerCompartmentGC(this, gcreason::TOO_MUCH_MALLOC);
 }
 
 
 bool
 JSCompartment::hasScriptsOnStack()
 {
     for (AllFramesIter i(rt->stackSpace); !i.done(); ++i) {
-        JSScript *script = i.fp()->maybeScript();
-        if (script && script->compartment() == this)
+        if (i.fp()->script()->compartment() == this)
             return true;
     }
     return false;
 }
 
 bool
 JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeGC &dmgc)
 {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -446,72 +446,30 @@ class js::AutoDebugModeGC
 
     void scheduleGC(JSCompartment *compartment) {
         JS_ASSERT(!rt->isHeapBusy());
         PrepareCompartmentForGC(compartment);
         needGC = true;
     }
 };
 
-inline void
-JSContext::setCompartment(JSCompartment *compartment)
+inline bool
+JSContext::typeInferenceEnabled() const
 {
-    this->compartment = compartment;
-    this->inferenceEnabled = compartment ? compartment->types.inferenceEnabled : false;
+    return compartment->types.inferenceEnabled;
 }
 
 inline js::Handle<js::GlobalObject*>
 JSContext::global() const
 {
     return js::Handle<js::GlobalObject*>::fromMarkedLocation(&compartment->global_);
 }
 
 namespace js {
 
-class PreserveCompartment {
-  protected:
-    JSContext *cx;
-  private:
-    JSCompartment *oldCompartment;
-    bool oldInferenceEnabled;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-  public:
-     PreserveCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx) {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-        oldCompartment = cx->compartment;
-        oldInferenceEnabled = cx->inferenceEnabled;
-    }
-
-    ~PreserveCompartment() {
-        /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */
-        cx->compartment = oldCompartment;
-        cx->inferenceEnabled = oldInferenceEnabled;
-    }
-};
-
-class SwitchToCompartment : public PreserveCompartment {
-  public:
-    SwitchToCompartment(JSContext *cx, JSCompartment *newCompartment
-                        JS_GUARD_OBJECT_NOTIFIER_PARAM)
-        : PreserveCompartment(cx)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-        cx->setCompartment(newCompartment);
-    }
-
-    SwitchToCompartment(JSContext *cx, JSObject *target JS_GUARD_OBJECT_NOTIFIER_PARAM)
-        : PreserveCompartment(cx)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-        cx->setCompartment(target->compartment());
-    }
-
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 class AssertCompartmentUnchanged {
   protected:
     JSContext * const cx;
     JSCompartment * const oldCompartment;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
   public:
      AssertCompartmentUnchanged(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM)
      : cx(cx), oldCompartment(cx->compartment) {
@@ -525,32 +483,66 @@ class AssertCompartmentUnchanged {
 
 class AutoCompartment
 {
   public:
     JSContext * const context;
     JSCompartment * const origin;
     JSCompartment * const destination;
   private:
-    Maybe<DummyFrameGuard> frame;
     bool entered;
 
   public:
-    AutoCompartment(JSContext *cx, JSObject *target);
-    ~AutoCompartment();
+    AutoCompartment(JSContext *cx, JSObject *target)
+      : context(cx),
+        origin(cx->compartment),
+        destination(target->compartment()),
+        entered(false)
+    {}
 
-    bool enter();
-    void leave();
+    ~AutoCompartment() {
+        if (entered)
+            leave();
+    }
+
+    bool enter() { context->enterCompartment(destination); entered = true; return true; }
+    void leave() { JS_ASSERT(entered); context->leaveCompartment(origin); entered = false; }
 
   private:
     AutoCompartment(const AutoCompartment &) MOZ_DELETE;
     AutoCompartment & operator=(const AutoCompartment &) MOZ_DELETE;
 };
 
 /*
+ * Entering the atoms comaprtment is not possible with the AutoCompartment
+ * since the atoms compartment does not have a global.
+ *
+ * Note: since most of the VM assumes that cx->global is non-null, only a
+ * restricted set of (atom creating/destroying) operations may be used from
+ * inside the atoms compartment.
+ */
+class AutoEnterAtomsCompartment
+{
+    JSContext *cx;
+    JSCompartment *oldCompartment;
+  public:
+    AutoEnterAtomsCompartment(JSContext *cx)
+      : cx(cx),
+        oldCompartment(cx->compartment)
+    {
+        cx->setCompartment(cx->runtime->atomsCompartment);
+    }
+
+    ~AutoEnterAtomsCompartment()
+    {
+        cx->setCompartment(oldCompartment);
+    }
+};
+
+/*
  * Use this to change the behavior of an AutoCompartment slightly on error. If
  * the exception happens to be an Error object, copy it to the origin compartment
  * instead of wrapping it.
  */
 class ErrorCopier
 {
     AutoCompartment &ac;
     RootedObject scope;
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -488,75 +488,58 @@ JS_FrameIterator(JSContext *cx, JSStackF
     StackFrame *fp = Valueify(*iteratorp);
     *iteratorp = Jsvalify((fp == NULL) ? js_GetTopStackFrame(cx, FRAME_EXPAND_ALL) : fp->prev());
     return *iteratorp;
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_GetFrameScript(JSContext *cx, JSStackFrame *fpArg)
 {
-    StackFrame *fp = Valueify(fpArg);
-    if (fp->isDummyFrame())
-        return NULL;
-
-    return fp->maybeScript();
+    return Valueify(fpArg)->script();
 }
 
 JS_PUBLIC_API(jsbytecode *)
 JS_GetFramePC(JSContext *cx, JSStackFrame *fpArg)
 {
-    StackFrame *fp = Valueify(fpArg);
-    if (fp->isDummyFrame())
-        return NULL;
-
     /*
      * This API is used to compute the line number for jsd and XPConnect
      * exception handling backtraces. Once the stack gets really deep, the
      * overall cost can become quadratic. This can hang the browser (eventually
      * terminated by a slow-script dialog) when content causes infinite
      * recursion and a backtrace.
      */
-    return fp->pcQuadratic(cx->stack, 100);
+    return Valueify(fpArg)->pcQuadratic(cx->stack, 100);
 }
 
 JS_PUBLIC_API(void *)
 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fpArg)
 {
     StackFrame *fp = Valueify(fpArg);
-    if (fp->annotation() && fp->isScriptFrame()) {
-        if (fp->scopeChain()->compartment()->principals) {
-            /*
-             * Give out an annotation only if privileges have not been revoked
-             * or disabled globally.
-             */
-            return fp->annotation();
-        }
+    if (fp->annotation() && fp->scopeChain()->compartment()->principals) {
+        /*
+         * Give out an annotation only if privileges have not been revoked
+         * or disabled globally.
+         */
+        return fp->annotation();
     }
 
     return NULL;
 }
 
 JS_PUBLIC_API(void)
 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
 {
     Valueify(fp)->setAnnotation(annotation);
 }
 
-JS_PUBLIC_API(JSBool)
-JS_IsScriptFrame(JSContext *cx, JSStackFrame *fp)
-{
-    return !Valueify(fp)->isDummyFrame();
-}
-
 JS_PUBLIC_API(JSObject *)
 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fpArg)
 {
     StackFrame *fp = Valueify(fpArg);
     JS_ASSERT(cx->stack.space().containsSlow(fp));
-
     js::AutoCompartment ac(cx, fp->scopeChain());
     if (!ac.enter())
         return NULL;
 
     return GetDebugScopeForFrame(cx, fp);
 }
 
 JS_PUBLIC_API(JSObject *)
@@ -586,18 +569,16 @@ JS_GetFrameCallObject(JSContext *cx, JSS
     }
     return NULL;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetFrameThis(JSContext *cx, JSStackFrame *fpArg, jsval *thisv)
 {
     StackFrame *fp = Valueify(fpArg);
-    if (fp->isDummyFrame())
-        return false;
 
     js::AutoCompartment ac(cx, fp->scopeChain());
     if (!ac.enter())
         return false;
 
     if (!ComputeThis(cx, fp))
         return false;
     *thisv = fp->thisValue();
@@ -671,17 +652,17 @@ JS_GetFrameReturnValue(JSContext *cx, JS
     return Valueify(fp)->returnValue();
 }
 
 JS_PUBLIC_API(void)
 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fpArg, jsval rval)
 {
     StackFrame *fp = Valueify(fpArg);
 #ifdef JS_METHODJIT
-    JS_ASSERT_IF(fp->isScriptFrame(), fp->script()->debugMode);
+    JS_ASSERT(fp->script()->debugMode);
 #endif
     assertSameCompartment(cx, fp, rval);
     fp->setReturnValue(rval);
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(const char *)
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -216,19 +216,16 @@ extern JS_PUBLIC_API(jsbytecode *)
 JS_GetFramePC(JSContext *cx, JSStackFrame *fp);
 
 extern JS_PUBLIC_API(void *)
 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp);
 
 extern JS_PUBLIC_API(void)
 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation);
 
-extern JS_PUBLIC_API(JSBool)
-JS_IsScriptFrame(JSContext *cx, JSStackFrame *fp);
-
 extern JS_PUBLIC_API(JSObject *)
 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp, jsval *thisv);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2218,18 +2218,17 @@ gc_lock_traversal(const GCLocks::Entry &
     JS_ASSERT(tmp == entry.key);
 }
 
 namespace js {
 
 void
 MarkCompartmentActive(StackFrame *fp)
 {
-    if (fp->isScriptFrame())
-        fp->script()->compartment()->active = true;
+    fp->script()->compartment()->active = true;
 }
 
 } /* namespace js */
 
 void
 AutoIdArray::trace(JSTracer *trc)
 {
     JS_ASSERT(tag == IDARRAY);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -529,18 +529,17 @@ FixObjectType(JSContext *cx, JSObject *o
 /* 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)
 {
     return fp->isConstructing() && cx->typeInferenceEnabled() &&
-           fp->prev() && fp->prev()->isScriptFrame() &&
-           UseNewType(cx, fp->prev()->script(), fp->prevpc());
+           fp->prev() && UseNewType(cx, fp->prev()->script(), fp->prevpc());
 }
 
 inline bool
 UseNewTypeForClone(JSFunction *fun)
 {
     if (fun->hasSingletonType() || !fun->isInterpreted())
         return false;
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4683,20 +4683,18 @@ js::GetMethod(JSContext *cx, HandleObjec
 JS_FRIEND_API(bool)
 js::CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
 {
     StackFrame *const fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
     if (!fp)
         return true;
 
     /* If neither cx nor the code is strict, then no check is needed. */
-    if (!(fp->isScriptFrame() && fp->script()->strictModeCode) &&
-        !cx->hasStrictOption()) {
+    if (!fp->script()->strictModeCode && !cx->hasStrictOption())
         return true;
-    }
 
     JSAutoByteString bytes(cx, propname);
     return !!bytes &&
            JS_ReportErrorFlagsAndNumber(cx,
                                         (JSREPORT_WARNING | JSREPORT_STRICT
                                          | JSREPORT_STRICT_MODE_ERROR),
                                         js_GetErrorMessage, NULL,
                                         JSMSG_UNDECLARED_VAR, bytes.ptr());
@@ -5736,37 +5734,27 @@ js_DumpStackFrame(JSContext *cx, StackFr
         if (fp->isFunctionFrame()) {
             fprintf(stderr, "callee fun: ");
             dumpValue(ObjectValue(fp->callee()));
         } else {
             fprintf(stderr, "global frame, no callee");
         }
         fputc('\n', stderr);
 
-        if (fp->isScriptFrame()) {
-            fprintf(stderr, "file %s line %u\n",
-                    fp->script()->filename, (unsigned) fp->script()->lineno);
-        }
+        fprintf(stderr, "file %s line %u\n",
+                fp->script()->filename, (unsigned) fp->script()->lineno);
 
         if (jsbytecode *pc = i.pc()) {
-            if (!fp->isScriptFrame()) {
-                fprintf(stderr, "*** pc && !script, skipping frame\n\n");
-                continue;
-            }
             fprintf(stderr, "  pc = %p\n", pc);
             fprintf(stderr, "  current op: %s\n", js_CodeName[*pc]);
         }
         MaybeDumpObject("blockChain", fp->maybeBlockChain());
-        if (!fp->isDummyFrame()) {
-            MaybeDumpValue("this", fp->thisValue());
-            fprintf(stderr, "  rval: ");
-            dumpValue(fp->returnValue());
-        } else {
-            fprintf(stderr, "dummy frame");
-        }
+        MaybeDumpValue("this", fp->thisValue());
+        fprintf(stderr, "  rval: ");
+        dumpValue(fp->returnValue());
         fputc('\n', stderr);
 
         fprintf(stderr, "  flags:");
         if (fp->isConstructing())
             fprintf(stderr, " constructing");
         if (fp->isDebuggerFrame())
             fprintf(stderr, " debugger");
         if (fp->isEvalFrame())
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -6168,19 +6168,21 @@ static bool
 DecompileExpressionFromStack(JSContext *cx, int spindex, int skipStackHits, Value v, char **res)
 {
     JS_ASSERT(spindex < 0 ||
               spindex == JSDVG_IGNORE_STACK ||
               spindex == JSDVG_SEARCH_STACK);
 
     *res = NULL;
 
-    if (!cx->hasfp() || !cx->fp()->isScriptFrame())
+    ScriptFrameIter iter(cx);
+    if (iter.done())
         return true;
-    StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
+
+    StackFrame *fp = iter.fp();
     JSScript *script = fp->script();
     jsbytecode *valuepc = cx->regs().pc;
     JSFunction *fun = fp->maybeFun();
     JS_ASSERT(script->code <= valuepc && valuepc < script->code + script->length);
 
     // Give up if in prologue.
     if (valuepc < script->main())
         return true;
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -375,105 +375,29 @@ DirectWrapper::fun_toString(JSContext *c
     return str;
 }
 
 DirectWrapper DirectWrapper::singleton((unsigned)0);
 DirectWrapper DirectWrapper::singletonWithPrototype((unsigned)0, true);
 
 /* Compartments. */
 
-namespace js {
-
 extern JSObject *
-TransparentObjectWrapper(JSContext *cx, JSObject *objArg, JSObject *wrappedProtoArg, JSObject *parentArg,
-                         unsigned flags)
+js::TransparentObjectWrapper(JSContext *cx, JSObject *objArg, JSObject *wrappedProtoArg, JSObject *parentArg,
+                             unsigned flags)
 {
     RootedObject obj(cx, objArg);
     RootedObject wrappedProto(cx, wrappedProtoArg);
     RootedObject parent(cx, parentArg);
 
     // Allow wrapping outer window proxies.
     JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
     return Wrapper::New(cx, obj, wrappedProto, parent, &CrossCompartmentWrapper::singleton);
 }
 
-}
-
-ForceFrame::ForceFrame(JSContext *cx, JSObject *target)
-    : context(cx),
-      target(target),
-      frame(NULL)
-{
-}
-
-ForceFrame::~ForceFrame()
-{
-    context->delete_(frame);
-}
-
-bool
-ForceFrame::enter()
-{
-    frame = context->new_<DummyFrameGuard>();
-    if (!frame)
-       return false;
-
-    JS_ASSERT(context->compartment == target->compartment());
-    JSCompartment *destination = context->compartment;
-
-    JSObject &scopeChain = target->global();
-    JS_ASSERT(scopeChain.isNative());
-
-    return context->stack.pushDummyFrame(context, destination, scopeChain, frame);
-}
-
-AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
-    : context(cx),
-      origin(cx->compartment),
-      destination(target->compartment()),
-      entered(false)
-{
-}
-
-AutoCompartment::~AutoCompartment()
-{
-    if (entered)
-        leave();
-}
-
-bool
-AutoCompartment::enter()
-{
-    JS_ASSERT(!entered);
-    if (origin != destination) {
-        GlobalObject& scopeChain = *destination->maybeGlobal();
-        JS_ASSERT(scopeChain.isNative());
-
-        frame.construct();
-        if (!context->stack.pushDummyFrame(context, destination, scopeChain, &frame.ref()))
-            return false;
-
-        if (context->isExceptionPending())
-            context->wrapPendingException();
-    }
-    entered = true;
-    return true;
-}
-
-void
-AutoCompartment::leave()
-{
-    JS_ASSERT(entered);
-    if (origin != destination) {
-        frame.destroy();
-        context->resetCompartment();
-    }
-    entered = false;
-}
-
 ErrorCopier::~ErrorCopier()
 {
     JSContext *cx = ac.context;
     if (cx->compartment == ac.destination &&
         ac.origin != ac.destination &&
         cx->isExceptionPending())
     {
         Value exc = cx->getPendingException();
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -277,34 +277,16 @@ class JS_FRIEND_API(SecurityWrapper) : p
                             CallArgs args) MOZ_OVERRIDE;
     virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE;
 };
 
 typedef SecurityWrapper<DirectWrapper> SameCompartmentSecurityWrapper;
 typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper;
 
-/*
- * A hacky class that lets a friend force a fake frame. We must already be
- * in the compartment of |target| when we enter the forced frame.
- */
-class JS_FRIEND_API(ForceFrame)
-{
-  public:
-    JSContext * const context;
-    JSObject * const target;
-  private:
-    DummyFrameGuard *frame;
-
-  public:
-    ForceFrame(JSContext *cx, JSObject *target);
-    ~ForceFrame();
-    bool enter();
-};
-
 class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler
 {
   public:
     static int sDeadObjectFamily;
 
     explicit DeadObjectProxy();
 
     /* ES5 Harmony fundamental wrapper traps. */
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1678,17 +1678,17 @@ GetXMLSettingFlags(JSContext *cx, unsign
         if (flag[n])
             *flagsp |= JS_BIT(n);
     return true;
 }
 
 static JSObject *
 GetCurrentScopeChain(JSContext *cx)
 {
-    if (cx->hasfp())
+    if (cx->hasfp() && cx->fp()->scopeChain()->compartment() == cx->compartment)
         return cx->fp()->scopeChain();
     return cx->global();
 }
 
 static JSXML *
 ParseXMLSource(JSContext *cx, HandleString src)
 {
     jsval nsval;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2649,31 +2649,26 @@ EvalInFrame(JSContext *cx, unsigned argc
     JS_ASSERT(cx->hasfp());
 
     ScriptFrameIter fi(cx);
     for (uint32_t i = 0; i < upCount; ++i, ++fi) {
         if (!fi.fp()->prev())
             break;
     }
 
-    StackFrame *const fp = fi.fp();
-    if (!fp->isScriptFrame()) {
-        JS_ReportError(cx, "cannot eval in non-script frame");
-        return false;
-    }
-
     bool saved = false;
     if (saveCurrent)
         saved = JS_SaveFrameChain(cx);
 
     size_t length;
     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
     if (!chars)
         return false;
 
+    StackFrame *fp = fi.fp();
     bool ok = !!JS_EvaluateUCInStackFrame(cx, Jsvalify(fp), chars, length,
                                           fp->script()->filename,
                                           JS_PCToLineNumber(cx, fp->script(),
                                                             fi.pc()),
                                           vp);
 
     if (saved)
         JS_RestoreFrameChain(cx);
@@ -3482,20 +3477,23 @@ Deserialize(JSContext *cx, unsigned argc
 }
 
 static JSObject *
 NewGlobalObject(JSContext *cx);
 
 static JSBool
 NewGlobal(JSContext *cx, unsigned argc, jsval *vp)
 {
-    JSObject *global = NewGlobalObject(cx);
+    RootedObject global(cx, NewGlobalObject(cx));
     if (!global)
         return false;
 
+    if (!JS_WrapObject(cx, global.address()))
+        return false;
+
     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(global));
     return true;
 }
 
 static JSBool
 ParseLegacyJSON(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -4719,19 +4717,16 @@ NewGlobalObject(JSContext *cx)
         if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
                                its_setter, 0))
             return NULL;
         if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
                                its_setter, JSPROP_READONLY))
             return NULL;
     }
 
-    if (!JS_WrapObject(cx, glob.address()))
-        return NULL;
-
     return glob;
 }
 
 static bool
 BindScriptArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
 {
     RootedObject obj(cx, obj_);
 
@@ -4847,20 +4842,16 @@ Shell(JSContext *cx, OptionParser *op, c
         JS_ToggleOptions(cx, JSOPTION_TYPE_INFERENCE);
     }
 
     RootedObject glob(cx);
     glob = NewGlobalObject(cx);
     if (!glob)
         return 1;
 
-    JSAutoEnterCompartment ac;
-    if (!ac.enter(cx, glob))
-        return 1;
-
     JS_SetGlobalObject(cx, glob);
 
     JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
     if (!envobj)
         return 1;
     JS_SetPrivate(envobj, envp);
 
     int result = ProcessArgs(cx, glob, op);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -403,17 +403,16 @@ Debugger::fromChildJSObject(JSObject *ob
               obj->getClass() == &DebuggerEnv_class);
     JSObject *dbgobj = &obj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER).toObject();
     return fromJSObject(dbgobj);
 }
 
 bool
 Debugger::getScriptFrame(JSContext *cx, StackFrame *fp, Value *vp)
 {
-    JS_ASSERT(fp->isScriptFrame());
     FrameMap::AddPtr p = frames.lookupForAdd(fp);
     if (!p) {
         /* Create and populate the Debugger.Frame object. */
         JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject();
         JSObject *frameobj =
             NewObjectWithGivenProto(cx, &DebuggerFrame_class, proto, NULL);
         if (!frameobj)
             return false;
@@ -572,17 +571,16 @@ Debugger::slowPathOnLeaveFrame(JSContext
         JSObject *frameobj = r.frontFrame();
         Debugger *dbg = r.frontDebugger();
         JS_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
 
         frameobj->setPrivate(NULL);
 
         /* If this frame had an onStep handler, adjust the script's count. */
         if (!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
-            fp->isScriptFrame() &&
             !fp->script()->changeStepModeCount(cx, -1))
         {
             status = JSTRAP_ERROR;
             /* Don't exit the loop; we must mark all frames as dead. */
         }
 
         dbg->frames.remove(fp);
     }
@@ -1168,19 +1166,16 @@ Debugger::onSingleStep(JSContext *cx, Va
      */
     RootedValue exception(cx, UndefinedValue());
     bool exceptionPending = cx->isExceptionPending();
     if (exceptionPending) {
         exception = cx->getPendingException();
         cx->clearPendingException();
     }
 
-    /* We should only receive single-step traps for scripted frames. */
-    JS_ASSERT(fp->isScriptFrame());
-
     /*
      * Build list of Debugger.Frame instances referring to this frame with
      * onStep handlers.
      */
     AutoObjectVector frames(cx);
     for (FrameRange r(fp); !r.empty(); r.popFront()) {
         JSObject *frame = r.frontFrame();
         if (!frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
@@ -1205,18 +1200,17 @@ Debugger::onSingleStep(JSContext *cx, Va
         JSScript *trappingScript = fp->script();
         GlobalObject *global = &fp->global();
         if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
             for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
                 Debugger *dbg = *p;
                 for (FrameMap::Range r = dbg->frames.all(); !r.empty(); r.popFront()) {
                     StackFrame *frame = r.front().key;
                     JSObject *frameobj = r.front().value;
-                    if (frame->isScriptFrame() &&
-                        frame->script() == trappingScript &&
+                    if (frame->script() == trappingScript &&
                         !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
                     {
                         stepperCount++;
                     }
                 }
             }
         }
         if (trappingScript->compileAndGo)
@@ -3303,17 +3297,17 @@ DebuggerFrame_getScript(JSContext *cx, u
     if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
         JSFunction &callee = fp->callee();
         if (callee.isInterpreted()) {
             Rooted<JSScript*> script(cx, callee.script());
             scriptObject = debug->wrapScript(cx, script);
             if (!scriptObject)
                 return false;
         }
-    } else if (fp->isScriptFrame()) {
+    } else {
         /*
          * We got eval, JS_Evaluate*, or JS_ExecuteScript non-function script
          * frames.
          */
         Rooted<JSScript*> script(cx, fp->script());
         scriptObject = debug->wrapScript(cx, script);
         if (!scriptObject)
             return false;
@@ -3321,26 +3315,22 @@ DebuggerFrame_getScript(JSContext *cx, u
     args.rval().setObjectOrNull(scriptObject);
     return true;
 }
 
 static JSBool
 DebuggerFrame_getOffset(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_FRAME(cx, argc, vp, "get offset", args, thisobj, fp);
-    if (fp->isScriptFrame()) {
-        JSScript *script = fp->script();
-        jsbytecode *pc = fp->pcQuadratic(cx);
-        JS_ASSERT(script->code <= pc);
-        JS_ASSERT(pc < script->code + script->length);
-        size_t offset = pc - script->code;
-        args.rval().setNumber(double(offset));
-    } else {
-        args.rval().setUndefined();
-    }
+    JSScript *script = fp->script();
+    jsbytecode *pc = fp->pcQuadratic(cx);
+    JS_ASSERT(script->code <= pc);
+    JS_ASSERT(pc < script->code + script->length);
+    size_t offset = pc - script->code;
+    args.rval().setNumber(double(offset));
     return true;
 }
 
 static JSBool
 DebuggerFrame_getLive(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSObject *thisobj = CheckThisFrame(cx, args, "get live", false);
@@ -3368,20 +3358,16 @@ DebuggerFrame_getOnStep(JSContext *cx, u
     return true;
 }
 
 static JSBool
 DebuggerFrame_setOnStep(JSContext *cx, unsigned argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.Frame.set onStep", 1);
     THIS_FRAME(cx, argc, vp, "set onStep", args, thisobj, fp);
-    if (!fp->isScriptFrame()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_SCRIPT_FRAME);
-        return false;
-    }
     if (!IsValidHook(args[0])) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
         return false;
     }
 
     Value prior = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
     int delta = !args[0].isUndefined() - !prior.isUndefined();
     if (delta != 0) {
@@ -3410,20 +3396,16 @@ DebuggerFrame_getOnPop(JSContext *cx, un
     return true;
 }
 
 static JSBool
 DebuggerFrame_setOnPop(JSContext *cx, unsigned argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.Frame.set onPop", 1);
     THIS_FRAME(cx, argc, vp, "set onPop", args, thisobj, fp);
-    if (!fp->isScriptFrame()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_SCRIPT_FRAME);
-        return false;
-    }
     if (!IsValidHook(args[0])) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
         return false;
     }
 
     thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER, args[0]);
     args.rval().setUndefined();
     return true;
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -468,17 +468,17 @@ bool
 Debugger::observesGlobal(GlobalObject *global) const
 {
     return debuggees.has(global);
 }
 
 bool
 Debugger::observesFrame(StackFrame *fp) const
 {
-    return !fp->isDummyFrame() && observesGlobal(&fp->global());
+    return observesGlobal(&fp->global());
 }
 
 JSTrapStatus
 Debugger::onEnterFrame(JSContext *cx, Value *vp)
 {
     if (cx->compartment->getDebuggees().empty())
         return JSTRAP_CONTINUE;
     return slowPathOnEnterFrame(cx, vp);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -168,16 +168,17 @@ CallObject::create(JSContext *cx, JSScri
 
     return &obj->asCall();
 }
 
 CallObject *
 CallObject::createForFunction(JSContext *cx, StackFrame *fp)
 {
     JS_ASSERT(fp->isNonEvalFunctionFrame());
+    assertSameCompartment(cx, fp);
 
     RootedObject scopeChain(cx, fp->scopeChain());
 
     /*
      * For a named function expression Call's parent points to an environment
      * object holding function's name.
      */
     if (fp->fun()->isNamedLambda()) {
@@ -234,16 +235,18 @@ Class js::DeclEnvClass = {
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
 DeclEnvObject *
 DeclEnvObject::create(JSContext *cx, StackFrame *fp)
 {
+    assertSameCompartment(cx, fp);
+
     RootedTypeObject type(cx, cx->compartment->getEmptyType(cx));
     if (!type)
         return NULL;
 
     RootedShape emptyDeclEnvShape(cx);
     emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &DeclEnvClass, NULL,
                                                     &fp->global(), FINALIZE_KIND);
     if (!emptyDeclEnvShape)
@@ -551,16 +554,18 @@ Class js::WithClass = {
     }
 };
 
 /*****************************************************************************/
 
 ClonedBlockObject *
 ClonedBlockObject::create(JSContext *cx, Handle<StaticBlockObject *> block, StackFrame *fp)
 {
+    assertSameCompartment(cx, fp);
+
     RootedTypeObject type(cx, block->getNewType(cx));
     if (!type)
         return NULL;
 
     HeapSlot *slots;
     if (!PreallocateObjectDynamicSlots(cx, block->lastProperty(), &slots))
         return NULL;
 
@@ -845,16 +850,17 @@ ScopeIter::ScopeIter(JSObject &enclosing
 }
 
 ScopeIter::ScopeIter(StackFrame *fp, JSContext *cx
                      JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT)
   : fp_(fp),
     cur_(cx, fp->scopeChain()),
     block_(cx, fp->maybeBlockChain())
 {
+    assertSameCompartment(cx, fp);
     settle();
     JS_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
 ScopeIter::ScopeIter(const ScopeIter &si, StackFrame *fp, JSContext *cx
                      JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT)
   : fp_(fp),
     cur_(cx, si.cur_),
@@ -1624,16 +1630,17 @@ DebugScopes::addDebugScope(JSContext *cx
     }
     return true;
 }
 
 void
 DebugScopes::onPopCall(StackFrame *fp, JSContext *cx)
 {
     JS_ASSERT(!fp->isYielding());
+    assertSameCompartment(cx, fp);
 
     DebugScopeObject *debugScope = NULL;
 
     if (fp->fun()->isHeavyweight()) {
         /*
          * The StackFrame may be observed before the prologue has created the
          * CallObject. See ScopeIter::settle.
          */
@@ -1697,16 +1704,18 @@ DebugScopes::onPopCall(StackFrame *fp, J
 
         debugScope->initSnapshot(*snapshot);
     }
 }
 
 void
 DebugScopes::onPopBlock(JSContext *cx, StackFrame *fp)
 {
+    assertSameCompartment(cx, fp);
+
     StaticBlockObject &staticBlock = *fp->maybeBlockChain();
     if (staticBlock.needsClone()) {
         ClonedBlockObject &clone = fp->scopeChain()->asClonedBlock();
         clone.copyUnaliasedValues(fp);
         liveScopes.remove(&clone);
     } else {
         ScopeIter si(fp, cx);
         if (MissingScopeMap::Ptr p = missingScopes.lookup(si)) {
@@ -1790,17 +1799,17 @@ DebugScopes::updateLiveScopes(JSContext 
      * 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->stackSpace); !i.done(); ++i) {
         StackFrame *fp = i.fp();
-        if (fp->isDummyFrame() || fp->scopeChain()->compartment() != cx->compartment)
+        if (fp->scopeChain()->compartment() != cx->compartment)
             continue;
 
         for (ScopeIter si(fp, cx); !si.done(); ++si) {
             if (si.hasScopeObject() && !liveScopes.put(&si.scope(), fp))
                 return false;
         }
 
         if (fp->prevUpToDate())
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -60,17 +60,17 @@ StackFrame::varObj()
     while (!obj->isVarObj())
         obj = obj->enclosingScope();
     return *obj;
 }
 
 inline JSCompartment *
 StackFrame::compartment() const
 {
-    JS_ASSERT_IF(isScriptFrame(), scopeChain()->compartment() == script()->compartment());
+    JS_ASSERT(scopeChain()->compartment() == script()->compartment());
     return scopeChain()->compartment();
 }
 
 #ifdef JS_METHODJIT
 inline mjit::JITScript *
 StackFrame::jit()
 {
     JSScript *script_ = script();
@@ -81,18 +81,17 @@ StackFrame::jit()
 inline void
 StackFrame::initPrev(JSContext *cx)
 {
     JS_ASSERT(flags_ & HAS_PREVPC);
     if (FrameRegs *regs = cx->maybeRegs()) {
         prev_ = regs->fp();
         prevpc_ = regs->pc;
         prevInline_ = regs->inlined();
-        JS_ASSERT_IF(!prev_->isDummyFrame(),
-                     uint32_t(prevpc_ - prev_->script()->code) < prev_->script()->length);
+        JS_ASSERT(uint32_t(prevpc_ - prev_->script()->code) < prev_->script()->length);
     } else {
         prev_ = NULL;
 #ifdef DEBUG
         prevpc_ = (jsbytecode *)0xbadc;
         prevInline_ = (InlinedSite *)0xbadc;
 #endif
     }
 }
@@ -365,26 +364,25 @@ StackFrame::callObj() const
         pobj = pobj->enclosingScope();
     return pobj->asCall();
 }
 
 /*****************************************************************************/
 
 STATIC_POSTCONDITION(!return || ubound(from) >= nvals)
 JS_ALWAYS_INLINE bool
-StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
-                        JSCompartment *dest) const
+StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
 {
     assertInvariants();
     JS_ASSERT(from >= firstUnused());
 #ifdef XP_WIN
     JS_ASSERT(from <= commitEnd_);
 #endif
     if (JS_UNLIKELY(conservativeEnd_ - from < nvals))
-        return ensureSpaceSlow(cx, report, from, nvals, dest);
+        return ensureSpaceSlow(cx, report, from, nvals);
     return true;
 }
 
 inline Value *
 StackSpace::getStackLimit(JSContext *cx, MaybeReportError report)
 {
     FrameRegs &regs = cx->regs();
     unsigned nvals = regs.fp()->script()->nslots + STACK_JIT_EXTRA;
@@ -517,27 +515,26 @@ ContextStack::popFrameAfterOverflow()
 }
 
 inline JSScript *
 ContextStack::currentScript(jsbytecode **ppc) const
 {
     if (ppc)
         *ppc = NULL;
 
-    FrameRegs *regs = maybeRegs();
-    StackFrame *fp = regs ? regs->fp() : NULL;
-    while (fp && fp->isDummyFrame())
-        fp = fp->prev();
-    if (!fp)
+    if (!hasfp())
         return NULL;
 
+    FrameRegs &regs = this->regs();
+    StackFrame *fp = regs.fp();
+
 #ifdef JS_METHODJIT
-    mjit::CallSite *inlined = regs->inlined();
+    mjit::CallSite *inlined = regs.inlined();
     if (inlined) {
-        mjit::JITChunk *chunk = fp->jit()->chunk(regs->pc);
+        mjit::JITChunk *chunk = fp->jit()->chunk(regs.pc);
         JS_ASSERT(inlined->inlineIndex < chunk->nInlineFrames);
         mjit::InlineFrame *frame = &chunk->inlineFrames()[inlined->inlineIndex];
         JSScript *script = frame->fun->script();
         if (script->compartment() != cx_->compartment)
             return NULL;
         if (ppc)
             *ppc = script->code + inlined->pcOffset;
         return script;
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -78,26 +78,16 @@ StackFrame::initExecuteFrame(JSScript *s
     hookData_ = (void *)0xbad;
     annotation_ = (void *)0xbad;
 #endif
 
     if (prev && prev->annotation())
         setAnnotation(prev->annotation());
 }
 
-void
-StackFrame::initDummyFrame(JSContext *cx, JSObject &chain)
-{
-    PodZero(this);
-    flags_ = DUMMY | HAS_PREVPC | HAS_SCOPECHAIN;
-    initPrev(cx);
-    JS_ASSERT(chain.isGlobal());
-    scopeChain_ = &chain;
-}
-
 template <StackFrame::TriggerPostBarriers doPostBarrier>
 void
 StackFrame::copyFrameAndValues(JSContext *cx, Value *vp, StackFrame *otherfp,
                                const Value *othervp, Value *othersp)
 {
     JS_ASSERT(vp == (Value *)this - ((Value *)otherfp - othervp));
     JS_ASSERT(othervp == otherfp->generatorArgsSnapshotBegin());
     JS_ASSERT(othersp >= otherfp->slots());
@@ -138,18 +128,16 @@ void StackFrame::copyFrameAndValues<Stac
                                     JSContext *, Value *, StackFrame *, const Value *, Value *);
 
 void
 StackFrame::writeBarrierPost()
 {
     /* This needs to follow the same rules as in StackFrame::mark. */
     if (scopeChain_)
         JSObject::writeBarrierPost(scopeChain_, (void *)&scopeChain_);
-    if (isDummyFrame())
-        return;
     if (flags_ & HAS_ARGS_OBJ)
         JSObject::writeBarrierPost(argsObj_, (void *)&argsObj_);
     if (isFunctionFrame()) {
         JSFunction::writeBarrierPost(exec.fun, (void *)&exec.fun);
         if (isEvalFrame())
             JSScript::writeBarrierPost(u.evalScript, (void *)&u.evalScript);
     } else {
         JSScript::writeBarrierPost(exec.script, (void *)&exec.script);
@@ -266,17 +254,16 @@ AssertDynamicScopeMatchesStaticScope(JSS
      * scope chain stops at eval() boundaries. See StaticScopeIter comment.
      */
 #endif
 }
 
 bool
 StackFrame::prologue(JSContext *cx, bool newType)
 {
-    JS_ASSERT(!isDummyFrame());
     JS_ASSERT(!isGeneratorFrame());
     JS_ASSERT(cx->regs().pc == script()->code);
 
     if (isEvalFrame()) {
         if (script()->strictModeCode) {
             CallObject *callobj = CallObject::createForStrictEval(cx, this);
             if (!callobj)
                 return false;
@@ -313,17 +300,16 @@ StackFrame::prologue(JSContext *cx, bool
 
     Probes::enterScript(cx, script(), script()->function(), this);
     return true;
 }
 
 void
 StackFrame::epilogue(JSContext *cx)
 {
-    JS_ASSERT(!isDummyFrame());
     JS_ASSERT(!isYielding());
     JS_ASSERT(!hasBlockChain());
 
     Probes::exitScript(cx, script(), script()->function(), this);
 
     if (isEvalFrame()) {
         if (isStrictEvalFrame()) {
             JS_ASSERT_IF(hasCallObj(), scopeChain()->asCall().isForEval());
@@ -426,18 +412,16 @@ StackFrame::mark(JSTracer *trc)
 {
     /*
      * Normally we would use MarkRoot here, except that generators also take
      * this path. However, generators use a special write barrier when the stack
      * frame is copied to the floating frame. Therefore, no barrier is needed.
      */
     if (flags_ & HAS_SCOPECHAIN)
         gc::MarkObjectUnbarriered(trc, &scopeChain_, "scope chain");
-    if (isDummyFrame())
-        return;
     if (flags_ & HAS_ARGS_OBJ)
         gc::MarkObjectUnbarriered(trc, &argsObj_, "arguments");
     if (isFunctionFrame()) {
         gc::MarkObjectUnbarriered(trc, &exec.fun, "fun");
         if (isEvalFrame())
             gc::MarkScriptUnbarriered(trc, &u.evalScript, "eval script");
     } else {
         gc::MarkScriptUnbarriered(trc, &exec.script, "script");
@@ -623,23 +607,16 @@ StackSpace::containingSegment(const Stac
     return *(StackSegment *)NULL;
 }
 
 void
 StackSpace::markAndClobberFrame(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc)
 {
     Value *slotsBegin = fp->slots();
 
-    if (!fp->isScriptFrame()) {
-        JS_ASSERT(fp->isDummyFrame());
-        if (trc)
-            gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
-        return;
-    }
-
     /* If it's a scripted frame, we should have a pc. */
     JS_ASSERT(pc);
 
     JSScript *script = fp->script();
     if (!script->hasAnalysis() || !script->analysis()->ranLifetimes()) {
         if (trc)
             gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
         return;
@@ -744,26 +721,22 @@ StackSpace::markActiveCompartments()
 {
     for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) {
         for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev())
             MarkCompartmentActive(fp);
     }
 }
 
 JS_FRIEND_API(bool)
-StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
-                            JSCompartment *dest) const
+StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
 {
     assertInvariants();
 
-    /* See CX_COMPARTMENT comment. */
-    if (dest == (JSCompartment *)CX_COMPARTMENT)
-        dest = cx->compartment;
-
-    bool trusted = !dest || dest->principals == cx->runtime->trustedPrincipals();
+    JSCompartment *dest = cx->compartment;
+    bool trusted = dest->principals == cx->runtime->trustedPrincipals();
     Value *end = trusted ? trustedEnd_ : defaultEnd_;
 
     /*
      * conservativeEnd_ must stay below defaultEnd_: if conservativeEnd_ were
      * to be bumped past defaultEnd_, untrusted JS would be able to consume the
      * buffer space at the end of the stack reserved for trusted JS.
      */
 
@@ -847,17 +820,17 @@ ContextStack::ContextStack(JSContext *cx
 ContextStack::~ContextStack()
 {
     JS_ASSERT(!seg_);
 }
 
 ptrdiff_t
 ContextStack::spIndexOf(const Value *vp)
 {
-    if (!hasfp() || !fp()->isScriptFrame())
+    if (!hasfp())
         return JSDVG_SEARCH_STACK;
 
     Value *base = fp()->base();
     Value *sp = regs().sp;
     if (vp < base || vp >= sp)
         return JSDVG_SEARCH_STACK;
 
     return vp - sp;
@@ -885,17 +858,17 @@ ContextStack::containsSlow(const StackFr
  * pushing a StackSegment. The 'pushedSeg' outparam indicates whether such a
  * segment was pushed (and hence whether the caller needs to call popSegment).
  *
  * Additionally, to minimize calls to ensureSpace, ensureOnTop ensures that
  * there is space for nvars slots on top of the stack.
  */
 Value *
 ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, unsigned nvars,
-                          MaybeExtend extend, bool *pushedSeg, JSCompartment *dest)
+                          MaybeExtend extend, bool *pushedSeg)
 {
     Value *firstUnused = space().firstUnused();
 
 #ifdef JS_METHODJIT
     /*
      * The only calls made by inlined methodjit frames can be to other JIT
      * frames associated with the same VMFrame. If we try to Invoke(),
      * Execute() or so forth, any topmost inline frame will need to be
@@ -914,30 +887,32 @@ ContextStack::ensureOnTop(JSContext *cx,
             if (fp->isFunctionFrame()) {
                 JSFunction *f = fp->fun();
                 if (f->isInterpreted())
                     fun = f;
             }
         }
 
         if (fun) {
+            AutoCompartment ac(cx, fun);
+            (void) ac.enter();
             fun->script()->uninlineable = true;
             types::MarkTypeObjectFlags(cx, fun, types::OBJECT_FLAG_UNINLINEABLE);
         }
     }
     JS_ASSERT_IF(cx->hasfp(), !cx->regs().inlined());
 #endif
 
     if (onTop() && extend) {
-        if (!space().ensureSpace(cx, report, firstUnused, nvars, dest))
+        if (!space().ensureSpace(cx, report, firstUnused, nvars))
             return NULL;
         return firstUnused;
     }
 
-    if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars, dest))
+    if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
         return NULL;
 
     FrameRegs *regs;
     CallArgsList *calls;
     if (seg_ && extend) {
         regs = seg_->maybeRegs();
         calls = seg_->maybeCalls();
     } else {
@@ -1069,59 +1044,31 @@ ContextStack::pushExecuteFrame(JSContext
         seg_->pointAtCall(*evalInFrameCalls);
 
     efg->prevRegs_ = seg_->pushRegs(efg->regs_);
     JS_ASSERT(space().firstUnused() == efg->regs_.sp);
     efg->setPushed(*this);
     return true;
 }
 
-bool
-ContextStack::pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg)
-{
-    JS_ASSERT(dest == scopeChain.compartment());
-
-    unsigned nvars = VALUES_PER_STACK_FRAME;
-    Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest);
-    if (!firstUnused)
-        return false;
-
-    StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused);
-    fp->initDummyFrame(cx, scopeChain);
-    dfg->regs_.initDummyFrame(*fp);
-
-    cx->setCompartment(dest);
-    dfg->prevRegs_ = seg_->pushRegs(dfg->regs_);
-    JS_ASSERT(space().firstUnused() == dfg->regs_.sp);
-    dfg->setPushed(*this);
-    return true;
-}
-
 void
 ContextStack::popFrame(const FrameGuard &fg)
 {
     JS_ASSERT(fg.pushed());
     JS_ASSERT(onTop());
     JS_ASSERT(space().firstUnused() == fg.regs_.sp);
     JS_ASSERT(&fg.regs_ == &seg_->regs());
 
     Value *oldend = seg_->end();
 
     seg_->popRegs(fg.prevRegs_);
     if (fg.pushedSeg_)
         popSegment();
 
     Debug_SetValueRangeToCrashOnTouch(space().firstUnused(), oldend);
-
-    /*
-     * NB: this code can call out and observe the stack (e.g., through GC), so
-     * it should only be called from a consistent stack state.
-     */
-    if (!hasfp())
-        cx_->resetCompartment();
 }
 
 bool
 ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg)
 {
     HeapValue *genvp = gen->stackSnapshot;
     JS_ASSERT(genvp == HeapValueify(gen->fp->generatorArgsSnapshotBegin()));
     unsigned vplen = HeapValueify(gen->fp->generatorArgsSnapshotEnd()) - genvp;
@@ -1186,37 +1133,35 @@ ContextStack::popGeneratorFrame(const Ge
 
     /* ~FrameGuard/popFrame will finish the popping. */
     JS_ASSERT(ImplicitCast<const FrameGuard>(gfg).pushed());
 }
 
 bool
 ContextStack::saveFrameChain()
 {
-    JSCompartment *dest = NULL;
-
     bool pushedSeg;
-    if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg, dest))
-        return false;
+    if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg))
+        return NULL;
 
     JS_ASSERT(pushedSeg);
     JS_ASSERT(!hasfp());
-    JS_ASSERT(onTop() && seg_->isEmpty());
-
-    cx_->resetCompartment();
+    JS_ASSERT(onTop());
+    JS_ASSERT(seg_->isEmpty());
     return true;
 }
 
 void
 ContextStack::restoreFrameChain()
 {
-    JS_ASSERT(onTop() && seg_->isEmpty());
+    JS_ASSERT(!hasfp());
+    JS_ASSERT(onTop());
+    JS_ASSERT(seg_->isEmpty());
 
     popSegment();
-    cx_->resetCompartment();
 }
 
 /*****************************************************************************/
 
 void
 StackIter::poisonRegs()
 {
     pc_ = (jsbytecode *)0xbad;
@@ -1228,17 +1173,17 @@ StackIter::popFrame()
 {
     StackFrame *oldfp = fp_;
     JS_ASSERT(seg_->contains(oldfp));
     fp_ = fp_->prev();
     if (seg_->contains(fp_)) {
         InlinedSite *inline_;
         pc_ = oldfp->prevpc(&inline_);
         JS_ASSERT(!inline_);
-        script_ = fp_->maybeScript();
+        script_ = fp_->script();
     } else {
         poisonRegs();
     }
 }
 
 void
 StackIter::popCall()
 {
@@ -1250,17 +1195,17 @@ StackIter::popCall()
 }
 
 void
 StackIter::settleOnNewSegment()
 {
     if (FrameRegs *regs = seg_->maybeRegs()) {
         pc_ = regs->pc;
         if (fp_)
-            script_ = fp_->maybeScript();
+            script_ = fp_->script();
     } else {
         poisonRegs();
     }
 }
 
 void
 StackIter::startOnSegment(StackSegment *seg)
 {
@@ -1333,22 +1278,16 @@ StackIter::settleOnNewState()
             settleOnNewSegment();
         }
 
         /*
          * In case of both a scripted frame and call record, use linear memory
          * ordering to decide which was the most recent.
          */
         if (containsFrame && (!containsCall || (Value *)fp_ >= calls_->array())) {
-            /* Nobody wants to see dummy frames. */
-            if (fp_->isDummyFrame()) {
-                popFrame();
-                continue;
-            }
-
             state_ = SCRIPTED;
             script_ = fp_->script();
             return;
         }
 
         /*
          * A CallArgsList element is pushed for any call to Invoke, regardless
          * of whether the callee is a scripted function or even a callable
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -22,17 +22,16 @@ class FrameRegs;
 class StackSegment;
 class StackSpace;
 class ContextStack;
 
 class InvokeArgsGuard;
 class InvokeFrameGuard;
 class FrameGuard;
 class ExecuteFrameGuard;
-class DummyFrameGuard;
 class GeneratorFrameGuard;
 
 class CallIter;
 class ScriptFrameIter;
 class AllFramesIter;
 
 class ArgumentsObject;
 class ScopeObject;
@@ -201,73 +200,72 @@ CallArgsListFromVp(unsigned argc, Value 
 
 enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
 
 /*****************************************************************************/
 
 /* Flags specified for a frame as it is constructed. */
 enum InitialFrameFlags {
     INITIAL_NONE           =          0,
-    INITIAL_CONSTRUCT      =       0x40, /* == StackFrame::CONSTRUCTING, asserted below */
-    INITIAL_LOWERED        =   0x100000  /* == StackFrame::LOWERED_CALL_APPLY, asserted below */
+    INITIAL_CONSTRUCT      =       0x20, /* == StackFrame::CONSTRUCTING, asserted below */
+    INITIAL_LOWERED        =    0x80000  /* == StackFrame::LOWERED_CALL_APPLY, asserted below */
 };
 
 enum ExecuteType {
     EXECUTE_GLOBAL         =        0x1, /* == StackFrame::GLOBAL */
-    EXECUTE_DIRECT_EVAL    =        0x8, /* == StackFrame::EVAL */
-    EXECUTE_INDIRECT_EVAL  =        0x9, /* == StackFrame::GLOBAL | EVAL */
-    EXECUTE_DEBUG          =       0x18  /* == StackFrame::EVAL | DEBUGGER */
+    EXECUTE_DIRECT_EVAL    =        0x4, /* == StackFrame::EVAL */
+    EXECUTE_INDIRECT_EVAL  =        0x5, /* == StackFrame::GLOBAL | EVAL */
+    EXECUTE_DEBUG          =        0xc  /* == StackFrame::EVAL | DEBUGGER */
 };
 
 /*****************************************************************************/
 
 class StackFrame
 {
   public:
     enum Flags {
         /* Primary frame type */
         GLOBAL             =        0x1,  /* frame pushed for a global script */
         FUNCTION           =        0x2,  /* frame pushed for a scripted call */
-        DUMMY              =        0x4,  /* frame pushed for bookkeeping */
 
         /* Frame subtypes */
-        EVAL               =        0x8,  /* frame pushed for eval() or debugger eval */
-        DEBUGGER           =       0x10,  /* frame pushed for debugger eval */
-        GENERATOR          =       0x20,  /* frame is associated with a generator */
-        CONSTRUCTING       =       0x40,  /* frame is for a constructor invocation */
+        EVAL               =        0x4,  /* frame pushed for eval() or debugger eval */
+        DEBUGGER           =        0x8,  /* frame pushed for debugger eval */
+        GENERATOR          =       0x10,  /* frame is associated with a generator */
+        CONSTRUCTING       =       0x20,  /* frame is for a constructor invocation */
 
         /* Temporary frame states */
-        YIELDING           =       0x80,  /* Interpret dispatched JSOP_YIELD */
-        FINISHED_IN_INTERP =      0x100,  /* set if frame finished in Interpret() */
+        YIELDING           =       0x40,  /* Interpret dispatched JSOP_YIELD */
+        FINISHED_IN_INTERP =       0x80,  /* set if frame finished in Interpret() */
 
         /* Function arguments */
-        OVERFLOW_ARGS      =      0x200,  /* numActualArgs > numFormalArgs */
-        UNDERFLOW_ARGS     =      0x400,  /* numActualArgs < numFormalArgs */
+        OVERFLOW_ARGS      =      0x100,  /* numActualArgs > numFormalArgs */
+        UNDERFLOW_ARGS     =      0x200,  /* numActualArgs < numFormalArgs */
 
         /* Function prologue state */
-        HAS_CALL_OBJ       =      0x800,  /* CallObject created for heavyweight fun */
-        HAS_ARGS_OBJ       =     0x1000,  /* ArgumentsObject created for needsArgsObj script */
+        HAS_CALL_OBJ       =      0x400,  /* CallObject created for heavyweight fun */
+        HAS_ARGS_OBJ       =      0x800,  /* ArgumentsObject created for needsArgsObj script */
 
         /* Lazy frame initialization */
-        HAS_HOOK_DATA      =     0x2000,  /* frame has hookData_ set */
-        HAS_ANNOTATION     =     0x4000,  /* frame has annotation_ set */
-        HAS_RVAL           =     0x8000,  /* frame has rval_ set */
-        HAS_SCOPECHAIN     =    0x10000,  /* frame has scopeChain_ set */
-        HAS_PREVPC         =    0x20000,  /* frame has prevpc_ and prevInline_ set */
-        HAS_BLOCKCHAIN     =    0x40000,  /* frame has blockChain_ set */
+        HAS_HOOK_DATA      =     0x1000,  /* frame has hookData_ set */
+        HAS_ANNOTATION     =     0x2000,  /* frame has annotation_ set */
+        HAS_RVAL           =     0x4000,  /* frame has rval_ set */
+        HAS_SCOPECHAIN     =     0x8000,  /* frame has scopeChain_ set */
+        HAS_PREVPC         =    0x10000,  /* frame has prevpc_ and prevInline_ set */
+        HAS_BLOCKCHAIN     =    0x20000,  /* frame has blockChain_ set */
 
         /* Method JIT state */
-        DOWN_FRAMES_EXPANDED =  0x80000,  /* inlining in down frames has been expanded */
-        LOWERED_CALL_APPLY   = 0x100000,  /* Pushed by a lowered call/apply */
+        DOWN_FRAMES_EXPANDED =  0x40000,  /* inlining in down frames has been expanded */
+        LOWERED_CALL_APPLY   =  0x80000,  /* Pushed by a lowered call/apply */
 
         /* Debugger state */
-        PREV_UP_TO_DATE    =   0x200000,  /* see DebugScopes::updateLiveScopes */
+        PREV_UP_TO_DATE    =   0x100000,  /* see DebugScopes::updateLiveScopes */
 
         /* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */
-        HAS_PUSHED_SPS_FRAME = 0x400000  /* SPS was notified of enty */
+        HAS_PUSHED_SPS_FRAME = 0x200000  /* SPS was notified of enty */
     };
 
   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;
@@ -333,19 +331,16 @@ class StackFrame
 
     /* Used for getFixupFrame (for FixupArity). */
     void initFixupFrame(StackFrame *prev, StackFrame::Flags flags, void *ncode, unsigned nactual);
 
     /* Used for eval. */
     void initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
                           const Value &thisv, JSObject &scopeChain, ExecuteType type);
 
-    /* Perhaps one fine day we will remove dummy frames. */
-    void initDummyFrame(JSContext *cx, JSObject &chain);
-
   public:
     /*
      * Frame prologue/epilogue
      *
      * Every stack frame must have 'prologue' called before executing the
      * first op and 'epilogue' called after executing the last op and before
      * popping the frame (whether the exit is exceptional or not).
      *
@@ -377,51 +372,39 @@ class StackFrame
     /*
      * Stack frame type
      *
      * A stack frame may have one of three types, which determines which
      * members of the frame may be accessed and other invariants:
      *
      *  global frame:   execution of global code or an eval in global code
      *  function frame: execution of function code or an eval in a function
-     *  dummy frame:    bookkeeping frame (to be removed in bug 625199)
      */
 
     bool isFunctionFrame() const {
         return !!(flags_ & FUNCTION);
     }
 
     bool isGlobalFrame() const {
         return !!(flags_ & GLOBAL);
     }
 
-    bool isDummyFrame() const {
-        return !!(flags_ & DUMMY);
-    }
-
-    bool isScriptFrame() const {
-        bool retval = !!(flags_ & (FUNCTION | GLOBAL));
-        JS_ASSERT(retval == !isDummyFrame());
-        return retval;
-    }
-
     /*
      * Eval frames
      *
      * As noted above, global and function frames may optionally be 'eval
      * frames'. Eval code shares its parent's arguments which means that the
      * arg-access members of StackFrame may not be used for eval frames.
      * Search for 'hasArgs' below for more details.
      *
      * A further sub-classification of eval frames is whether the frame was
      * pushed for an ES5 strict-mode eval().
      */
 
     bool isEvalFrame() const {
-        JS_ASSERT_IF(flags_ & EVAL, isScriptFrame());
         return flags_ & EVAL;
     }
 
     bool isEvalInFunction() const {
         return (flags_ & (EVAL | FUNCTION)) == (EVAL | FUNCTION);
     }
 
     bool isNonEvalFunctionFrame() const {
@@ -520,26 +503,26 @@ class StackFrame
 
     /*
      * Scope chain
      *
      * In theory, the scope chain would contain an object for every lexical
      * scope. However, only objects that are required for dynamic lookup are
      * actually created.
      *
-     * Given that a (non-dummy) StackFrame corresponds roughly to a ES5
-     * Execution Context (ES5 10.3), StackFrame::varObj corresponds to the
-     * VariableEnvironment component of a Exection Context. Intuitively, the
-     * variables object is where new bindings (variables and functions) are
-     * stored. One might expect that this is either the Call object or
-     * scopeChain.globalObj for function or global code, respectively, however
-     * the JSAPI allows calls of Execute to specify a variables object on the
-     * scope chain other than the call/global object. This allows embeddings to
-     * run multiple scripts under the same global, each time using a new
-     * variables object to collect and discard the script's global variables.
+     * Given that a StackFrame corresponds roughly to a ES5 Execution Context
+     * (ES5 10.3), StackFrame::varObj corresponds to the VariableEnvironment
+     * component of a Exection Context. Intuitively, the variables object is
+     * where new bindings (variables and functions) are stored. One might
+     * expect that this is either the Call object or scopeChain.globalObj for
+     * function or global code, respectively, however the JSAPI allows calls of
+     * Execute to specify a variables object on the scope chain other than the
+     * call/global object. This allows embeddings to run multiple scripts under
+     * the same global, each time using a new variables object to collect and
+     * discard the script's global variables.
      */
 
     inline HandleObject scopeChain() const;
 
     inline ScopeObject &aliasedVarScope(ScopeCoordinate sc) const;
     inline GlobalObject &global() const;
     inline CallObject &callObj() const;
     inline JSObject &varObj();
@@ -595,26 +578,21 @@ class StackFrame
      *
      * - Inlined frames have the same scope chain as the outer frame.
      * - Inlined frames have the same strictness as the outer frame.
      * - Inlined frames can only make calls to other JIT frames associated with
      *   the same VMFrame. Other calls force expansion of the inlined frames.
      */
 
     JSScript *script() const {
-        JS_ASSERT(isScriptFrame());
         return isFunctionFrame()
                ? isEvalFrame() ? u.evalScript : fun()->script()
                : exec.script;
     }
 
-    JSScript *maybeScript() const {
-        return isScriptFrame() ? script() : NULL;
-    }
-
     /*
      * Get the frame's current bytecode, assuming 'this' is in 'stack'. Beware,
      * as the name implies, pcQuadratic can lead to quadratic behavior in loops
      * such as:
      *
      *   for ( ...; fp; fp = fp->prev())
      *     ... fp->pcQuadratic(cx->stack);
      *
@@ -715,17 +693,16 @@ class StackFrame
     }
 
     const Value &calleev() const {
         JS_ASSERT(isFunctionFrame());
         return mutableCalleev();
     }
 
     const Value &maybeCalleev() const {
-        JS_ASSERT(isScriptFrame());
         Value &calleev = flags_ & (EVAL | GLOBAL)
                          ? ((Value *)this)[-2]
                          : formals()[-2];
         JS_ASSERT(calleev.isObjectOrNull());
         return calleev;
     }
 
     Value &mutableCalleev() const {
@@ -1157,24 +1134,16 @@ class FrameRegs
 
     void setToEndOfScript() {
         JSScript *script = fp()->script();
         sp = fp()->base();
         pc = script->code + script->length - JSOP_STOP_LENGTH;
         JS_ASSERT(*pc == JSOP_STOP);
     }
 
-    /* For pushDummyFrame: */
-    void initDummyFrame(StackFrame &fp) {
-        pc = NULL;
-        sp = fp.slots();
-        fp_ = &fp;
-        inlined_ = NULL;
-    }
-
     /* For expandInlineFrames: */
     void expandInline(StackFrame *innerfp, jsbytecode *innerpc) {
         pc = innerpc;
         fp_ = innerfp;
         inlined_ = NULL;
     }
 
 #ifdef JS_METHODJIT
@@ -1335,32 +1304,20 @@ class StackSpace
     static void staticAsserts() {
         JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
     }
 
     friend class AllFramesIter;
     friend class ContextStack;
     friend class StackFrame;
 
-    /*
-     * Except when changing compartment (see pushDummyFrame), the 'dest'
-     * parameter of ensureSpace is cx->compartment. Ideally, we'd just pass
-     * this directly (and introduce a helper that supplies cx->compartment when
-     * no 'dest' is given). For some compilers, this really hurts performance,
-     * so, instead, a trivially sinkable magic constant is used to indicate
-     * that dest should be cx->compartment.
-     */
-    static const size_t CX_COMPARTMENT = 0xc;
-
     inline bool ensureSpace(JSContext *cx, MaybeReportError report,
-                            Value *from, ptrdiff_t nvals,
-                            JSCompartment *dest = (JSCompartment *)CX_COMPARTMENT) const;
+                            Value *from, ptrdiff_t nvals) const;
     JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
-                                        Value *from, ptrdiff_t nvals,
-                                        JSCompartment *dest) const;
+                                        Value *from, ptrdiff_t nvals) const;
 
     StackSegment &findContainingSegment(const StackFrame *target) const;
 
     bool containsFast(StackFrame *fp) {
         return (Value *)fp >= base_ && (Value *)fp <= trustedEnd_;
     }
 
     void markAndClobberFrame(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc);
@@ -1452,18 +1409,17 @@ class ContextStack
 #else
     void assertSpaceInSync() const {}
 #endif
 
     /* Implementation details of push* public interface. */
     StackSegment *pushSegment(JSContext *cx);
     enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
     Value *ensureOnTop(JSContext *cx, MaybeReportError report, unsigned nvars,
-                       MaybeExtend extend, bool *pushedSeg,
-                       JSCompartment *dest = (JSCompartment *)StackSpace::CX_COMPARTMENT);
+                       MaybeExtend extend, bool *pushedSeg);
 
     inline StackFrame *
     getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
                  JSFunction *fun, JSScript *script, StackFrame::Flags *pflags) const;
 
     /* Make pop* functions private since only called by guard classes. */
     void popSegment();
     friend class InvokeArgsGuard;
@@ -1485,18 +1441,17 @@ class ContextStack
      * A context's stack is "empty" if there are no scripts or natives
      * executing. Note that JS_SaveFrameChain does not factor into this definition.
      */
     bool empty() const                { return !seg_; }
 
     /*
      * Return whether there has been at least one frame pushed since the most
      * recent call to JS_SaveFrameChain. Note that natives do not have frames
-     * and dummy frames are frames that do not represent script execution hence
-     * this query has little semantic meaning past "you can call fp()".
+     * hence this query has little semantic meaning past "you can call fp()".
      */
     inline bool hasfp() const { return seg_ && seg_->maybeRegs(); }
 
     /*
      * Return the spindex value for 'vp' which can be used to call
      * DecompileValueGenerator. (The spindex is either the negative offset of
      * 'vp' from 'sp', if 'vp' points to a value in the innermost scripted
      * stack frame, otherwise it is JSDVG_SEARCH_STACK.)
@@ -1543,27 +1498,16 @@ class ContextStack
      * Called by SendToGenerator to resume a yielded generator. In addition to
      * pushing a frame onto the VM stack, this function copies over the
      * floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
      * will copy the frame back to the floating frame.
      */
     bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
 
     /*
-     * When changing the compartment of a cx, it is necessary to immediately
-     * change the scope chain to a global in the right compartment since any
-     * amount of general VM code can run before the first scripted frame is
-     * pushed (if at all). This is currently and hackily accomplished by
-     * pushing a "dummy frame" with the correct scope chain. On success, this
-     * function will change the compartment to 'scopeChain.compartment()' and
-     * push a dummy frame for 'scopeChain'. On failure, nothing is changed.
-     */
-    bool pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg);
-
-    /*
      * An "inline frame" may only be pushed from within the top, active
      * segment. This is the case for calls made inside mjit code and Interpret.
      * The 'stackLimit' overload updates 'stackLimit' if it changes.
      */
     bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                          JSFunction &callee, JSScript *script,
                          InitialFrameFlags initial);
     bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
@@ -1633,19 +1577,16 @@ class FrameGuard
 };
 
 class InvokeFrameGuard : public FrameGuard
 {};
 
 class ExecuteFrameGuard : public FrameGuard
 {};
 
-class DummyFrameGuard : public FrameGuard
-{};
-
 class GeneratorFrameGuard : public FrameGuard
 {
     friend class ContextStack;
     JSGenerator *gen_;
     Value *stackvp_;
   public:
     ~GeneratorFrameGuard() { if (pushed()) stack_->popGeneratorFrame(*this); }
 };
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -426,17 +426,17 @@ JSFlatString::isIndexSlow(uint32_t *inde
 
 #define R TO_SMALL_CHAR
 const StaticStrings::SmallChar StaticStrings::toSmallChar[] = { R7(0) };
 #undef R
 
 bool
 StaticStrings::init(JSContext *cx)
 {
-    SwitchToCompartment sc(cx, cx->runtime->atomsCompartment);
+    AutoEnterAtomsCompartment ac(cx);
 
     for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
         jschar buffer[] = { jschar(i), '\0' };
         JSFixedString *s = js_NewStringCopyN(cx, buffer, 1);
         if (!s)
             return false;
         unitStaticTable[i] = s->morphAtomizedStringIntoAtom();
     }
--- a/js/xpconnect/src/XPCStack.cpp
+++ b/js/xpconnect/src/XPCStack.cpp
@@ -98,52 +98,48 @@ XPCJSStackFrame::CreateStack(JSContext* 
                              XPCJSStackFrame** stack)
 {
     static const unsigned MAX_FRAMES = 100;
     unsigned numFrames = 0;
 
     nsRefPtr<XPCJSStackFrame> first = new XPCJSStackFrame();
     nsRefPtr<XPCJSStackFrame> self = first;
     while (fp && self) {
-        if (!JS_IsScriptFrame(cx, fp)) {
-            self->mLanguage = nsIProgrammingLanguage::CPLUSPLUS;
-        } else {
-            self->mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
-            JSScript* script = JS_GetFrameScript(cx, fp);
-            jsbytecode* pc = JS_GetFramePC(cx, fp);
-            if (script && pc) {
-                JS::AutoEnterFrameCompartment ac;
-                if (ac.enter(cx, fp)) {
-                    const char* filename = JS_GetScriptFilename(cx, script);
-                    if (filename) {
-                        self->mFilename = (char*)
-                            nsMemory::Clone(filename,
-                                            sizeof(char)*(strlen(filename)+1));
-                    }
+        self->mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
+        JSScript* script = JS_GetFrameScript(cx, fp);
+        jsbytecode* pc = JS_GetFramePC(cx, fp);
+        if (script && pc) {
+            JS::AutoEnterFrameCompartment ac;
+            if (ac.enter(cx, fp)) {
+                const char* filename = JS_GetScriptFilename(cx, script);
+                if (filename) {
+                    self->mFilename = (char*)
+                        nsMemory::Clone(filename,
+                                        sizeof(char)*(strlen(filename)+1));
+                }
 
-                    self->mLineno = (int32_t) JS_PCToLineNumber(cx, script, pc);
+                self->mLineno = (int32_t) JS_PCToLineNumber(cx, script, pc);
 
-                    JSFunction* fun = JS_GetFrameFunction(cx, fp);
-                    if (fun) {
-                        JSString *funid = JS_GetFunctionId(fun);
-                        if (funid) {
-                            size_t length = JS_GetStringEncodingLength(cx, funid);
-                            if (length != size_t(-1)) {
-                                self->mFunname = static_cast<char *>(nsMemory::Alloc(length + 1));
-                                if (self->mFunname) {
-                                    JS_EncodeStringToBuffer(funid, self->mFunname, length);
-                                    self->mFunname[length] = '\0';
-                                }
+                JSFunction* fun = JS_GetFrameFunction(cx, fp);
+                if (fun) {
+                    JSString *funid = JS_GetFunctionId(fun);
+                    if (funid) {
+                        size_t length = JS_GetStringEncodingLength(cx, funid);
+                        if (length != size_t(-1)) {
+                            self->mFunname = static_cast<char *>(nsMemory::Alloc(length + 1));
+                            if (self->mFunname) {
+                                JS_EncodeStringToBuffer(funid, self->mFunname, length);
+                                self->mFunname[length] = '\0';
                             }
                         }
                     }
                 }
-            } else {
-                self->mLanguage = nsIProgrammingLanguage::CPLUSPLUS;
             }
+        } else {
+            self->mLanguage = nsIProgrammingLanguage::CPLUSPLUS;
         }
 
         if (++numFrames > MAX_FRAMES) {
             fp = NULL;
         } else if (JS_FrameIterator(cx, &fp)) {
             XPCJSStackFrame* frame = new XPCJSStackFrame();
             self->mCaller = frame;
             self = frame;
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -239,24 +239,24 @@ AccessCheck::isSystemOnlyAccessPermitted
 
     JSStackFrame *fp;
     nsIPrincipal *principal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp);
     if (!principal) {
         return false;
     }
 
     JSScript *script = nullptr;
-    if (!fp) {
+    if (fp) {
+      script = JS_GetFrameScript(cx, fp);
+    } else {
         if (!JS_DescribeScriptedCaller(cx, &script, nullptr)) {
             // No code at all is running. So we must be arriving here as the result
             // of C++ code asking us to do something. Allow access.
             return true;
         }
-    } else if (JS_IsScriptFrame(cx, fp)) {
-        script = JS_GetFrameScript(cx, fp);
     }
 
     bool privileged;
     if (NS_SUCCEEDED(ssm->IsSystemPrincipal(principal, &privileged)) &&
         privileged) {
         return true;
     }