Bug 786068 - make JS_EnterCrossCompartmentCall infallible (and call it JS_EnterCompartment) (r=sfink)
authorLuke Wagner <luke@mozilla.com>
Mon, 27 Aug 2012 13:32:51 -0700
changeset 105664 d65863fbb129f3403d80fba6a508e5558f42509f
parent 105663 cc8bc4e1bdfcaab199e7eb733de0c3bba0d261a8
child 105665 8bdeda83f7b1be7264ed1d2476c6bf432e20efe2
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewerssfink
bugs786068
milestone18.0a1
Bug 786068 - make JS_EnterCrossCompartmentCall infallible (and call it JS_EnterCompartment) (r=sfink)
js/jsd/jsd_high.c
js/jsd/jsd_scpt.c
js/jsd/jsd_val.c
js/jsd/jsd_xpc.cpp
js/src/jsapi-tests/tests.cpp
js/src/jsapi-tests/tests.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsdbgapi.h
js/src/vm/GlobalObject.cpp
js/xpconnect/src/XPCStack.cpp
startupcache/test/TestStartupCache.cpp
--- a/js/jsd/jsd_high.c
+++ b/js/jsd/jsd_high.c
@@ -52,17 +52,17 @@ static JSBool
 
 static JSDContext*
 _newJSDContext(JSRuntime*         jsrt, 
                JSD_UserCallbacks* callbacks, 
                void*              user,
                JSObject*          scopeobj)
 {
     JSDContext* jsdc = NULL;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment *oldCompartment = NULL;
     JSBool ok;
 
     if( ! jsrt )
         return NULL;
 
     if( ! _validateUserCallbacks(callbacks) )
         return NULL;
 
@@ -108,26 +108,24 @@ static JSDContext*
     JS_BeginRequest(jsdc->dumbContext);
     JS_SetOptions(jsdc->dumbContext, JS_GetOptions(jsdc->dumbContext));
 
     jsdc->glob = JS_NewGlobalObject(jsdc->dumbContext, &global_class, NULL);
 
     if( ! jsdc->glob )
         goto label_newJSDContext_failure;
 
-    call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
-    if( ! call )
-        goto label_newJSDContext_failure;
+    oldCompartment = JS_EnterCompartment(jsdc->dumbContext, jsdc->glob);
 
     if ( ! JS_AddNamedObjectRoot(jsdc->dumbContext, &jsdc->glob, "JSD context global") )
         goto label_newJSDContext_failure;
 
     ok = JS_InitStandardClasses(jsdc->dumbContext, jsdc->glob);
 
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
     if( ! ok )
         goto label_newJSDContext_failure;
 
     JS_EndRequest(jsdc->dumbContext);
 
     jsdc->data = NULL;
     jsdc->inited = JS_TRUE;
 
--- a/js/jsd/jsd_scpt.c
+++ b/js/jsd/jsd_scpt.c
@@ -490,52 +490,48 @@ jsd_GetScriptLineExtent(JSDContext* jsdc
         jsdscript->lineExtent = JS_GetScriptLineExtent(jsdc->dumbContext, jsdscript->script);
     return jsdscript->lineExtent;
 }
 
 uintptr_t
 jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line)
 {
     uintptr_t pc;
-    JSCrossCompartmentCall *call;
+    JSCompartment *oldCompartment;
 
     if( !jsdscript )
         return 0;
 #ifdef LIVEWIRE
     if( jsdscript->lwscript )
     {
         unsigned newline;
         jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline);
         if( line != newline )
             line = newline;
     }
 #endif
 
