Backed out changeset d58e45442c87
authorRobert Sayre <sayrer@gmail.com>
Thu, 06 Jan 2011 21:39:25 -0500
changeset 60194 bbb64eb86aa25a27acf9638aeda1648049acc0c8
parent 60178 d58e45442c87bdae66439ebb7a726dd2dd5ea70c
child 60195 b20f4eb4a59c2ce383071086c6ac73c09b985661
push id17881
push usercleary@mozilla.com
push dateFri, 07 Jan 2011 19:57:21 +0000
treeherdermozilla-central@54576be62860 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b9pre
backs outd58e45442c87bdae66439ebb7a726dd2dd5ea70c
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset d58e45442c87
dom/base/nsJSEnvironment.cpp
js/src/jsapi.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/jsgc.cpp
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jswrapper.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1125,17 +1125,17 @@ nsJSContext::DOMOperationCallback(JSCont
     switch(cx->debugHooks->debuggerHandler(cx, script, ::JS_GetFramePC(cx, fp),
                                            &rval,
                                            cx->debugHooks->
                                            debuggerHandlerData)) {
       case JSTRAP_RETURN:
         JS_SetFrameReturnValue(cx, fp, rval);
         return JS_TRUE;
       case JSTRAP_ERROR:
-        JS_ClearPendingException(cx);
+        cx->throwing = JS_FALSE;
         return JS_FALSE;
       case JSTRAP_THROW:
         JS_SetPendingException(cx, rval);
         return JS_FALSE;
       case JSTRAP_CONTINUE:
       default:
         return JS_TRUE;
     }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5168,18 +5168,17 @@ JS_IsRunning(JSContext *cx)
     /*
      * The use of cx->fp below is safe. Rationale: Here we don't care if the
      * interpreter state is stale. We just want to know if there *is* any
      * interpreter state.
      */
     VOUCH_DOES_NOT_REQUIRE_STACK();
 
 #ifdef JS_TRACER
-    JS_ASSERT_IF(cx->compartment &&
-                 JS_TRACE_MONITOR(cx).tracecx == cx, cx->hasfp());
+    JS_ASSERT_IF(JS_TRACE_MONITOR(cx).tracecx == cx, cx->hasfp());
 #endif
     JSStackFrame *fp = cx->maybefp();
     while (fp && fp->isDummyFrame())
         fp = fp->prev();
     return fp != NULL;
 }
 
 JS_PUBLIC_API(JSStackFrame *)
@@ -5842,42 +5841,42 @@ JS_GetLocaleCallbacks(JSContext *cx)
     return cx->localeCallbacks;
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(JSBool)
 JS_IsExceptionPending(JSContext *cx)
 {
-    return (JSBool) cx->isExceptionPending();
+    return (JSBool) cx->throwing;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPendingException(JSContext *cx, jsval *vp)
 {
     CHECK_REQUEST(cx);
-    if (!cx->isExceptionPending())
+    if (!cx->throwing)
         return JS_FALSE;
-    Valueify(*vp) = cx->getPendingException();
-    assertSameCompartment(cx, *vp);
+    Valueify(*vp) = cx->exception;
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(void)
 JS_SetPendingException(JSContext *cx, jsval v)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
-    cx->setPendingException(Valueify(v));
+    SetPendingException(cx, Valueify(v));
 }
 
 JS_PUBLIC_API(void)
 JS_ClearPendingException(JSContext *cx)
 {
-    cx->clearPendingException();
+    cx->throwing = JS_FALSE;
+    cx->exception.setUndefined();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ReportPendingException(JSContext *cx)
 {
     JSBool ok;
     JSPackedBool save;
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1352,17 +1352,17 @@ js_ReportOutOfMemory(JSContext *cx)
     PopulateReportBlame(cx, &report);
 
     /*
      * If debugErrorHook is present then we give it a chance to veto sending
      * the error on to the regular ErrorReporter. We also clear a pending
      * exception if any now so the hooks can replace the out-of-memory error
      * by a script-catchable exception.
      */
-    cx->clearPendingException();
+    cx->throwing = JS_FALSE;
     if (onError) {
         JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
         if (hook &&
             !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
             onError = NULL;
         }
     }
 
@@ -2000,47 +2000,37 @@ JSContext::JSContext(JSRuntime *rt)
 void
 JSContext::resetCompartment()
 {
     JSObject *scopeobj;
     if (hasfp()) {
         scopeobj = &fp()->scopeChain();
     } else {
         scopeobj = globalObject;
-        if (!scopeobj)
-            goto error;
+        if (!scopeobj) {
+            compartment = runtime->defaultCompartment;
+            return;
+        }
 
         /*
          * Innerize. Assert, but check anyway, that this succeeds. (It
          * can only fail due to bugs in the engine or embedding.)
          */
         OBJ_TO_INNER_OBJECT(this, scopeobj);
-        if (!scopeobj)
-            goto error;
+        if (!scopeobj) {
+            /*
+             * Bug. Return NULL, not defaultCompartment, to crash rather
+             * than open a security hole.
+             */
+            JS_ASSERT(0);
+            compartment = NULL;
+            return;
+        }
     }
-
-    compartment = scopeobj->compartment();
-
-    /*
-     * If wrapException fails, it overrides this->exception and
-     * reports OOM. The upshot is that we silently turn the exception
-     * into an uncatchable OOM error. A bit surprising, but the
-     * caller is just going to return false either way.
-     */
-    if (isExceptionPending())
-        (void) compartment->wrapException(this);
-    return;
-
-error:
-
-    /*
-     * If we try to use the context without a selected compartment,
-     * we will crash.
-     */
-    compartment = NULL;
+    compartment = scopeobj->getCompartment();
 }
 
 void
 JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs)
 {
     JS_ASSERT(regs != &newregs);
     if (hasActiveSegment())
         currentSegment->suspend(regs);
@@ -2294,9 +2284,16 @@ JS_FORCES_STACK JS_FRIEND_API(void)
 LeaveTrace(JSContext *cx)
 {
 #ifdef JS_TRACER
     if (JS_ON_TRACE(cx))
         DeepBail(cx);
 #endif
 }
 
+void
+SetPendingException(JSContext *cx, const Value &v)
+{
+    cx->throwing = JS_TRUE;
+    cx->exception = v;
+}
+
 } /* namespace js */
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -883,17 +883,17 @@ private:
 } /* namespace js */
 
 /*
  * N.B. JS_ON_TRACE(cx) is true if JIT code is on the stack in the current
  * thread, regardless of whether cx is the context in which that trace is
  * executing.  cx must be a context on the current thread.
  */
 #ifdef JS_TRACER
-# define JS_ON_TRACE(cx)            (cx->compartment && JS_TRACE_MONITOR(cx).ontrace())
+# define JS_ON_TRACE(cx)            (JS_TRACE_MONITOR(cx).ontrace())
 #else
 # define JS_ON_TRACE(cx)            false
 #endif
 
 #ifdef DEBUG
 # define FUNCTION_KIND_METER_LIST(_)                                          \
                         _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar),  \
                         _(display), _(flat), _(setupvar), _(badfunarg),       \
