Bug 593147 - TM: global Object created in _newJSDContext needs a compartment. r=gal.
authorJason Orendorff <jorendorff@mozilla.com>
Tue, 14 Sep 2010 16:24:59 -0700
changeset 54154 35e5647f070a56000dc6b1c6835c4c843e966f6a
parent 54153 84b4d4856e1eaab0ccc7ba6184a977ab44ef5ab5
child 54156 219353d3eb4ce7a23855442c7ee881b464f6bc79
push idunknown
push userunknown
push dateunknown
reviewersgal
bugs593147
milestone2.0b6pre
Bug 593147 - TM: global Object created in _newJSDContext needs a compartment. r=gal.
js/jsd/jsd.h
js/jsd/jsd_high.c
js/jsd/jsdebug.c
js/jsd/jsdebug.h
js/src/jsapi.cpp
js/src/jsapi.h
--- a/js/jsd/jsd.h
+++ b/js/jsd/jsd.h
@@ -335,17 +335,19 @@ extern void JSD_ASSERT_VALID_OBJECT(JSDO
 #endif
 
 /***************************************************************************/
 /* higher level functions */
 
 extern JSDContext*
 jsd_DebuggerOnForUser(JSRuntime*         jsrt,
                       JSD_UserCallbacks* callbacks,
-                      void*              user);
+                      void*              user,
+                      JSObject*          scopeobj);
+
 extern JSDContext*
 jsd_DebuggerOn(void);
 
 extern void
 jsd_DebuggerOff(JSDContext* jsdc);
 
 extern void
 jsd_DebuggerPause(JSDContext* jsdc, JSBool forceAllHooksOff);