-    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
-    if(!call)
-        return 0;
+    oldCompartment = JS_EnterCompartmentOfScript(jsdc->dumbContext, jsdscript->script);
     pc = (uintptr_t) JS_LineNumberToPC(jsdc->dumbContext, jsdscript->script, line );
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
     return pc;
 }
 
 unsigned
 jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc)
 {
-    JSCrossCompartmentCall *call;
+    JSCompartment* oldCompartment;
     unsigned first = jsdscript->lineBase;
     unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
     unsigned line = 0;
 
-    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
-    if(!call)
-        return 0;
+    oldCompartment = JS_EnterCompartmentOfScript(jsdc->dumbContext, jsdscript->script);
     if (pc)
         line = JS_PCToLineNumber(jsdc->dumbContext, jsdscript->script, (jsbytecode*)pc);
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
 
     if( line < first )
         return first;
     if( line > last )
         return last;
 
 #ifdef LIVEWIRE
     if( jsdscript && jsdscript->lwscript )
@@ -549,46 +545,44 @@ jsd_GetClosestLine(JSDContext* jsdc, JSD
     return line;    
 }
 
 JSBool
 jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript,
                unsigned startLine, unsigned maxLines,
                unsigned* count, unsigned** retLines, uintptr_t** retPCs)
 {
-    JSCrossCompartmentCall *call;
+    JSCompartment* oldCompartment;
     unsigned first = jsdscript->lineBase;
     unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
     JSBool ok;
     unsigned *lines;
     jsbytecode **pcs;
     unsigned i;
 
     if (last < startLine)
         return JS_TRUE;
 
-    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
-    if (!call)
-        return JS_FALSE;
+    oldCompartment = JS_EnterCompartmentOfScript(jsdc->dumbContext, jsdscript->script);
 
     ok = JS_GetLinePCs(jsdc->dumbContext, jsdscript->script,
                        startLine, maxLines,
                        count, retLines, &pcs);
 
     if (ok) {
         if (retPCs) {
             for (i = 0; i < *count; ++i) {
                 (*retPCs)[i] = (*pcs)[i];
             }
         }
 
         JS_free(jsdc->dumbContext, pcs);
     }
 
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
     return ok;
 }
 
 JSBool
 jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata)
 {
     JSD_LOCK();
     jsdc->scriptHook = hook;
@@ -607,25 +601,23 @@ jsd_GetScriptHook(JSDContext* jsdc, JSD_
         *callerdata = jsdc->scriptHookData;
     JSD_UNLOCK();
     return JS_TRUE;
 }    
 
 JSBool
 jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable)
 {
-    JSCrossCompartmentCall *call;
+    JSCompartment* oldCompartment;
     JSBool rv;
-    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
-    if(!call)
-        return JS_FALSE;
+    oldCompartment = JS_EnterCompartmentOfScript(jsdc->dumbContext, jsdscript->script);
     JSD_LOCK();
     rv = JS_SetSingleStepMode(jsdc->dumbContext, jsdscript->script, enable);
     JSD_UNLOCK();
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
     return rv;
 }
 
 
 /***************************************************************************/
 
 void
 jsd_NewScriptHookProc( 
@@ -823,17 +815,17 @@ JSBool
 jsd_SetExecutionHook(JSDContext*           jsdc, 
                      JSDScript*            jsdscript,
                      uintptr_t             pc,
                      JSD_ExecutionHookProc hook,
                      void*                 callerdata)
 {
     JSDExecHook* jsdhook;
     JSBool rv;
-    JSCrossCompartmentCall *call;
+    JSCompartment* oldCompartment;
 
     JSD_LOCK();
     if( ! hook )
     {
         jsd_ClearExecutionHook(jsdc, jsdscript, pc);
         JSD_UNLOCK();
         return JS_TRUE;
     }
@@ -853,28 +845,23 @@ jsd_SetExecutionHook(JSDContext*        
         JSD_UNLOCK();
         return JS_FALSE;
     }
     jsdhook->jsdscript  = jsdscript;
     jsdhook->pc         = pc;
     jsdhook->hook       = hook;
     jsdhook->callerdata = callerdata;
 
-    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
-    if(!call) {
-        free(jsdhook);
-        JSD_UNLOCK();
-        return JS_FALSE;
-    }
+    oldCompartment = JS_EnterCompartmentOfScript(jsdc->dumbContext, jsdscript->script);
 
     rv = JS_SetTrap(jsdc->dumbContext, jsdscript->script, 
                     (jsbytecode*)pc, jsd_TrapHandler,
                     PRIVATE_TO_JSVAL(jsdhook));
 
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
 
     if ( ! rv ) {
         free(jsdhook);
         JSD_UNLOCK();
         return JS_FALSE;
     }
 
     JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
@@ -883,38 +870,34 @@ jsd_SetExecutionHook(JSDContext*        
     return JS_TRUE;
 }
 
 JSBool
 jsd_ClearExecutionHook(JSDContext*           jsdc, 
                        JSDScript*            jsdscript,
                        uintptr_t             pc)
 {
-    JSCrossCompartmentCall *call;
+    JSCompartment* oldCompartment;
     JSDExecHook* jsdhook;
 
     JSD_LOCK();
 
     jsdhook = _findHook(jsdc, jsdscript, pc);
     if( ! jsdhook )
     {
         JSD_UNLOCK();
         return JS_FALSE;
     }
 
-    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
-    if(!call) {
-        JSD_UNLOCK();
-        return JS_FALSE;
-    }
+    oldCompartment = JS_EnterCompartmentOfScript(jsdc->dumbContext, jsdscript->script);
 
     JS_ClearTrap(jsdc->dumbContext, jsdscript->script, 
                  (jsbytecode*)pc, NULL, NULL );
 
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
 
     JS_REMOVE_LINK(&jsdhook->links);
     free(jsdhook);
 
     JSD_UNLOCK();
     return JS_TRUE;
 }
 
--- a/js/jsd/jsd_val.c
+++ b/js/jsd/jsd_val.c
@@ -114,35 +114,29 @@ jsd_IsValueFunction(JSDContext* jsdc, JS
 }
 
 JSBool
 jsd_IsValueNative(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
     JSFunction* fun;
     JSExceptionState* exceptionState;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     if(jsd_IsValueFunction(jsdc, jsdval))
     {
         JSBool ok = JS_FALSE;
         JS_BeginRequest(cx);
-        call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, JSVAL_TO_OBJECT(jsdval->val));
-        if(!call) {
-            JS_EndRequest(cx);
-
-            return JS_FALSE;
-        }
-
+        oldCompartment = JS_EnterCompartment(jsdc->dumbContext, JSVAL_TO_OBJECT(jsdval->val));
         exceptionState = JS_SaveExceptionState(cx);
         fun = JSD_GetValueFunction(jsdc, jsdval);
         JS_RestoreExceptionState(cx, exceptionState);
         if(fun)
             ok = JS_GetFunctionScript(cx, fun) ? JS_FALSE : JS_TRUE;
-        JS_LeaveCrossCompartmentCall(call);
+        JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
         JS_EndRequest(cx);
         JS_ASSERT(fun);
         return ok;
     }
     return !JSVAL_IS_PRIMITIVE(jsdval->val);
 }
 
 /***************************************************************************/
@@ -173,17 +167,17 @@ jsd_GetValueDouble(JSDContext* jsdc, JSD
     return JSVAL_TO_DOUBLE(jsdval->val);
 }
 
 JSString*
 jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
     JSExceptionState* exceptionState;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
     jsval stringval;
     JSString *string;
     JSBool needWrap;
     JSObject *scopeObj;
 
     if(jsdval->string)
         return jsdval->string;
 
@@ -192,73 +186,63 @@ jsd_GetValueString(JSDContext* jsdc, JSD
         jsdval->string = JSVAL_TO_STRING(jsdval->val);
         return jsdval->string;
     }
 
     JS_BeginRequest(cx);
 
     /* Objects call JS_ValueToString in their own compartment. */
     scopeObj = !JSVAL_IS_PRIMITIVE(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob;
-    call = JS_EnterCrossCompartmentCall(cx, scopeObj);
-    if(!call) {
-        JS_EndRequest(cx);
-        return NULL;
-    }
+    oldCompartment = JS_EnterCompartment(cx, scopeObj);
     exceptionState = JS_SaveExceptionState(cx);
 
     string = JS_ValueToString(cx, jsdval->val);
 
     JS_RestoreExceptionState(cx, exceptionState);
-    JS_LeaveCrossCompartmentCall(call);
-    call = NULL;
+    JS_LeaveCompartment(cx, oldCompartment);
+    oldCompartment = NULL;
 
     if(string) {
         stringval = STRING_TO_JSVAL(string);
-        call = JS_EnterCrossCompartmentCall(cx, jsdc->glob);
+        oldCompartment = JS_EnterCompartment(cx, jsdc->glob);
     }
-    if(!string || !call || !JS_WrapValue(cx, &stringval)) {
-        if(call)
-            JS_LeaveCrossCompartmentCall(call);
+    if(!string || !JS_WrapValue(cx, &stringval)) {
+        if(oldCompartment)
+            JS_LeaveCompartment(cx, oldCompartment);
         JS_EndRequest(cx);
         return NULL;
     }
 
     jsdval->string = JSVAL_TO_STRING(stringval);
     if(!JS_AddNamedStringRoot(cx, &jsdval->string, "ValueString"))
         jsdval->string = NULL;
 
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(cx, oldCompartment);
     JS_EndRequest(cx);
 
     return jsdval->string;
 }
 
 JSString*
 jsd_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
     JSFunction* fun;
     JSExceptionState* exceptionState;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     if(!jsdval->funName && jsd_IsValueFunction(jsdc, jsdval))
     {
         JS_BeginRequest(cx);
 
-        call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, JSVAL_TO_OBJECT(jsdval->val));
-        if(!call) {
-            JS_EndRequest(cx);
-
-            return NULL;
-        }
-
+        oldCompartment = JS_EnterCompartment(jsdc->dumbContext, JSVAL_TO_OBJECT(jsdval->val));
         exceptionState = JS_SaveExceptionState(cx);
         fun = JSD_GetValueFunction(jsdc, jsdval);
         JS_RestoreExceptionState(cx, exceptionState);
-        JS_LeaveCrossCompartmentCall(call);
+        JS_LeaveCompartment(cx, oldCompartment);
         JS_EndRequest(cx);
         if(!fun)
             return NULL;
         jsdval->funName = JS_GetFunctionId(fun);
 
         /* For compatibility we return "anonymous", not an empty string here. */
         if (!jsdval->funName)
             jsdval->funName = JS_GetAnonymousString(jsdc->jsrt);
@@ -272,41 +256,36 @@ jsd_GetValueFunctionId(JSDContext* jsdc,
  * Create a new JSD value referring to a jsval. Copy string values into the
  * JSD compartment. Leave all other GCTHINGs in their native compartments
  * and access them through cross-compartment calls.
  */
 JSDValue*
 jsd_NewValue(JSDContext* jsdc, jsval val)
 {
     JSDValue* jsdval;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     if(!(jsdval = (JSDValue*) calloc(1, sizeof(JSDValue))))
         return NULL;
 
     if(JSVAL_IS_GCTHING(val))
     {
         JSBool ok;
         JS_BeginRequest(jsdc->dumbContext);
 
-        call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
-        if(!call) {
-            JS_EndRequest(jsdc->dumbContext);
-            free(jsdval);
-            return NULL;
-        }
+        oldCompartment = JS_EnterCompartment(jsdc->dumbContext, jsdc->glob);
 
         ok = JS_AddNamedValueRoot(jsdc->dumbContext, &jsdval->val, "JSDValue");
         if(ok && JSVAL_IS_STRING(val)) {
             if(!JS_WrapValue(jsdc->dumbContext, &val)) {
                 ok = JS_FALSE;
             }
         }
 
-        JS_LeaveCrossCompartmentCall(call);
+        JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
         JS_EndRequest(jsdc->dumbContext);
         if(!ok)
         {
             free(jsdval);
             return NULL;
         }
     }
     jsdval->val  = val;
@@ -314,34 +293,28 @@ jsd_NewValue(JSDContext* jsdc, jsval val
     JS_INIT_CLIST(&jsdval->props);
 
     return jsdval;
 }
 
 void
 jsd_DropValue(JSDContext* jsdc, JSDValue* jsdval)
 {
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     JS_ASSERT(jsdval->nref > 0);
     if(0 == --jsdval->nref)
     {
         jsd_RefreshValue(jsdc, jsdval);
         if(JSVAL_IS_GCTHING(jsdval->val))
         {
             JS_BeginRequest(jsdc->dumbContext);
-            call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
-            if(!call) {
-                JS_EndRequest(jsdc->dumbContext);
-
-                return;
-            }
-
+            oldCompartment = JS_EnterCompartment(jsdc->dumbContext, jsdc->glob);
             JS_RemoveValueRoot(jsdc->dumbContext, &jsdval->val);
-            JS_LeaveCrossCompartmentCall(call);
+            JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
             JS_EndRequest(jsdc->dumbContext);
         }
         free(jsdval);
     }
 }
 
 jsval
 jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval)
@@ -407,83 +380,72 @@ static void _freeProps(JSDContext* jsdc,
 }
 
 static JSBool _buildProps(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
     JSObject *obj;
     JSPropertyDescArray pda;
     unsigned i;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     JS_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props));
     JS_ASSERT(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)));
     JS_ASSERT(!JSVAL_IS_PRIMITIVE(jsdval->val));
 
     if(JSVAL_IS_PRIMITIVE(jsdval->val))
         return JS_FALSE;
 
     obj = JSVAL_TO_OBJECT(jsdval->val);
 
     JS_BeginRequest(cx);