@@ -1695,20 +1695,16 @@ struct JSContext
     JSCList             link;
 
   private:
     /* See JSContext::findVersion. */
     JSVersion           defaultVersion;      /* script compilation version */
     JSVersion           versionOverride;     /* supercedes defaultVersion when valid */
     bool                hasVersionOverride;
 
-    /* Exception state -- the exception member is a GC root by definition. */
-    JSBool              throwing;           /* is there a pending exception? */
-    js::Value           exception;          /* most-recently-thrown exception */
-
   public:
     /* Per-context options. */
     uint32              options;            /* see jsapi.h for JSOPTION_* */
 
     /* Locale specific callbacks for string conversion. */
     JSLocaleCallbacks   *localeCallbacks;
 
     /*
@@ -1720,16 +1716,20 @@ struct JSContext
     JSDHashTable        *resolvingTable;
 
     /*
      * True if generating an error, to prevent runaway recursion.
      * NB: generatingError packs with throwing below.
      */
     JSPackedBool        generatingError;
 
+    /* Exception state -- the exception member is a GC root by definition. */
+    JSBool              throwing;           /* is there a pending exception? */
+    js::Value           exception;          /* most-recently-thrown exception */
+
     /* Limit pointer for checking native stack consumption during recursion. */
     jsuword             stackLimit;
 
     /* Quota on the size of arenas used to compile and execute scripts. */
     size_t              scriptStackQuota;
 
     /* Data shared by threads in an address space. */
     JSRuntime *const    runtime;
@@ -2163,32 +2163,16 @@ struct JSContext
     void assertValidStackDepth(uintN depth) {
         JS_ASSERT(0 <= regs->sp - regs->fp->base());
         JS_ASSERT(depth <= uintptr_t(regs->sp - regs->fp->base()));
     }
 #else
     void assertValidStackDepth(uintN /*depth*/) {}
 #endif
 
-    bool isExceptionPending() {
-        return throwing;
-    }
-
-    js::Value getPendingException() {
-        JS_ASSERT(throwing);
-        return exception;
-    }
-
-    void setPendingException(js::Value v);
-
-    void clearPendingException() {
-        this->throwing = false;
-        this->exception.setUndefined();
-    }
-
   private:
     /*
      * The allocation code calls the function to indicate either OOM failure
      * when p is null or that a memory pressure counter has reached some
      * threshold when p is not null. The function takes the pointer and not
      * a boolean flag to minimize the amount of code in its inlined callers.
      */
     JS_FRIEND_API(void) checkMallocGCPressure(void *p);
@@ -3158,16 +3142,19 @@ js_GetScriptedCaller(JSContext *cx, JSSt
 extern jsbytecode*
 js_GetCurrentBytecodePC(JSContext* cx);
 
 extern bool
 js_CurrentPCIsInImacro(JSContext *cx);
 
 namespace js {
 
+extern void
+SetPendingException(JSContext *cx, const Value &v);
+
 class RegExpStatics;
 
 extern JS_FORCES_STACK JS_FRIEND_API(void)
 LeaveTrace(JSContext *cx);
 
 } /* namespace js */
 
 /*
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -681,23 +681,23 @@ assertSameCompartment(JSContext *cx, T1 
 
 #undef START_ASSERT_SAME_COMPARTMENT
 
 STATIC_PRECONDITION_ASSUME(ubound(vp) >= argc + 2)
 JS_ALWAYS_INLINE bool
 CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp)
 {
 #ifdef DEBUG
-    JSBool alreadyThrowing = cx->isExceptionPending();
+    JSBool alreadyThrowing = cx->throwing;
 #endif
     assertSameCompartment(cx, ValueArray(vp, argc + 2));
     JSBool ok = native(cx, argc, vp);
     if (ok) {
         assertSameCompartment(cx, vp[0]);
-        JS_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
+        JS_ASSERT_IF(!alreadyThrowing, !cx->throwing);
     }
     return ok;
 }
 
 STATIC_PRECONDITION(ubound(vp) >= argc + 2)
 JS_ALWAYS_INLINE bool
 CallJSNativeConstructor(JSContext *cx, js::Native native, uintN argc, js::Value *vp)
 {
@@ -795,16 +795,9 @@ CanLeaveTrace(JSContext *cx)
     return cx->bailExit != NULL;
 #else
     return JS_FALSE;
 #endif
 }
 
 }  /* namespace js */
 
-inline void
-JSContext::setPendingException(js::Value v) {
-    this->throwing = true;
-    this->exception = v;
-    assertSameCompartment(this, v);
-}
-
 #endif /* jscntxtinlines_h___ */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -353,21 +353,24 @@ JSCompartment::wrap(JSContext *cx, AutoI
     return true;
 }
 
 bool
 JSCompartment::wrapException(JSContext *cx)
 {
     JS_ASSERT(cx->compartment == this);
 
-    if (cx->isExceptionPending()) {
-        Value v = cx->getPendingException();
-        cx->clearPendingException();
-        if (wrap(cx, &v))
-            cx->setPendingException(v);
+    if (cx->throwing) {
+        AutoValueRooter tvr(cx, cx->exception);
+        cx->throwing = false;
+        cx->exception.setNull();
+        if (wrap(cx, tvr.addr())) {
+            cx->throwing = true;
+            cx->exception = tvr.value();
+        }
         return false;
     }
     return true;
 }
 
 #if defined JS_METHODJIT && defined JS_MONOIC
 /*
  * Check if the pool containing the code for jit should be destroyed, per the
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -372,17 +372,16 @@ class PreserveCompartment {
   protected:
     JSContext *cx;
   private:
     JSCompartment *oldCompartment;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
   public:
      PreserveCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx) {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
-        JS_ASSERT(!cx->isExceptionPending());
         oldCompartment = cx->compartment;
     }
 
     ~PreserveCompartment() {
         cx->compartment = oldCompartment;
     }
 };
 
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1540,36 +1540,35 @@ JS_PropertyIterator(JSObject *obj, JSSco
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
                    JSPropertyDesc *pd)
 {
     assertSameCompartment(cx, obj);
     Shape *shape = (Shape *) sprop;
     pd->id = IdToJsval(shape->id);
 
-    JSBool wasThrowing = cx->isExceptionPending();
-    Value lastException = UndefinedValue();
-    if (wasThrowing)
-        lastException = cx->getPendingException();
-    cx->clearPendingException();
+    JSBool wasThrowing = cx->throwing;
+    AutoValueRooter lastException(cx, cx->exception);
+    cx->throwing = JS_FALSE;
 
     if (!js_GetProperty(cx, obj, shape->id, Valueify(&pd->value))) {
-        if (!cx->isExceptionPending()) {
+        if (!cx->throwing) {
             pd->flags = JSPD_ERROR;
             pd->value = JSVAL_VOID;
         } else {
             pd->flags = JSPD_EXCEPTION;
-            pd->value = Jsvalify(cx->getPendingException());
+            pd->value = Jsvalify(cx->exception);
         }
     } else {
         pd->flags = 0;
     }
 
+    cx->throwing = wasThrowing;
     if (wasThrowing)
-        cx->setPendingException(lastException);
+        cx->exception = lastException.value();
 
     pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
               |  (!shape->writable()  ? JSPD_READONLY  : 0)
               |  (!shape->configurable() ? JSPD_PERMANENT : 0);
     pd->spare = 0;
     if (shape->getter() == GetCallArg) {
         pd->slot = shape->shortid;
         pd->flags |= JSPD_ARGUMENT;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1577,18 +1577,22 @@ namespace js {
 void
 MarkContext(JSTracer *trc, JSContext *acx)
 {
     /* Stack frames and slots are traced by StackSpace::mark. */
 
     /* Mark other roots-by-definition in acx. */
     if (acx->globalObject && !JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL))
         MarkObject(trc, *acx->globalObject, "global object");
