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 109929 d65863fbb129f3403d80fba6a508e5558f42509f
parent 109928 cc8bc4e1bdfcaab199e7eb733de0c3bba0d261a8
child 109930 8bdeda83f7b1be7264ed1d2476c6bf432e20efe2
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs786068
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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;
 }