-    call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
-    if(!call)
-    {
-        JS_EndRequest(jsdc->dumbContext);
-        return JS_FALSE;
-    }
+    oldCompartment = JS_EnterCompartment(jsdc->dumbContext, obj);
 
     if(!JS_GetPropertyDescArray(cx, obj, &pda))
     {
         JS_EndRequest(cx);
-        JS_LeaveCrossCompartmentCall(call);
+        JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
         return JS_FALSE;
     }
 
     for(i = 0; i < pda.length; i++)
     {
         JSDProperty* prop = _newProperty(jsdc, &pda.array[i], 0);
         if(!prop)
         {
             _freeProps(jsdc, jsdval);
             break;
         }
         JS_APPEND_LINK(&prop->links, &jsdval->props);
     }
     JS_PutPropertyDescArray(cx, &pda);
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
     JS_EndRequest(cx);
     SET_BIT_FLAG(jsdval->flags, GOT_PROPS);
     return !JS_CLIST_IS_EMPTY(&jsdval->props);
 }
 
 #undef  DROP_CLEAR_VALUE
 #define DROP_CLEAR_VALUE(jsdc, x) if(x){jsd_DropValue(jsdc,x); x = NULL;}
 
 void
 jsd_RefreshValue(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     if(jsdval->string)
     {
         /* if the jsval is a string, then we didn't need to root the string */
         if(!JSVAL_IS_STRING(jsdval->val))
         {
             JS_BeginRequest(cx);
-            call = JS_EnterCrossCompartmentCall(cx, jsdc->glob);
-            if(!call) {
-                JS_EndRequest(cx);
-
-                return;
-            }
-
+            oldCompartment = JS_EnterCompartment(cx, jsdc->glob);
             JS_RemoveStringRoot(cx, &jsdval->string);
-            JS_LeaveCrossCompartmentCall(call);
+            JS_LeaveCompartment(cx, oldCompartment);
             JS_EndRequest(cx);
         }
         jsdval->string = NULL;
     }
 
     jsdval->funName = NULL;
     jsdval->className = NULL;
     DROP_CLEAR_VALUE(jsdc, jsdval->proto);
@@ -545,17 +507,17 @@ jsd_GetValueProperty(JSDContext* jsdc, J
     JSObject* obj;
     unsigned  attrs = 0;
     JSBool found;
     JSPropertyDesc pd;
     const jschar * nameChars;
     size_t nameLen;
     jsval val, nameval;
     jsid nameid;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     if(!jsd_IsValueObject(jsdc, jsdval))
         return NULL;
 
     /* If we already have the prop, then return it */
     while(NULL != (jsdprop = jsd_IterateProperties(jsdc, jsdval, &iter)))
     {
         JSString* propName = jsd_GetValueString(jsdc, jsdprop->name);
@@ -570,57 +532,52 @@ jsd_GetValueProperty(JSDContext* jsdc, J
 
     if(!(obj = JSVAL_TO_OBJECT(jsdval->val)))
         return NULL;
 
     if (!(nameChars = JS_GetStringCharsZAndLength(cx, name, &nameLen)))
         return NULL;
 
     JS_BeginRequest(cx);
-    call = JS_EnterCrossCompartmentCall(cx, obj);
-    if(!call) {
-        JS_EndRequest(cx);
-
-        return NULL;
-    }
+    oldCompartment = JS_EnterCompartment(cx, obj);
 
     JS_GetUCPropertyAttributes(cx, obj, nameChars, nameLen, &attrs, &found);
     if (!found)
     {
-        JS_LeaveCrossCompartmentCall(call);
+        JS_LeaveCompartment(cx, oldCompartment);
         JS_EndRequest(cx);
         return NULL;
     }
 
     JS_ClearPendingException(cx);
 
     if(!JS_GetUCProperty(cx, obj, nameChars, nameLen, &val))
     {
         if (JS_IsExceptionPending(cx))
         {
             if (!JS_GetPendingException(cx, &pd.value))
             {
-                JS_LeaveCrossCompartmentCall(call);
+                JS_LeaveCompartment(cx, oldCompartment);
                 JS_EndRequest(cx);
                 return NULL;
             }
             pd.flags = JSPD_EXCEPTION;
         }
         else
         {
             pd.flags = JSPD_ERROR;
             pd.value = JSVAL_VOID;
         }
     }
     else
     {
         pd.value = val;
     }
 
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(cx, oldCompartment);
     JS_EndRequest(cx);
 
     nameval = STRING_TO_JSVAL(name);
     if (!JS_ValueToId(cx, nameval, &nameid) ||
         !JS_IdToValue(cx, nameid, &pd.id)) {
         return NULL;
     }
 
@@ -638,36 +595,31 @@ jsd_GetValueProperty(JSDContext* jsdc, J
  * JS_ValueToFunction by fully unwrapping the object first.
  */
 JSFunction*
 jsd_GetValueFunction(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSObject *obj;
     JSFunction *fun;
 
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
     if (JSVAL_IS_PRIMITIVE(jsdval->val))
         return NULL;
 
     obj = JS_UnwrapObject(JSVAL_TO_OBJECT(jsdval->val));
-    call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
-    if (!call)
-        return NULL;
-
+    oldCompartment = JS_EnterCompartment(jsdc->dumbContext, obj);
     fun = JS_ValueToFunction(jsdc->dumbContext, OBJECT_TO_JSVAL(obj));
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
 
     return fun;
 }
 
 JSDValue*
 jsd_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval)
 {
-    JSCrossCompartmentCall *call = NULL;
-
     if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO)))
     {
         JSObject* obj;
         JSObject* proto;
         JS_ASSERT(!jsdval->proto);
         SET_BIT_FLAG(jsdval->flags, GOT_PROTO);
         if(JSVAL_IS_PRIMITIVE(jsdval->val))
             return NULL;
@@ -680,134 +632,113 @@ jsd_GetValuePrototype(JSDContext* jsdc, 
     if(jsdval->proto)
         jsdval->proto->nref++;
     return jsdval->proto;
 }
 
 JSDValue*
 jsd_GetValueParent(JSDContext* jsdc, JSDValue* jsdval)
 {
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT)))
     {
         JSObject* obj;
         JSObject* parent;
         JS_ASSERT(!jsdval->parent);
         SET_BIT_FLAG(jsdval->flags, GOT_PARENT);
         if(JSVAL_IS_PRIMITIVE(jsdval->val))
             return NULL;
         obj = JSVAL_TO_OBJECT(jsdval->val);
         JS_BeginRequest(jsdc->dumbContext);
-        call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
-        if(!call) {
-            JS_EndRequest(jsdc->dumbContext);
-
-            return NULL;
-        }
+        oldCompartment = JS_EnterCompartment(jsdc->dumbContext, obj);
         parent = JS_GetParentOrScopeChain(jsdc->dumbContext,obj);
-        JS_LeaveCrossCompartmentCall(call);
+        JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
         JS_EndRequest(jsdc->dumbContext);
         if(!parent)
             return NULL;
         jsdval->parent = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(parent));
     }
     if(jsdval->parent)
         jsdval->parent->nref++;
     return jsdval->parent;
 }
 
 JSDValue*
 jsd_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval)
 {
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR)))
     {
         JSObject* obj;
         JSObject* proto;
         JSObject* ctor;
         JS_ASSERT(!jsdval->ctor);
         SET_BIT_FLAG(jsdval->flags, GOT_CTOR);
         if(JSVAL_IS_PRIMITIVE(jsdval->val))
             return NULL;
         obj = JSVAL_TO_OBJECT(jsdval->val);
         proto = JS_GetPrototype(obj);
         if(!proto)
             return NULL;
         JS_BeginRequest(jsdc->dumbContext);
-        call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
-        if(!call) {
-            JS_EndRequest(jsdc->dumbContext);
-
-            return NULL;
-        }
+        oldCompartment = JS_EnterCompartment(jsdc->dumbContext, obj);
         ctor = JS_GetConstructor(jsdc->dumbContext,proto);
-        JS_LeaveCrossCompartmentCall(call);
+        JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
         JS_EndRequest(jsdc->dumbContext);
         if(!ctor)
             return NULL;
         jsdval->ctor = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(ctor));
     }
     if(jsdval->ctor)
         jsdval->ctor->nref++;
     return jsdval->ctor;
 }
 
 const char*
 jsd_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval)
 {
     jsval val = jsdval->val;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     if(!jsdval->className && !JSVAL_IS_PRIMITIVE(val))
     {
         JSObject* obj = JSVAL_TO_OBJECT(val);
         JS_BeginRequest(jsdc->dumbContext);
-        call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
-        if(!call) {
-            JS_EndRequest(jsdc->dumbContext);
-
-            return NULL;
-        }
+        oldCompartment = JS_EnterCompartment(jsdc->dumbContext, obj);
         jsdval->className = JS_GetDebugClassName(obj);
-        JS_LeaveCrossCompartmentCall(call);
+        JS_LeaveCompartment(jsdc->dumbContext, oldCompartment);
         JS_EndRequest(jsdc->dumbContext);
     }
     return jsdval->className;
 }
 
 JSDScript*
 jsd_GetScriptForValue(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
     jsval val = jsdval->val;
     JSFunction* fun = NULL;
     JSExceptionState* exceptionState;
     JSScript* script = NULL;
     JSDScript* jsdscript;
-    JSCrossCompartmentCall *call = NULL;
+    JSCompartment* oldCompartment = NULL;
 
     if (!jsd_IsValueFunction(jsdc, jsdval))
         return NULL;
 
     JS_BeginRequest(cx);
-    call = JS_EnterCrossCompartmentCall(cx, JSVAL_TO_OBJECT(val));
-    if (!call) {
-        JS_EndRequest(cx);
-
-        return NULL;
-    }
-
+    oldCompartment = JS_EnterCompartment(cx, JSVAL_TO_OBJECT(val));
     exceptionState = JS_SaveExceptionState(cx);
     fun = JSD_GetValueFunction(jsdc, jsdval);
     JS_RestoreExceptionState(cx, exceptionState);
     if (fun)
         script = JS_GetFunctionScript(cx, fun);
-    JS_LeaveCrossCompartmentCall(call);
+    JS_LeaveCompartment(cx, oldCompartment);
     JS_EndRequest(cx);
 
     if (!script)
         return NULL;
 
     JSD_LOCK_SCRIPTS(jsdc);
     jsdscript = jsd_FindJSDScript(jsdc, script);
     JSD_UNLOCK_SCRIPTS(jsdc);
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -1022,19 +1022,17 @@ jsdScript::CreatePPLineMap()
         if (!fun || !(script = JS_GetFunctionScript(cx, fun)))
             return nullptr;
         baseLine = 3;
     } else {
         script = JSD_GetJSScript(mCx, mScript);
         JSString *jsstr;
 
         {
-            JS::AutoEnterScriptCompartment ac;
-            if (!ac.enter(cx, script))
-                return nullptr;
+            JSAutoCompartment ac(cx, script);
 
             jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
             if (!jsstr)
                 return nullptr;
 
             if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length)))
                 return nullptr;
         }