-    if (acx->isExceptionPending())
-        MarkValue(trc, acx->getPendingException(), "exception");
+    if (acx->throwing) {
+        MarkValue(trc, acx->exception, "exception");
+    } else {
+        /* Avoid keeping GC-ed junk stored in JSContext.exception. */
+        acx->exception.setNull();
+    }
 
     for (js::AutoGCRooter *gcr = acx->autoGCRooters; gcr; gcr = gcr->down)
         gcr->trace(trc);
 
     if (acx->sharpObjectMap.depth > 0)
         js_TraceSharpMap(trc, &acx->sharpObjectMap);
 
     MarkValue(trc, acx->iterValue, "iterValue");
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2424,33 +2424,33 @@ Interpret(JSContext *cx, JSStackFrame *e
 #endif
 
 #define RESTORE_INTERP_VARS()                                                 \
     JS_BEGIN_MACRO                                                            \
         script = regs.fp->script();                                           \
         argv = regs.fp->maybeFormalArgs();                                    \
         atoms = FrameAtomBase(cx, regs.fp);                                   \
         JS_ASSERT(cx->regs == &regs);                                         \
-        if (cx->isExceptionPending())                                         \
+        if (cx->throwing)                                                     \
             goto error;                                                       \
     JS_END_MACRO
 
 #define MONITOR_BRANCH()                                                      \
     JS_BEGIN_MACRO                                                            \
         if (TRACING_ENABLED(cx)) {                                            \
             MonitorResult r = MonitorLoopEdge(cx, inlineCallCount);           \
             if (r == MONITOR_RECORDING) {                                     \
                 JS_ASSERT(TRACE_RECORDER(cx));                                \
                 JS_ASSERT(!TRACE_PROFILER(cx));                               \
                 MONITOR_BRANCH_TRACEVIS;                                      \
                 ENABLE_INTERRUPTS();                                          \
                 CLEAR_LEAVE_ON_TRACE_POINT();                                 \
             }                                                                 \
             RESTORE_INTERP_VARS();                                            \
-            JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR);       \
+            JS_ASSERT_IF(cx->throwing, r == MONITOR_ERROR);                   \
             if (r == MONITOR_ERROR)                                           \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 #else /* !JS_TRACER */
 
 #define MONITOR_BRANCH() ((void) 0)
@@ -2566,19 +2566,19 @@ Interpret(JSContext *cx, JSStackFrame *e
 #if JS_HAS_GENERATORS
     if (JS_UNLIKELY(regs.fp->isGeneratorFrame())) {
         JS_ASSERT(interpGuard.prevContextRegs == &cx->generatorFor(regs.fp)->regs);
         JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
         JS_ASSERT((size_t) (regs.sp - regs.fp->base()) <= StackDepth(script));
 
         /*
          * To support generator_throw and to catch ignored exceptions,
-         * fail if cx->isExceptionPending() is true.
+         * fail if cx->throwing is set.
          */
-        if (cx->isExceptionPending())
+        if (cx->throwing)
             goto error;
     }
 #endif
 
 #ifdef JS_TRACER
     /*
      * The method JIT may have already initiated a recording, in which case
      * there should already be a valid recorder. Otherwise...
@@ -2682,17 +2682,18 @@ Interpret(JSContext *cx, JSStackFrame *e
                 goto error;
               case JSTRAP_CONTINUE:
                 break;
               case JSTRAP_RETURN:
                 regs.fp->setReturnValue(rval);
                 interpReturnOK = JS_TRUE;
                 goto forced_return;
               case JSTRAP_THROW:
-                cx->setPendingException(rval);
+                cx->throwing = JS_TRUE;
+                cx->exception = rval;
                 goto error;
               default:;
             }
             moreInterrupts = true;
         }
 
 #ifdef JS_TRACER
 #ifdef JS_METHODJIT
@@ -2710,17 +2711,17 @@ Interpret(JSContext *cx, JSStackFrame *e
                     moreInterrupts = true;
                     break;
             }
         }
 #endif
         if (TraceRecorder* tr = TRACE_RECORDER(cx)) {
             JS_ASSERT(!TRACE_PROFILER(cx));
             AbortableRecordingStatus status = tr->monitorRecording(op);
-            JS_ASSERT_IF(cx->isExceptionPending(), status == ARECORD_ERROR);
+            JS_ASSERT_IF(cx->throwing, status == ARECORD_ERROR);
 
             if (interpMode != JSINTERP_NORMAL) {
                 JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT);
                 switch (status) {
                   case ARECORD_IMACRO_ABORTED:
                   case ARECORD_ABORTED:
                   case ARECORD_COMPLETED:
                   case ARECORD_STOP:
@@ -5198,17 +5199,18 @@ BEGIN_CASE(JSOP_TRAP)
     switch (status) {
       case JSTRAP_ERROR:
         goto error;
       case JSTRAP_RETURN:
         regs.fp->setReturnValue(rval);
         interpReturnOK = JS_TRUE;
         goto forced_return;
       case JSTRAP_THROW:
-        cx->setPendingException(rval);
+        cx->throwing = JS_TRUE;
+        cx->exception = rval;
         goto error;
       default:
         break;
     }
     JS_ASSERT(status == JSTRAP_CONTINUE);
     CHECK_INTERRUPT_HANDLER();
     JS_ASSERT(rval.isInt32());
     op = (JSOp) rval.toInt32();
@@ -6224,60 +6226,57 @@ BEGIN_CASE(JSOP_RETSUB)
     JS_ASSERT(lval.isBoolean());
     if (lval.toBoolean()) {
         /*
          * Exception was pending during finally, throw it *before* we adjust
          * pc, because pc indexes into script->trynotes.  This turns out not to
          * be necessary, but it seems clearer.  And it points out a FIXME:
          * 350509, due to Igor Bukanov.
          */
-        cx->setPendingException(rval);
+        cx->throwing = JS_TRUE;
+        cx->exception = rval;
         goto error;
     }
     JS_ASSERT(rval.isInt32());
     len = rval.toInt32();
     regs.pc = script->main;
 END_VARLEN_CASE
 }
 
 BEGIN_CASE(JSOP_EXCEPTION)