--- a/js/jsd/jsd_high.c
+++ b/js/jsd/jsd_high.c
@@ -81,19 +81,21 @@ static JSBool
 {
     return !callbacks ||
            (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks));
 }    
 
 static JSDContext*
 _newJSDContext(JSRuntime*         jsrt, 
                JSD_UserCallbacks* callbacks, 
-               void*              user)
+               void*              user,
+               JSObject*          scopeobj)
 {
     JSDContext* jsdc = NULL;
+    JSCompartment *compartment;
 
     if( ! jsrt )
         return NULL;
 
     if( ! _validateUserCallbacks(callbacks) )
         return NULL;
 
     jsdc = (JSDContext*) calloc(1, sizeof(JSDContext));
@@ -132,17 +134,21 @@ static JSDContext*
         goto label_newJSDContext_failure;
 
     jsdc->dumbContext = JS_NewContext(jsdc->jsrt, 256);
     if( ! jsdc->dumbContext )
         goto label_newJSDContext_failure;
 
     JS_BeginRequest(jsdc->dumbContext);
 
+    if( scopeobj )
+        compartment = js_SwitchToObjectCompartment(jsdc->dumbContext, scopeobj);
     jsdc->glob = JS_NewGlobalObject(jsdc->dumbContext, &global_class);
+    if( scopeobj )
+        js_SwitchToCompartment(jsdc->dumbContext, compartment);
     if( ! jsdc->glob )
         goto label_newJSDContext_failure;
 
     if( ! JS_InitStandardClasses(jsdc->dumbContext, jsdc->glob) )
         goto label_newJSDContext_failure;
 
     JS_EndRequest(jsdc->dumbContext);
 
@@ -189,22 +195,23 @@ static void
     jsdc->dumbContext = NULL;
 }
 
 /***************************************************************************/
 
 JSDContext*
 jsd_DebuggerOnForUser(JSRuntime*         jsrt, 
                       JSD_UserCallbacks* callbacks, 
-                      void*              user)
+                      void*              user,
+                      JSObject*          scopeobj)
 {
     JSDContext* jsdc;
     JSContext* iter = NULL;
 
-    jsdc = _newJSDContext(jsrt, callbacks, user);
+    jsdc = _newJSDContext(jsrt, callbacks, user, scopeobj);
     if( ! jsdc )
         return NULL;
 
     /*
      * Set hooks here.  The new/destroy script hooks are on even when
      * the debugger is paused.  The destroy hook so we'll clean up
      * internal data structures when scripts are destroyed, and the
      * newscript hook for backwards compatibility for now.  We'd like
@@ -221,17 +228,17 @@ jsd_DebuggerOnForUser(JSRuntime*        
     return jsdc;
 }
 
 JSDContext*
 jsd_DebuggerOn(void)
 {
     JS_ASSERT(_jsrt);
     JS_ASSERT(_validateUserCallbacks(&_callbacks));
-    return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user);
+    return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user, NULL);
 }
 
 void
 jsd_DebuggerOff(JSDContext* jsdc)
 {
     jsd_DebuggerPause(jsdc, JS_TRUE);
     /* clear hooks here */
     JS_SetNewScriptHookProc(jsdc->jsrt, NULL, NULL);
--- a/js/jsd/jsdebug.c
+++ b/js/jsd/jsdebug.c
@@ -44,17 +44,17 @@
 /***************************************************************************/
 /* High Level calls */
 
 JSD_PUBLIC_API(JSDContext*)
 JSD_DebuggerOnForUser(JSRuntime*         jsrt,
                       JSD_UserCallbacks* callbacks,
                       void*              user)
 {
-    return jsd_DebuggerOnForUser(jsrt, callbacks, user);
+    return jsd_DebuggerOnForUser(jsrt, callbacks, user, NULL);
 }
 
 JSD_PUBLIC_API(JSDContext*)
 JSD_DebuggerOn(void)
 {
     return jsd_DebuggerOn();
 }
 
--- a/js/jsd/jsdebug.h
+++ b/js/jsd/jsdebug.h
@@ -138,16 +138,26 @@ JSD_DebuggerOn(void);
 * Startup JSD on a particular JSRuntime with (optional) callbacks
 */
 extern JSD_PUBLIC_API(JSDContext*)
 JSD_DebuggerOnForUser(JSRuntime*         jsrt,
                       JSD_UserCallbacks* callbacks,
                       void*              user);
 
 /*
+ * Startup JSD in an application that uses compartments. Debugger
+ * objects will be allocated in the same compartment as scopeobj.
+ */
+extern JSD_PUBLIC_API(JSDContext*)
+JSD_DebuggerOnForUserWithCompartment(JSRuntime*         jsrt,
+                                     JSD_UserCallbacks* callbacks,
+                                     void*              user,
+                                     JSObject*          scopeobj);
+
+/*
 * Shutdown JSD for this JSDContext
 */
 extern JSD_PUBLIC_API(void)
 JSD_DebuggerOff(JSDContext* jsdc);
 
 /*
  * Pause JSD for this JSDContext
  */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1180,32 +1180,30 @@ JSAutoCrossCompartmentCall::enter(JSCont
 {
     JS_ASSERT(!call);
     if (cx->compartment == target->getCompartment(cx))
         return true;
     call = JS_EnterCrossCompartmentCall(cx, target);
     return call != NULL;
 }
 
-JSAutoEnterCompartment::JSAutoEnterCompartment(JSContext *cx,
-                                               JSCompartment *newCompartment)
-  : cx(cx), compartment(cx->compartment)
-{
-    cx->compartment = newCompartment;
-}
-
-JSAutoEnterCompartment::JSAutoEnterCompartment(JSContext *cx, JSObject *target)
-  : cx(cx), compartment(cx->compartment)
-{
-    cx->compartment = target->getCompartment(cx);
-}
-
-JSAutoEnterCompartment::~JSAutoEnterCompartment()
-{
+JS_FRIEND_API(JSCompartment *)
+js_SwitchToCompartment(JSContext *cx, JSCompartment *compartment)
+{
+    JSCompartment *c = cx->compartment;
     cx->compartment = compartment;
+    return c;
+}
+
+JS_FRIEND_API(JSCompartment *)
+js_SwitchToObjectCompartment(JSContext *cx, JSObject *obj)
+{
+    JSCompartment *c = cx->compartment;
+    cx->compartment = obj->getCompartment(cx);
+    return c;
 }
 
 JS_PUBLIC_API(void *)
 JS_SetCompartmentPrivate(JSContext *cx, JSCompartment *compartment, void *data)
 {
     CHECK_REQUEST(cx);
     void *old = compartment->data;
     compartment->data = data;
@@ -1240,17 +1238,18 @@ JS_GetGlobalObject(JSContext *cx)
 }
 
 JS_PUBLIC_API(void)
 JS_SetGlobalObject(JSContext *cx, JSObject *obj)
 {
     CHECK_REQUEST(cx);
 
     cx->globalObject = obj;
-    cx->compartment = obj ? obj->getCompartment(cx) : cx->runtime->defaultCompartment;
+    if (!cx->maybefp())
+        cx->compartment = obj ? obj->getCompartment(cx) : cx->runtime->defaultCompartment;
 }
 
 class AutoResolvingEntry {
 public:
     AutoResolvingEntry() : entry(NULL) {}
 
     /*
      * Returns false on error. But N.B. if obj[id] was already being resolved,
@@ -1329,20 +1328,24 @@ js_InitFunctionAndObjectClasses(JSContex
     return fun_proto;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_InitStandardClasses(JSContext *cx, JSObject *obj)
 {
     CHECK_REQUEST(cx);
 
-    if (cx->globalObject)
-        assertSameCompartment(cx, obj);
-    else
+    /*
+     * JS_SetGlobalObject might or might not change cx's compartment, so call
+     * it before assertSameCompartment. (The API contract is that *after* this,
+     * cx and obj must be in the same compartment.)
+     */
+    if (!cx->globalObject)
         JS_SetGlobalObject(cx, obj);
+    assertSameCompartment(cx, obj);
 
     /* Define a top-level property 'undefined' with the undefined value. */
     JSAtom *atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
     if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), UndefinedValue(),
                              PropertyStub, PropertyStub,
                              JSPROP_PERMANENT | JSPROP_READONLY)) {
         return JS_FALSE;
     }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -952,16 +952,22 @@ extern JS_PUBLIC_API(void *)
 JS_GetCompartmentPrivate(JSContext *cx, JSCompartment *compartment);
 
 extern JS_PUBLIC_API(JSBool)
 JS_RewrapObject(JSContext *cx, JSObject **objp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_RewrapValue(JSContext *cx, jsval *p);
 
+extern JS_FRIEND_API(JSCompartment *)
+js_SwitchToCompartment(JSContext *cx, JSCompartment *compartment);
+
+extern JS_FRIEND_API(JSCompartment *)
+js_SwitchToObjectCompartment(JSContext *cx, JSObject *obj);
+
 #ifdef __cplusplus
 JS_END_EXTERN_C
 
 class JS_PUBLIC_API(JSAutoCrossCompartmentCall)
 {
     JSCrossCompartmentCall *call;
   public:
     JSAutoCrossCompartmentCall() : call(NULL) {}
@@ -977,24 +983,30 @@ class JS_PUBLIC_API(JSAutoCrossCompartme
 
     void swap(JSAutoCrossCompartmentCall &other) {
         JSCrossCompartmentCall *tmp = call;
         call = other.call;
         other.call = tmp;
     }
 };
 
-class JS_FRIEND_API(JSAutoEnterCompartment)
+class JSAutoEnterCompartment
 {
     JSContext *cx;
     JSCompartment *compartment;
   public:
-    JSAutoEnterCompartment(JSContext *cx, JSCompartment *newCompartment);
-    JSAutoEnterCompartment(JSContext *cx, JSObject *target);
-    ~JSAutoEnterCompartment();
+    JSAutoEnterCompartment(JSContext *cx, JSCompartment *newCompartment) : cx(cx) {
+        compartment = js_SwitchToCompartment(cx, newCompartment);
+    }
+    JSAutoEnterCompartment(JSContext *cx, JSObject *target) : cx(cx) {
+        compartment = js_SwitchToObjectCompartment(cx, target);
+    }
+    ~JSAutoEnterCompartment() {
+        js_SwitchToCompartment(cx, compartment);
+    }
 };
 
 JS_BEGIN_EXTERN_C
 #endif
 
 extern JS_PUBLIC_API(JSObject *)
 JS_GetGlobalObject(JSContext *cx);