@@ -1125,19 +1123,17 @@ jsdScript::GetJSDScript(JSDScript **_rva
 }
 
 NS_IMETHODIMP
 jsdScript::GetVersion (int32_t *_rval)
 {
     ASSERT_VALID_EPHEMERAL;
     JSContext *cx = JSD_GetDefaultJSContext (mCx);
     JSScript *script = JSD_GetJSScript(mCx, mScript);
-    JS::AutoEnterScriptCompartment ac;
-    if (!ac.enter(cx, script))
-        return NS_ERROR_FAILURE;
+    JSAutoCompartment ac(cx, script);
     *_rval = static_cast<int32_t>(JS_GetScriptVersion(cx, script));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdScript::GetTag(uint32_t *_rval)
 {
     if (!mTag)
@@ -1319,24 +1315,22 @@ jsdScript::GetFunctionSource(nsAString &
         return NS_ERROR_FAILURE;
     }
     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
 
     JSAutoRequest ar(cx);
 
     JSString *jsstr;
     mozilla::Maybe<JSAutoCompartment> ac;
-    JS::AutoEnterScriptCompartment asc;
     if (fun) {
         ac.construct(cx, JS_GetFunctionObject(fun));
         jsstr = JS_DecompileFunction (cx, fun, 4);
     } else {
         JSScript *script = JSD_GetJSScript (mCx, mScript);
-        if (!asc.enter(cx, script))
-            return NS_ERROR_FAILURE;
+        ac.construct(cx, script);
         jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
     }
     if (!jsstr)
         return NS_ERROR_FAILURE;
 
     size_t length;
     const jschar *chars = JS_GetStringCharsZAndLength(cx, jsstr, &length);
     if (!chars)
--- a/js/src/jsapi-tests/tests.cpp
+++ b/js/src/jsapi-tests/tests.cpp
@@ -22,18 +22,18 @@ bool JSAPITest::init()
         return false;
 #ifdef JS_GC_ZEAL
     JS_SetGCZeal(cx, 0, 0);
 #endif
     JS_BeginRequest(cx);
     JS::RootedObject global(cx, createGlobal());
     if (!global)
         return false;
-    call = JS_EnterCrossCompartmentCall(cx, global);
-    return call != NULL;
+    oldCompartment = JS_EnterCompartment(cx, global);
+    return oldCompartment != NULL;
 }
 
 bool JSAPITest::exec(const char *bytes, const char *filename, int lineno)
 {
     JS::RootedValue v(cx);
     JS::HandleObject global = JS::HandleObject::fromMarkedLocation(&this->global);
     return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, v.address()) ||
         fail(bytes, filename, lineno);
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -52,31 +52,31 @@ class JSAPITest
     static JSAPITest *list;
     JSAPITest *next;
 
     JSRuntime *rt;
     JSContext *cx;
     JSObject *global;
     bool knownFail;
     JSAPITestString msgs;
-    JSCrossCompartmentCall *call;
+    JSCompartment *oldCompartment;
 
-    JSAPITest() : rt(NULL), cx(NULL), global(NULL), knownFail(false), call(NULL) {
+    JSAPITest() : rt(NULL), cx(NULL), global(NULL), knownFail(false), oldCompartment(NULL) {
         next = list;
         list = this;
     }
 
     virtual ~JSAPITest() { uninit(); }
 
     virtual bool init();
 
     virtual void uninit() {
-        if (call) {
-            JS_LeaveCrossCompartmentCall(call);
-            call = NULL;
+        if (oldCompartment) {
+            JS_LeaveCompartment(cx, oldCompartment);
+            oldCompartment = NULL;
         }
         if (cx) {
             JS_RemoveObjectRoot(cx, &global);
             JS_EndRequest(cx);
             JS_DestroyContext(cx);
             cx = NULL;
         }
         if (rt) {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1431,103 +1431,73 @@ 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);
-
-    JSCrossCompartmentCall *call = OffTheBooks::new_<JSCrossCompartmentCall>();
-    if (!call)
-        return NULL;
-
-    call->context = cx;
-    call->oldCompartment = cx->compartment;
-
+JS_PUBLIC_API(JSCompartment *)
+JS_EnterCompartment(JSContext *cx, JSRawObject target)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+
+    JSCompartment *oldCompartment = cx->compartment;
     cx->enterCompartment(target->compartment());
-    return call;
-}
-
-JS_PUBLIC_API(JSCrossCompartmentCall *)
-JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target)
+    return oldCompartment;
+}
+
+JS_PUBLIC_API(JSCompartment *)
+JS_EnterCompartmentOfScript(JSContext *cx, JSScript *target)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     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);
+    return JS_EnterCompartment(cx, &global);
 }
 
 JS_PUBLIC_API(void)
-JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call)
-{
-    AssertHeapIsIdle(call->context);
-    CHECK_REQUEST(call->context);
-    call->context->leaveCompartment(call->oldCompartment);
-    Foreground::delete_(call);
+JS_LeaveCompartment(JSContext *cx, JSCompartment *oldCompartment)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    cx->leaveCompartment(oldCompartment);
 }
 
 JSAutoCompartment::JSAutoCompartment(JSContext *cx, JSRawObject target)
   : cx_(cx),
     oldCompartment_(cx->compartment)
 {
     AssertHeapIsIdleOrIterating(cx_);
     cx_->enterCompartment(target->compartment());
 }
 
+JSAutoCompartment::JSAutoCompartment(JSContext *cx, JSScript *target)
+  : cx_(cx),
+    oldCompartment_(cx->compartment)
+{
+    AssertHeapIsIdleOrIterating(cx_);
+    cx_->enterCompartment(target->compartment());
+}
+
+JSAutoCompartment::JSAutoCompartment(JSContext *cx, JSStackFrame *target)
+  : cx_(cx),
+    oldCompartment_(cx->compartment)
+{
+    AssertHeapIsIdleOrIterating(cx_);
+    cx_->enterCompartment(Valueify(target)->global().compartment());
+}
+
 JSAutoCompartment::~JSAutoCompartment()
 {
     cx_->leaveCompartment(oldCompartment_);
 }
 