-    PUSH_COPY(cx->getPendingException());
-    cx->clearPendingException();
+    JS_ASSERT(cx->throwing);
+    PUSH_COPY(cx->exception);
+    cx->throwing = JS_FALSE;
 #if defined(JS_TRACER) && defined(JS_METHODJIT)
     if (interpMode == JSINTERP_PROFILE) {
         leaveOnSafePoint = true;
         LEAVE_ON_SAFE_POINT();
     }
 #endif
     CHECK_BRANCH();
 END_CASE(JSOP_EXCEPTION)
 
 BEGIN_CASE(JSOP_FINALLY)
     CHECK_BRANCH();
 END_CASE(JSOP_FINALLY)
 
 BEGIN_CASE(JSOP_THROWING)
-{
-    JS_ASSERT(!cx->isExceptionPending());
-    Value v;
-    POP_COPY_TO(v);
-    cx->setPendingException(v);
-}
+    JS_ASSERT(!cx->throwing);
+    cx->throwing = JS_TRUE;
+    POP_COPY_TO(cx->exception);
 END_CASE(JSOP_THROWING)
 
 BEGIN_CASE(JSOP_THROW)
-{
-    JS_ASSERT(!cx->isExceptionPending());
+    JS_ASSERT(!cx->throwing);
     CHECK_BRANCH();
-    Value v;
-    POP_COPY_TO(v);
-    cx->setPendingException(v);
+    cx->throwing = JS_TRUE;
+    POP_COPY_TO(cx->exception);
     /* let the code at error try to catch the exception. */
     goto error;
-}
+
 BEGIN_CASE(JSOP_SETLOCALPOP)
 {
     /*
      * The stack must have a block with at least one local slot below the
      * exception object.
      */
     JS_ASSERT((size_t) (regs.sp - regs.fp->base()) >= 2);
     uint32 slot = GET_UINT16(regs.pc);
@@ -6343,17 +6342,18 @@ BEGIN_CASE(JSOP_DEBUGGER)
             goto error;
         case JSTRAP_CONTINUE:
             break;
         case JSTRAP_RETURN:
             regs.fp->setReturnValue(rval);
             interpReturnOK = JS_TRUE;
             goto forced_return;
         case JSTRAP_THROW:
-            cx->setPendingException(rval);
+            cx->throwing = JS_TRUE;
+            cx->exception = rval;
             goto error;
         default:;
         }
         CHECK_INTERRUPT_HANDLER();
     }
 }
 END_CASE(JSOP_DEBUGGER)
 #endif /* JS_HAS_DEBUGGER_KEYWORD */
@@ -6715,31 +6715,31 @@ BEGIN_CASE(JSOP_LEAVEBLOCK)
         JS_ASSERT(regs.fp->base() + blockDepth == regs.sp);
     }
 }
 END_CASE(JSOP_LEAVEBLOCK)
 
 #if JS_HAS_GENERATORS
 BEGIN_CASE(JSOP_GENERATOR)
 {
-    JS_ASSERT(!cx->isExceptionPending());
+    JS_ASSERT(!cx->throwing);
     regs.pc += JSOP_GENERATOR_LENGTH;
     JSObject *obj = js_NewGenerator(cx);
     if (!obj)
         goto error;
     JS_ASSERT(!regs.fp->hasCallObj() && !regs.fp->hasArgsObj());
     regs.fp->setReturnValue(ObjectValue(*obj));
     interpReturnOK = true;
     if (entryFrame != regs.fp)
         goto inline_return;
     goto exit;
 }
 
 BEGIN_CASE(JSOP_YIELD)
-    JS_ASSERT(!cx->isExceptionPending());
+    JS_ASSERT(!cx->throwing);
     JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
     if (cx->generatorFor(regs.fp)->state == JSGEN_CLOSING) {
         js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
                             JSDVG_SEARCH_STACK, argv[-2], NULL);
         goto error;
     }
     regs.fp->setReturnValue(regs.sp[-1]);
     regs.fp->setYielding();
@@ -6825,17 +6825,17 @@ END_CASE(JSOP_ARRAYPUSH)
 #if !JS_THREADED_INTERP
         } /* switch (op) */
     } /* for (;;) */
 #endif /* !JS_THREADED_INTERP */
 
   error:
     JS_ASSERT(cx->regs == &regs);
 #ifdef JS_TRACER
-    if (regs.fp->hasImacropc() && cx->isExceptionPending()) {
+    if (regs.fp->hasImacropc() && cx->throwing) {
         // Handle exceptions as if they came from the imacro-calling pc.
         regs.pc = regs.fp->imacropc();
         regs.fp->clearImacropc();
     }
 #endif
 
     JS_ASSERT(size_t((regs.fp->hasImacropc() ? regs.fp->imacropc() : regs.pc) - script->code) <
               script->length);
@@ -6849,17 +6849,17 @@ END_CASE(JSOP_ARRAYPUSH)
     if (TRACE_RECORDER(cx))
         AbortRecording(cx, "error or exception while recording");
 # ifdef JS_METHODJIT
     if (TRACE_PROFILER(cx))
         AbortProfiling(cx);
 # endif
 #endif
 
-    if (!cx->isExceptionPending()) {
+    if (!cx->throwing) {
         /* This is an error, not a catchable exception, quit the frame ASAP. */
         interpReturnOK = JS_FALSE;
     } else {
         JSThrowHook handler;
         JSTryNote *tn, *tnlimit;
         uint32 offset;
 
         /* Restore atoms local in case we will resume. */
@@ -6867,25 +6867,25 @@ END_CASE(JSOP_ARRAYPUSH)
 
         /* Call debugger throw hook if set. */
         handler = cx->debugHooks->throwHook;
         if (handler) {
             Value rval;
             switch (handler(cx, script, regs.pc, Jsvalify(&rval),
                             cx->debugHooks->throwHookData)) {
               case JSTRAP_ERROR:
-                cx->clearPendingException();
+                cx->throwing = JS_FALSE;
                 goto error;
               case JSTRAP_RETURN:
-                cx->clearPendingException();
+                cx->throwing = JS_FALSE;
                 regs.fp->setReturnValue(rval);
                 interpReturnOK = JS_TRUE;
                 goto forced_return;
               case JSTRAP_THROW:
-                cx->setPendingException(rval);
+                cx->exception = rval;
               case JSTRAP_CONTINUE:
               default:;
             }
             CHECK_INTERRUPT_HANDLER();
         }
 
         /*
          * Look for a try block in script that can catch this exception.
@@ -6938,78 +6938,79 @@ END_CASE(JSOP_ARRAYPUSH)
                  */
                 goto error;
             }
 
             switch (tn->kind) {
               case JSTRY_CATCH:
 #if JS_HAS_GENERATORS
                 /* Catch cannot intercept the closing of a generator. */
-                  if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
+                if (JS_UNLIKELY(cx->exception.isMagic(JS_GENERATOR_CLOSING)))
                     break;
 #endif
 
                 /*
-                 * Don't clear exceptions to save cx->exception from GC
+                 * Don't clear cx->throwing to save cx->exception from GC
                  * until it is pushed to the stack via [exception] in the
                  * catch block.
                  */
                 len = 0;
                 DO_NEXT_OP(len);
 
               case JSTRY_FINALLY:
                 /*
                  * Push (true, exception) pair for finally to indicate that
                  * [retsub] should rethrow the exception.
                  */
                 PUSH_BOOLEAN(true);
-                PUSH_COPY(cx->getPendingException());
-                cx->clearPendingException();
+                PUSH_COPY(cx->exception);
+                cx->throwing = JS_FALSE;
                 len = 0;
                 DO_NEXT_OP(len);
 
               case JSTRY_ITER: {
                 /* This is similar to JSOP_ENDITER in the interpreter loop. */
                 JS_ASSERT(js_GetOpcode(cx, regs.fp->script(), regs.pc) == JSOP_ENDITER);
-                Value v = cx->getPendingException();
-                cx->clearPendingException();
+                AutoValueRooter tvr(cx, cx->exception);
+                cx->throwing = false;
                 ok = js_CloseIterator(cx, &regs.sp[-1].toObject());
                 regs.sp -= 1;
                 if (!ok)
                     goto error;
-                cx->setPendingException(v);
+                cx->throwing = true;
+                cx->exception = tvr.value();
               }
            }
         } while (++tn != tnlimit);
 
       no_catch:
         /*
          * Propagate the exception or error to the caller unless the exception
          * is an asynchronous return from a generator.
          */
         interpReturnOK = JS_FALSE;
 #if JS_HAS_GENERATORS
-        if (JS_UNLIKELY(cx->isExceptionPending() &&
-                        cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) {
-            cx->clearPendingException();
+        if (JS_UNLIKELY(cx->throwing &&
+                        cx->exception.isMagic(JS_GENERATOR_CLOSING))) {
+            cx->throwing = JS_FALSE;
             interpReturnOK = JS_TRUE;
             regs.fp->clearReturnValue();
         }
 #endif
     }
 
   forced_return:
     /*
      * Unwind the scope making sure that interpReturnOK stays false even when
      * js_UnwindScope returns true.
      *
      * When a trap handler returns JSTRAP_RETURN, we jump here with
      * interpReturnOK set to true bypassing any finally blocks.
      */
-    interpReturnOK &= js_UnwindScope(cx, 0, interpReturnOK || cx->isExceptionPending());
+    interpReturnOK &= js_UnwindScope(cx, 0, interpReturnOK || cx->throwing);
     JS_ASSERT(regs.sp == regs.fp->base());
 
 #ifdef DEBUG
     cx->logPrevPc = NULL;
 #endif
 
     if (entryFrame != regs.fp)
         goto inline_return;
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -762,17 +762,17 @@ Iterator(JSContext *cx, uintN argc, Valu
 
 JSBool
 js_ThrowStopIteration(JSContext *cx)
 {
     Value v;
 
     JS_ASSERT(!JS_IsExceptionPending(cx));
     if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
-        cx->setPendingException(v);
+        SetPendingException(cx, v);
     return JS_FALSE;
 }
 
 static JSBool
 iterator_next(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj;
 
@@ -1012,20 +1012,22 @@ js_IteratorMore(JSContext *cx, JSObject 
     }
 
     /* Fetch and cache the next value from the iterator. */
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
     if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
         return false;
     if (!ExternalInvoke(cx, iterobj, *rval, 0, NULL, rval)) {
         /* Check for StopIteration. */
-        if (!cx->isExceptionPending() || !js_ValueIsStopIteration(cx->getPendingException()))
+        if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
             return false;
 
-        cx->clearPendingException();
+        /* Inline JS_ClearPendingException(cx). */
+        cx->throwing = JS_FALSE;
+        cx->exception.setUndefined();
         cx->iterValue.setMagic(JS_NO_ITER_VALUE);
         rval->setBoolean(false);
         return true;
     }
 
     /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
     JS_ASSERT(!rval->isMagic(JS_NO_ITER_VALUE));
     cx->iterValue = *rval;
@@ -1279,23 +1281,23 @@ SendToGenerator(JSContext *cx, JSGenerat
              * expression.
              */
             gen->regs.sp[-1] = arg;
         }
         gen->state = JSGEN_RUNNING;
         break;
 
       case JSGENOP_THROW:
-        cx->setPendingException(arg);
+        SetPendingException(cx, arg);
         gen->state = JSGEN_RUNNING;
         break;
 
       default:
         JS_ASSERT(op == JSGENOP_CLOSE);
-        cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
+        SetPendingException(cx, MagicValue(JS_GENERATOR_CLOSING));
         gen->state = JSGEN_CLOSING;
         break;
     }
 
     JSStackFrame *genfp = gen->floatingFrame();
     Value *genvp = gen->floatingStack;
     uintN vplen = genfp->formalArgsEnd() - genvp;
 
@@ -1343,17 +1345,17 @@ SendToGenerator(JSContext *cx, JSGenerat
         genfp->setFloatingGenerator();
     }
     MUST_FLOW_LABEL(restore)
     RebaseRegsFromTo(&gen->regs, stackfp, genfp);
 
     if (gen->floatingFrame()->isYielding()) {
         /* Yield cannot fail, throw or be called on closing. */
         JS_ASSERT(ok);
-        JS_ASSERT(!cx->isExceptionPending());
+        JS_ASSERT(!cx->throwing);
         JS_ASSERT(gen->state == JSGEN_RUNNING);
         JS_ASSERT(op != JSGENOP_CLOSE);
         genfp->clearYielding();
         gen->state = JSGEN_OPEN;
         return JS_TRUE;
     }
 
     genfp->clearReturnValue();
@@ -1429,17 +1431,17 @@ generator_op(JSContext *cx, JSGeneratorO
         }
     } else if (gen->state == JSGEN_CLOSED) {
       closed_generator:
         switch (op) {
           case JSGENOP_NEXT:
           case JSGENOP_SEND:
             return js_ThrowStopIteration(cx);
           case JSGENOP_THROW:
-            cx->setPendingException(argc >= 1 ? vp[2] : UndefinedValue());
+            SetPendingException(cx, argc >= 1 ? vp[2] : UndefinedValue());
             return JS_FALSE;
           default:
             JS_ASSERT(op == JSGENOP_CLOSE);
             return JS_TRUE;
         }
     }
 
     bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1662,17 +1662,17 @@ class RegExpGuard
 
     /*
      * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
      * pattern string, or a lengthy pattern string can thwart this process.
      *
      * @param checkMetaChars    Look for regexp metachars in the pattern string.
      * @return                  Whether flat matching could be used.
      *
-     * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
+     * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->throwing.
      */
     const FlatMatch *
     tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
                  bool checkMetaChars = true)
     {
         if (rep.hasRegExp())
             return NULL;
 
@@ -1851,17 +1851,17 @@ str_match(JSContext *cx, uintN argc, Val
     JSString *str;
     NORMALIZE_THIS(cx, vp, str);
 
     RegExpGuard g(cx);
     if (!g.init(argc, vp))
         return false;
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc))
         return BuildFlatMatchArray(cx, str, *fm, vp);
-    if (cx->isExceptionPending())  /* from tryFlatMatch */
+    if (cx->throwing)  /* from tryFlatMatch */
         return false;
 
     const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
     if (!rep)
         return false;
 
     AutoObjectRooter array(cx);
     MatchArgType arg = array.addr();
@@ -1883,17 +1883,17 @@ str_search(JSContext *cx, uintN argc, Va
 
     RegExpGuard g(cx);
     if (!g.init(argc, vp))
         return false;
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc)) {
         vp->setInt32(fm->match());
         return true;
     }