-bool
-AutoEnterScriptCompartment::enter(JSContext *cx, JSScript *target)
-{
-    JS_ASSERT(!call);
-    if (cx->compartment == target->compartment()) {
-        call = reinterpret_cast<JSCrossCompartmentCall*>(1);
-        return true;
-    }
-    call = JS_EnterCrossCompartmentCallScript(cx, target);
-    return call != NULL;
-}
-
-bool
-AutoEnterFrameCompartment::enter(JSContext *cx, JSStackFrame *target)
-{
-    JS_ASSERT(!call);
-    if (cx->compartment == Valueify(target)->scopeChain()->compartment()) {
-        call = reinterpret_cast<JSCrossCompartmentCall*>(1);
-        return true;
-    }
-    call = JS_EnterCrossCompartmentCallStackFrame(cx, target);
-    return call != NULL;
-}
-
 JS_PUBLIC_API(void)
 JS_SetCompartmentPrivate(JSCompartment *compartment, void *data)
 {
     compartment->data = data;
 }
 
 JS_PUBLIC_API(void *)
 JS_GetCompartmentPrivate(JSCompartment *compartment)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3275,22 +3275,16 @@ extern JS_PUBLIC_API(void)
 JS_SetCompartmentNameCallback(JSRuntime *rt, JSCompartmentNameCallback callback);
 
 extern JS_PUBLIC_API(JSWrapObjectCallback)
 JS_SetWrapObjectCallbacks(JSRuntime *rt,
                           JSWrapObjectCallback callback,
                           JSSameCompartmentWrapObjectCallback sccallback,
                           JSPreWrapCallback precallback);
 