-    if (cx->isExceptionPending())  /* from tryFlatMatch */
+    if (cx->throwing)  /* from tryFlatMatch */
         return false;
     const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
     if (!rep)
         return false;
 
     RegExpStatics *res = cx->regExpStatics();
     size_t i = 0;
     if (!rep->re().execute(cx, res, str, &i, true, vp))
@@ -2496,17 +2496,17 @@ js::str_replace(JSContext *cx, uintN arg
      * However, if the user invokes our (non-standard) |flags| argument
      * extension then we revert to creating a regular expression. Note that
      * this is observable behavior through the side-effect mutation of the
      * |RegExp| statics.
      */
 
     const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, optarg, argc, false);
     if (!fm) {
-        if (cx->isExceptionPending())  /* oom in RopeMatch in tryFlatMatch */
+        if (cx->throwing)  /* oom in RopeMatch in tryFlatMatch */
             return false;
         JS_ASSERT_IF(!rdata.g.hasRegExpPair(), argc > optarg);
         return str_replace_regexp(cx, argc, vp, rdata);
     }
 
     if (fm->match() < 0) {
         vp->setString(rdata.str);
         return true;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -6563,17 +6563,17 @@ ExecuteTree(JSContext* cx, TreeFragment*
     JS_ASSERT(*(uint64*)&tm->storage->global()[globalSlots] == 0xdeadbeefdeadbeefLL);
     JS_ASSERT_IF(lr->exitType == LOOP_EXIT, !lr->calldepth);
 
     /* Restore interpreter state. */
     LeaveTree(tm, state, lr);
 
     *lrp = state.innermost;
     bool ok = !(state.builtinStatus & BUILTIN_ERROR);
-    JS_ASSERT_IF(cx->isExceptionPending(), !ok);
+    JS_ASSERT_IF(cx->throwing, !ok);
 
     size_t iters = tm->iterationCounter;
 
     f->execs++;
     f->iters += iters;
 
 #ifdef DEBUG
     JSStackFrame *fp = cx->fp();
@@ -16587,17 +16587,17 @@ RecordTracePoint(JSContext* cx, uintN& i
 
   interpret:
     JS_ASSERT(TRACE_RECORDER(cx));
 
     /* Locked and loaded with a recorder. Ask the interperter to go run some code. */
     if (!Interpret(cx, fp, inlineCallCount, JSINTERP_RECORD))
         return TPA_Error;
 
-    JS_ASSERT(!cx->isExceptionPending());
+    JS_ASSERT(!cx->throwing);
     
     return TPA_RanStuff;
 }
 
 LoopProfile::LoopProfile(JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom)
     : entryScript(entryfp->script()),
       entryfp(entryfp),
       top(top),
@@ -16754,17 +16754,17 @@ MonitorTracePoint(JSContext *cx, uintN& 
     debug_only_printf(LC_TMProfiler, "Profiling at line %d\n",
                       js_FramePCToLineNumber(cx, cx->fp()));
 
     tm->profile = prof;
 
     if (!Interpret(cx, cx->fp(), inlineCallCount, JSINTERP_PROFILE))
         return TPA_Error;
 
-    JS_ASSERT(!cx->isExceptionPending());
+    JS_ASSERT(!cx->throwing);
     
     return TPA_RanStuff;
 }
 
 /*
  * Returns true if pc is within the given loop.
  * If we're in a different script, then we must have come from
  * a call instruction within the loop (since we check if we're within
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -353,18 +353,17 @@ AutoCompartment::enter()
         context->resetCompartment();
         wasSane = (context->compartment == oldCompartment);
 #endif
 
         context->compartment = destination;
         JSObject *scopeChain = target->getGlobal();
         JS_ASSERT(scopeChain->isNative());
         frame.construct();
-        if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref()) ||
-            !destination->wrapException(context)) {
+        if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
             frame.destroy();
             context->compartment = origin;
             return false;
         }
     }
     entered = true;
     return true;
 }
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1715,19 +1715,26 @@ mjit::Compiler::generateMethod()
           END_CASE(JSOP_IN)
 
           BEGIN_CASE(JSOP_INSTANCEOF)
             if (!jsop_instanceof())
                 return Compile_Error;
           END_CASE(JSOP_INSTANCEOF)
 
           BEGIN_CASE(JSOP_EXCEPTION)
-            prepareStubCall(Uses(0));
-            INLINE_STUBCALL(stubs::Exception);
-            frame.pushSynced();
+          {
+            JS_STATIC_ASSERT(sizeof(cx->throwing) == 4);
+            RegisterID reg = frame.allocReg();
+            masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), reg);
+            masm.store32(Imm32(JS_FALSE), Address(reg, offsetof(JSContext, throwing)));
+
+            Address excn(reg, offsetof(JSContext, exception));
+            frame.freeReg(reg);
+            frame.push(excn);
+          }
           END_CASE(JSOP_EXCEPTION)
 
           BEGIN_CASE(JSOP_LINENO)
           END_CASE(JSOP_LINENO)
 
           BEGIN_CASE(JSOP_ENUMELEM)
             // Normally, SETELEM transforms the stack
             //  from: OBJ ID VALUE
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -76,17 +76,17 @@ using namespace JSC;
 
 static jsbytecode *
 FindExceptionHandler(JSContext *cx)
 {
     JSStackFrame *fp = cx->fp();
     JSScript *script = fp->script();
 
 top:
-    if (cx->isExceptionPending() && JSScript::isValidOffset(script->trynotesOffset)) {
+    if (cx->throwing && JSScript::isValidOffset(script->trynotesOffset)) {
         // The PC is updated before every stub call, so we can use it here.
         unsigned offset = cx->regs->pc - script->main;
 
         JSTryNoteArray *tnarray = script->trynotes();
         for (unsigned i = 0; i < tnarray->length; ++i) {
             JSTryNote *tn = &tnarray->vector[i];
 
             // The following if condition actually tests two separate conditions:
@@ -114,55 +114,56 @@ top:
             JS_ASSERT(cx->regs->sp == fp->base() + tn->stackDepth);
 
             switch (tn->kind) {
                 case JSTRY_CATCH:
                   JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENTERBLOCK);
 
 #if JS_HAS_GENERATORS
                   /* Catch cannot intercept the closing of a generator. */
-                  if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
+                  if (JS_UNLIKELY(cx->exception.isMagic(JS_GENERATOR_CLOSING)))
                       break;
 #endif
 
                   /*
                    * Don't clear cx->throwing to save cx->exception from GC
                    * until it is pushed to the stack via [exception] in the
                    * catch block.
                    */
                   return pc;
 
                 case JSTRY_FINALLY:
                   /*
                    * Push (true, exception) pair for finally to indicate that
                    * [retsub] should rethrow the exception.
                    */
                   cx->regs->sp[0].setBoolean(true);
-                  cx->regs->sp[1] = cx->getPendingException();
+                  cx->regs->sp[1] = cx->exception;
                   cx->regs->sp += 2;
-                  cx->clearPendingException();
+                  cx->throwing = JS_FALSE;
                   return pc;
 
                 case JSTRY_ITER:
                 {
                   /*
                    * This is similar to JSOP_ENDITER in the interpreter loop,
                    * except the code now uses the stack slot normally used by
                    * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
                    * adjustment and regs.sp[1] after, to save and restore the
                    * pending exception.
                    */
-                  Value v = cx->getPendingException();
+                  AutoValueRooter tvr(cx, cx->exception);
                   JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENDITER);
-                  cx->clearPendingException();
+                  cx->throwing = JS_FALSE;
                   ok = !!js_CloseIterator(cx, &cx->regs->sp[-1].toObject());
                   cx->regs->sp -= 1;
                   if (!ok)
                       goto top;
-                  cx->setPendingException(v);
+                  cx->throwing = JS_TRUE;
+                  cx->exception = tvr.value();
                 }
             }
         }
     }
 
     return NULL;
 }
 
@@ -496,27 +497,27 @@ js_InternalThrow(VMFrame &f)
 
     // Call the throw hook if necessary
     JSThrowHook handler = f.cx->debugHooks->throwHook;
     if (handler) {
         Value rval;
         switch (handler(cx, cx->fp()->script(), cx->regs->pc, Jsvalify(&rval),
                         cx->debugHooks->throwHookData)) {
           case JSTRAP_ERROR:
-            cx->clearPendingException();
+            cx->throwing = JS_FALSE;
             return NULL;
 
           case JSTRAP_RETURN:
-            cx->clearPendingException();
+            cx->throwing = JS_FALSE;
             cx->fp()->setReturnValue(rval);
             return JS_FUNC_TO_DATA_PTR(void *,
                    cx->jaegerCompartment()->forceReturnTrampoline());
 
           case JSTRAP_THROW:
-            cx->setPendingException(rval);
+            cx->exception = rval;
             break;
 
           default:
             break;
         }
     }
 
     jsbytecode *pc = NULL;
@@ -525,17 +526,17 @@ js_InternalThrow(VMFrame &f)
         if (pc)
             break;
 
         // If on the 'topmost' frame (where topmost means the first frame
         // called into through js_Interpret). In this case, we still unwind,
         // but we shouldn't return from a JS function, because we're not in a
         // JS function.
         bool lastFrame = (f.entryfp == f.fp());
-        js_UnwindScope(cx, 0, cx->isExceptionPending());
+        js_UnwindScope(cx, 0, cx->throwing);
 
         // For consistency with Interpret(), always run the script epilogue.
         // This simplifies interactions with RunTracer(), since it can assume
         // no matter how a function exited (error or not), that the epilogue
         // does not need to be run.
         ScriptEpilogue(f.cx, f.fp(), false);
 
         if (lastFrame)