-extern JS_PUBLIC_API(JSCrossCompartmentCall *)
-JS_EnterCrossCompartmentCall(JSContext *cx, JSRawObject target);
-
-extern JS_PUBLIC_API(void)
-JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call);
-
 extern JS_PUBLIC_API(void)
 JS_SetCompartmentPrivate(JSCompartment *compartment, void *data);
 
 extern JS_PUBLIC_API(void *)
 JS_GetCompartmentPrivate(JSCompartment *compartment);
 
 extern JS_PUBLIC_API(JSBool)
 JS_WrapObject(JSContext *cx, JSObject **objp);
@@ -3306,35 +3300,75 @@ js_TransplantObjectWithWrapper(JSContext
                                JSObject *origobj,
                                JSObject *origwrapper,
                                JSObject *targetobj,
                                JSObject *targetwrapper);
 
 extern JS_PUBLIC_API(JSBool)
 JS_RefreshCrossCompartmentWrappers(JSContext *cx, JSObject *ob);
 
+/*
+ * At any time, a JSContext has a current (possibly-NULL) compartment.
+ * Compartments are described in:
+ *
+ *   developer.mozilla.org/en-US/docs/SpiderMonkey/SpiderMonkey_compartments
+ *
+ * The current compartment of a context may be changed. The preferred way to do
+ * this is with JSAutoCompartment:
+ *
+ *   void foo(JSContext *cx, JSObject *obj) {
+ *     // in some compartment 'c'
+ *     {
+ *       JSAutoCompartment ac(cx, obj);  // constructor enters
+ *       // in the compartment of 'obj'
+ *     }                                 // destructor leaves
+ *     // back in compartment 'c'
+ *   }
+ *
+ * For more complicated uses that don't neatly fit in a C++ stack frame, the
+ * compartment can entered and left using separate function calls:
+ *
+ *   void foo(JSContext *cx, JSObject *obj) {
+ *     // in 'oldCompartment'
+ *     JSCompartment *oldCompartment = JS_EnterCompartment(cx, obj);
+ *     // in the compartment of 'obj'
+ *     JS_LeaveCompartment(cx, oldCompartment);
+ *     // back in 'oldCompartment'
+ *   }
+ *
+ * Note: these calls must still execute in a LIFO manner w.r.t all other
+ * enter/leave calls on the context. Furthermore, only the return value of a
+ * JS_EnterCompartment call may be passed as the 'oldCompartment' argument of
+ * the corresponding JS_LeaveCompartment call.
+ */
+
 #ifdef __cplusplus
 JS_END_EXTERN_C
 
-namespace js {
-class AutoCompartment;
-}
-
 class JS_PUBLIC_API(JSAutoCompartment)
 {
     JSContext *cx_;
     JSCompartment *oldCompartment_;
   public:
     JSAutoCompartment(JSContext *cx, JSRawObject target);
+    JSAutoCompartment(JSContext *cx, JSScript *target);
+    JSAutoCompartment(JSContext *cx, JSStackFrame *target);
     ~JSAutoCompartment();
 };
 
 JS_BEGIN_EXTERN_C
 #endif
 
+/* NB: This API is infallible; a NULL return value does not indicate error. */
+extern JS_PUBLIC_API(JSCompartment *)
+JS_EnterCompartment(JSContext *cx, JSRawObject target);
+
+extern JS_PUBLIC_API(void)
+JS_LeaveCompartment(JSContext *cx, JSCompartment *oldCompartment);
+
 typedef void (*JSIterateCompartmentCallback)(JSRuntime *rt, void *data, JSCompartment *compartment);
 
 /*
  * This function calls |compartmentCallback| on every compartment.  Beware that
  * there is no guarantee that the compartment will survive after the callback
  * returns.
  */
 extern JS_PUBLIC_API(void)
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -8,64 +8,29 @@
 #ifndef jsdbgapi_h___
 #define jsdbgapi_h___
 /*
  * JS debugger API.
  */
 #include "jsapi.h"
 #include "jsprvtd.h"
 
-JS_BEGIN_EXTERN_C
-
-extern JS_PUBLIC_API(JSCrossCompartmentCall *)
-JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target);
-
-extern JS_PUBLIC_API(JSCrossCompartmentCall *)
-JS_EnterCrossCompartmentCallStackFrame(JSContext *cx, JSStackFrame *target);
-
-#ifdef __cplusplus
-JS_END_EXTERN_C
-
-namespace JS {
-
-class JS_PUBLIC_API(AutoEnterScriptCompartment)
-{
-  protected:
-    JSCrossCompartmentCall *call;
-
-  public:
-    AutoEnterScriptCompartment() : call(NULL) {}
-
-    bool enter(JSContext *cx, JSScript *target);
-
-    bool entered() const { return call != NULL; }
-
-    ~AutoEnterScriptCompartment() {
-        if (call && call != reinterpret_cast<JSCrossCompartmentCall*>(1))
-            JS_LeaveCrossCompartmentCall(call);
-    }
-};
-
-class JS_PUBLIC_API(AutoEnterFrameCompartment) : public AutoEnterScriptCompartment
-{
-  public:
-    bool enter(JSContext *cx, JSStackFrame *target);
-};
-
-} /* namespace JS */
-
-#ifdef DEBUG
+#if defined(__cplusplus) && defined(DEBUG)
 JS_FRIEND_API(void) js_DumpValue(const js::Value &val);
 JS_FRIEND_API(void) js_DumpId(jsid id);
 JS_FRIEND_API(void) js_DumpStackFrame(JSContext *cx, js::StackFrame *start = NULL);
 #endif
-JS_FRIEND_API(void) js_DumpBacktrace(JSContext *cx);
 
 JS_BEGIN_EXTERN_C
-#endif
+
+JS_FRIEND_API(void)
+js_DumpBacktrace(JSContext *cx);
+
+extern JS_PUBLIC_API(JSCompartment *)
+JS_EnterCompartmentOfScript(JSContext *cx, JSScript *target);
 
 extern JS_PUBLIC_API(JSString *)
 JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, unsigned indent);
 
 /*
  * Currently, we only support runtime-wide debugging. In the future, we should
  * be able to support compartment-wide debugging.
  */
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -241,23 +241,16 @@ JSFunctionSpec intrinsic_functions[] = {
 JSObject *
 GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
 {
     Rooted<GlobalObject*> self(cx, this);
 
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     JS_ASSERT(isNative());
 
-    /*
-     * Calling a function from a cleared global triggers this (yeah, I know).
-     * Uncomment this once bug 470510 is fixed (if that bug doesn't remove
-     * isCleared entirely).
-     */
-    // JS_ASSERT(!isCleared());
-
     cx->setDefaultCompartmentObjectIfUnset(self);
 
     RootedObject objectProto(cx);
 
     /*
      * Create |Object.prototype| first, mirroring CreateBlankProto but for the
      * prototype of the created object.
      */
--- a/js/xpconnect/src/XPCStack.cpp
+++ b/js/xpconnect/src/XPCStack.cpp
@@ -102,38 +102,36 @@ XPCJSStackFrame::CreateStack(JSContext* 
 
     nsRefPtr<XPCJSStackFrame> first = new XPCJSStackFrame();
     nsRefPtr<XPCJSStackFrame> self = first;
     while (fp && self) {
         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));
-                }
+            JSAutoCompartment ac(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;
         }
 
--- a/startupcache/test/TestStartupCache.cpp
+++ b/startupcache/test/TestStartupCache.cpp
@@ -387,21 +387,19 @@ int main(int argc, char** argv)
     JS_EnumerateStub, JS_ResolveStub,
     JS_ConvertStub
   };
   JSObject *glob = nullptr;
   if (use_js)
     glob = JS_NewGlobalObject(cx, &global_class, NULL);
   if (!glob)
     use_js = false;
-  JSCrossCompartmentCall *compartment = nullptr;
+  mozilla::Maybe<JSAutoCompartment> ac;
   if (use_js)
-    compartment = JS_EnterCrossCompartmentCall(cx, glob);
-  if (!compartment)
-    use_js = false;
+    ac.construct(cx, glob);
   if (use_js && !JS_InitStandardClasses(cx, glob))
     use_js = false;
 
   JS::AutoValueRooter before_counts(cx);
   if (use_js && !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram before test",
                                  cx, before_counts.addr()))
     use_js = false;
   
@@ -439,13 +437,10 @@ int main(int argc, char** argv)
     } else if (compare == NS_ERROR_FAILURE) {
       fail("histogram didn't record samples");
       rv = 1;
     } else {
       passed("histogram records samples");
     }
   }
 
-  if (use_js)
-    JS_LeaveCrossCompartmentCall(compartment);
-
   return rv;
 }