@@ -652,31 +653,31 @@ HandleErrorInExcessFrame(VMFrame &f, JSS
         /* Clear imacros. */
         if (fp->hasImacropc()) {
             cx->regs->pc = fp->imacropc();
             fp->clearImacropc();
         }
         JS_ASSERT(!fp->hasImacropc());
 
         /* If there's an exception and a handler, set the pc and leave. */
-        if (cx->isExceptionPending()) {
+        if (cx->throwing) {
             jsbytecode *pc = FindExceptionHandler(cx);
             if (pc) {
                 cx->regs->pc = pc;
                 returnOK = true;
                 break;
             }
         }
 
         /* Don't unwind if this was the entry frame. */
         if (fp == stopFp)
             break;
 
         /* Unwind and return. */
-        returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->isExceptionPending()));
+        returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->throwing));
         returnOK = ScriptEpilogue(cx, fp, returnOK);
         InlineReturn(f);
     }
 
     JS_ASSERT(&f.regs == cx->regs);
     JS_ASSERT_IF(!returnOK, cx->fp() == stopFp);
 
     return returnOK;
@@ -967,17 +968,17 @@ RunTracer(VMFrame &f)
 
 #if JS_MONOIC
     if (blacklist)
         DisableTraceHint(f, tic);
 #endif
 
     // Even though ExecuteTree() bypasses the interpreter, it should propagate
     // error failures correctly.
-    JS_ASSERT_IF(cx->isExceptionPending(), tpa == TPA_Error);
+    JS_ASSERT_IF(cx->throwing, tpa == TPA_Error);
 
 	f.fp() = cx->fp();
     JS_ASSERT(f.fp() == cx->fp());
     switch (tpa) {
       case TPA_Nothing:
         return NULL;
 
       case TPA_Error:
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1286,33 +1286,34 @@ void JS_FASTCALL
 stubs::Debugger(VMFrame &f, jsbytecode *pc)
 {
     JSDebuggerHandler handler = f.cx->debugHooks->debuggerHandler;
     if (handler) {
         Value rval;
         switch (handler(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval),
                         f.cx->debugHooks->debuggerHandlerData)) {
           case JSTRAP_THROW:
-            f.cx->setPendingException(rval);
+            f.cx->throwing = JS_TRUE;
+            f.cx->exception = rval;
             THROW();
 
           case JSTRAP_RETURN:
-            f.cx->clearPendingException();
+            f.cx->throwing = JS_FALSE;
             f.cx->fp()->setReturnValue(rval);
 #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64)
             *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
                                          f.cx->jaegerCompartment()->forceReturnFastTrampoline());
 #else
             *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
                                          f.cx->jaegerCompartment()->forceReturnTrampoline());
 #endif
             break;
 
           case JSTRAP_ERROR:
-            f.cx->clearPendingException();
+            f.cx->throwing = JS_FALSE;
             THROW();
 
           default:
             break;
         }
     }
 }
 
@@ -1346,33 +1347,34 @@ stubs::Trap(VMFrame &f, uint32 trapTypes
                           f.cx->debugHooks->interruptHookData);
     }
 
     if (result == JSTRAP_CONTINUE && (trapTypes & JSTRAP_TRAP))
         result = JS_HandleTrap(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval));
 
     switch (result) {
       case JSTRAP_THROW:
-        f.cx->setPendingException(rval);
+        f.cx->throwing = JS_TRUE;
+        f.cx->exception = rval;
         THROW();
 
       case JSTRAP_RETURN:
-        f.cx->clearPendingException();
+        f.cx->throwing = JS_FALSE;
         f.cx->fp()->setReturnValue(rval);
 #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64)
         *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
                                      f.cx->jaegerCompartment()->forceReturnFastTrampoline());
 #else
         *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
                                      f.cx->jaegerCompartment()->forceReturnTrampoline());
 #endif
         break;
 
       case JSTRAP_ERROR:
-        f.cx->clearPendingException();
+        f.cx->throwing = JS_FALSE;
         THROW();
 
       default:
         break;
     }
 }
 
 void JS_FASTCALL
@@ -2328,18 +2330,19 @@ stubs::StrictNe(VMFrame &f)
     f.regs.sp[-1].setBoolean(equal != JS_TRUE);
 }
 
 void JS_FASTCALL
 stubs::Throw(VMFrame &f)
 {
     JSContext *cx = f.cx;
 
-    JS_ASSERT(!cx->isExceptionPending());
-    cx->setPendingException(f.regs.sp[-1]);
+    JS_ASSERT(!cx->throwing);
+    cx->throwing = JS_TRUE;
+    cx->exception = f.regs.sp[-1];
     THROW();
 }
 
 JSObject * JS_FASTCALL
 stubs::FlatLambda(VMFrame &f, JSFunction *fun)
 {
     JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
     if (!obj)
@@ -2762,22 +2765,16 @@ stubs::In(VMFrame &f)
         THROWV(JS_FALSE);
 
     return !!prop;
 }
 
 template void JS_FASTCALL stubs::DelElem<true>(VMFrame &f);
 template void JS_FASTCALL stubs::DelElem<false>(VMFrame &f);
 
-void JS_FASTCALL
-stubs::Exception(VMFrame &f)
-{
-    f.regs.sp[0] = f.cx->getPendingException();
-    f.cx->clearPendingException();
-}
 template <bool Clamped>
 int32 JS_FASTCALL
 stubs::ConvertToTypedInt(JSContext *cx, Value *vp)
 {
     JS_ASSERT(!vp->isInt32());
 
     if (vp->isDouble()) {
         if (Clamped)
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -219,18 +219,16 @@ JSString * JS_FASTCALL TypeOf(VMFrame &f
 JSBool JS_FASTCALL InstanceOf(VMFrame &f);
 void JS_FASTCALL FastInstanceOf(VMFrame &f);
 void JS_FASTCALL ArgCnt(VMFrame &f);
 void JS_FASTCALL Unbrand(VMFrame &f);
 
 template <bool strict> int32 JS_FASTCALL ConvertToTypedInt(JSContext *cx, Value *vp);
 void JS_FASTCALL ConvertToTypedFloat(JSContext *cx, Value *vp);
 
-void JS_FASTCALL Exception(VMFrame &f);
-
 } /* namespace stubs */
 
 /* 
  * If COND is true, return A; otherwise, return B. This allows us to choose between
  * function template instantiations without running afoul of C++'s overload resolution
  * rules. (Try simplifying, and you'll either see the problem --- or have found a
  * better solution!)
  */