Bug 628758 - Various compartment and request fixes for JSD (r=luke)
authorSteve Fink <sfink@mozilla.com>
Thu, 27 Jan 2011 18:31:48 -0800
changeset 62384 87dc60c12d2499d6abe7a66510687ab9048dfd66
parent 62383 d07aab3b3b7c6be1d548a9388586a78cee358485
child 62385 410fe81fff16b4bd8eac4ddec775439a15166f02
push idunknown
push userunknown
push dateunknown
reviewersluke
bugs628758
milestone2.0b12pre
Bug 628758 - Various compartment and request fixes for JSD (r=luke)
js/jsd/jsd_scpt.c
js/jsd/jsd_val.c
js/jsd/jsd_xpc.cpp
--- a/js/jsd/jsd_scpt.c
+++ b/js/jsd/jsd_scpt.c
@@ -522,40 +522,51 @@ jsd_GetScriptLineExtent(JSDContext* jsdc
     if( NOT_SET_YET == (int)jsdscript->lineExtent )
         jsdscript->lineExtent = JS_GetScriptLineExtent(jsdc->dumbContext, jsdscript->script);
     return jsdscript->lineExtent;
 }
 
 jsuword
 jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line)
 {
+    jsuword pc;
+    JSCrossCompartmentCall *call;
+
 #ifdef LIVEWIRE
     if( jsdscript && jsdscript->lwscript )
     {
         uintN newline;
         jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline);
         if( line != newline )
             line = newline;
     }
 #endif
 
-    return (jsuword) JS_LineNumberToPC(jsdc->dumbContext, 
-                                       jsdscript->script, line );
+    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
+    if(!call)
+        return 0;
+    pc = (jsuword) JS_LineNumberToPC(jsdc->dumbContext, jsdscript->script, line );
+    JS_LeaveCrossCompartmentCall(call);
+    return pc;
 }
 
 uintN
 jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc)
 {
+    JSCrossCompartmentCall *call;
     uintN first = jsdscript->lineBase;
     uintN last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
-    uintN line = pc
-        ? JS_PCToLineNumber(jsdc->dumbContext, 
-                            jsdscript->script,
-                            (jsbytecode*)pc)
-        : 0;
+    uintN line = 0;
+
+    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
+    if(!call)
+        return 0;
+    if (pc)
+        line = JS_PCToLineNumber(jsdc->dumbContext, jsdscript->script, (jsbytecode*)pc);
+    JS_LeaveCrossCompartmentCall(call);
 
     if( line < first )
         return first;
     if( line > last )
         return last;
 
 #ifdef LIVEWIRE
     if( jsdscript && jsdscript->lwscript )
@@ -589,20 +600,25 @@ jsd_GetScriptHook(JSDContext* jsdc, JSD_
         *callerdata = jsdc->scriptHookData;
     JSD_UNLOCK();
     return JS_TRUE;
 }    
 
 JSBool
 jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable)
 {
+    JSCrossCompartmentCall *call;
     JSBool rv;
+    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
+    if(!call)
+        return JS_FALSE;
     JSD_LOCK();
     rv = JS_SetSingleStepMode(jsdc->dumbContext, jsdscript->script, enable);
     JSD_UNLOCK();
+    JS_LeaveCrossCompartmentCall(call);
     return rv;
 }
 
 
 /***************************************************************************/
 
 void
 jsd_NewScriptHookProc( 
@@ -797,16 +813,18 @@ jsd_TrapHandler(JSContext *cx, JSScript 
 JSBool
 jsd_SetExecutionHook(JSDContext*           jsdc, 
                      JSDScript*            jsdscript,
                      jsuword               pc,
                      JSD_ExecutionHookProc hook,
                      void*                 callerdata)
 {
     JSDExecHook* jsdhook;
+    JSBool rv;
+    JSCrossCompartmentCall *call;
 
     JSD_LOCK();
     if( ! hook )
     {
         jsd_ClearExecutionHook(jsdc, jsdscript, pc);
         JSD_UNLOCK();
         return JS_TRUE;
     }
@@ -826,50 +844,69 @@ jsd_SetExecutionHook(JSDContext*        
         JSD_UNLOCK();
         return JS_FALSE;
     }
     jsdhook->jsdscript  = jsdscript;
     jsdhook->pc         = pc;
     jsdhook->hook       = hook;
     jsdhook->callerdata = callerdata;
 
-    if( ! JS_SetTrap(jsdc->dumbContext, jsdscript->script, 
-                     (jsbytecode*)pc, jsd_TrapHandler,
-                     PRIVATE_TO_JSVAL(jsdhook)) )
-    {
+    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
+    if(!call) {
+        free(jsdhook);
+        JSD_UNLOCK();
+        return JS_FALSE;
+    }
+
+    rv = JS_SetTrap(jsdc->dumbContext, jsdscript->script, 
+                    (jsbytecode*)pc, jsd_TrapHandler,
+                    PRIVATE_TO_JSVAL(jsdhook));
+
+    JS_LeaveCrossCompartmentCall(call);
+
+    if ( ! rv ) {
         free(jsdhook);
         JSD_UNLOCK();
         return JS_FALSE;
     }
 
     JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
     JSD_UNLOCK();
 
     return JS_TRUE;
 }
 
 JSBool
 jsd_ClearExecutionHook(JSDContext*           jsdc, 
                        JSDScript*            jsdscript,
                        jsuword               pc)
 {
+    JSCrossCompartmentCall *call;
     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;
+    }
+
     JS_ClearTrap(jsdc->dumbContext, jsdscript->script, 
                  (jsbytecode*)pc, NULL, NULL );
 
+    JS_LeaveCrossCompartmentCall(call);
+
     JS_REMOVE_LINK(&jsdhook->links);
     free(jsdhook);
 
     JSD_UNLOCK();
     return JS_TRUE;
 }
 
 JSBool
@@ -881,16 +918,17 @@ jsd_ClearAllExecutionHooksForScript(JSDC
     JSD_LOCK();
 
     while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) )
     {
         JS_REMOVE_LINK(&jsdhook->links);
         free(jsdhook);
     }
 
+    /* No cross-compartment call here because we may be in the middle of GC */
     JS_ClearScriptTraps(jsdc->dumbContext, jsdscript->script);
     JSD_UNLOCK();
 
     return JS_TRUE;
 }
 
 JSBool
 jsd_ClearAllExecutionHooks(JSDContext* jsdc)
--- a/js/jsd/jsd_val.c
+++ b/js/jsd/jsd_val.c
@@ -206,46 +206,60 @@ jsd_GetValueDouble(JSDContext* jsdc, JSD
 }
 
 JSString*
 jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
     JSExceptionState* exceptionState;
     JSCrossCompartmentCall *call = NULL;
+    jsval stringval;
+    JSString *string;
+    JSBool needWrap;
+    JSObject *scopeObj;
 
-    if(!jsdval->string)
-    {
-        /* if the jsval is a string, then we don't need to double root it */
-        if(JSVAL_IS_STRING(jsdval->val))
-            jsdval->string = JSVAL_TO_STRING(jsdval->val);
-        else
-        {
-            JS_BeginRequest(cx);
-            call = JSVAL_IS_PRIMITIVE(jsdval->val)
-                   ? NULL
-                   : JS_EnterCrossCompartmentCall(jsdc->dumbContext, JSVAL_TO_OBJECT(jsdval->val));
-            if(!call) {
-                JS_EndRequest(cx);
+    if(jsdval->string)
+        return jsdval->string;
+
+    /* Reuse the string without copying or re-rooting it */
+    if(JSVAL_IS_STRING(jsdval->val)) {
+        jsdval->string = JSVAL_TO_STRING(jsdval->val);
+        return jsdval->string;
+    }
+
+    JS_BeginRequest(cx);
 
-                return NULL;
-            }
+    /* Objects call JS_ValueToString in their own compartment. */
+    scopeObj = JSVAL_IS_OBJECT(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob;
+    call = JS_EnterCrossCompartmentCall(cx, scopeObj);
+    if(!call) {
+        JS_EndRequest(cx);
+        return NULL;
+    }
+    exceptionState = JS_SaveExceptionState(cx);
+
+    string = JS_ValueToString(cx, jsdval->val);
+
+    JS_RestoreExceptionState(cx, exceptionState);
+    JS_LeaveCrossCompartmentCall(call);
 
-            exceptionState = JS_SaveExceptionState(cx);
-            jsdval->string = JS_ValueToString(cx, jsdval->val);
-            JS_RestoreExceptionState(cx, exceptionState);
-            if(jsdval->string)
-            {
-                if(!JS_AddNamedStringRoot(cx, &jsdval->string, "ValueString"))
-                    jsdval->string = NULL;
-            }
-            JS_LeaveCrossCompartmentCall(call);
-            JS_EndRequest(cx);
-        }
+    stringval = STRING_TO_JSVAL(string);
+    call = JS_EnterCrossCompartmentCall(cx, jsdc->glob);
+    if(!call || !JS_WrapValue(cx, &stringval)) {
+        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_EndRequest(cx);
+
     return jsdval->string;
 }
 
 JSString*
 jsd_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
     JSFunction* fun;
@@ -276,38 +290,49 @@ jsd_GetValueFunctionId(JSDContext* jsdc,
         if (!jsdval->funName)
             jsdval->funName = JS_GetAnonymousString(jsdc->jsrt);
     }
     return jsdval->funName;
 }
 
 /***************************************************************************/
 
+/*
+ * 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;
 
     if(!(jsdval = (JSDValue*) calloc(1, sizeof(JSDValue))))
         return NULL;
 
     if(JSVAL_IS_GCTHING(val))
     {
-        JSBool ok = JS_FALSE;
+        JSBool ok;
         JS_BeginRequest(jsdc->dumbContext);
 
         call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
         if(!call) {
             JS_EndRequest(jsdc->dumbContext);
             free(jsdval);
             return NULL;
         }
 
         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_EndRequest(jsdc->dumbContext);
         if(!ok)
         {
             free(jsdval);
             return NULL;
         }
     }
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -1035,73 +1035,70 @@ jsdScript::~jsdScript ()
  */
 PCMapEntry *
 jsdScript::CreatePPLineMap()
 {    
     JSContext  *cx  = JSD_GetDefaultJSContext (mCx);
     JSAutoRequest ar(cx);
     JSObject   *obj = JS_NewObject(cx, NULL, NULL, NULL);
     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
-    JSScript   *script;
-    JSString   *jsstr;
+    JSScript   *script; /* In JSD compartment */
     PRUint32    baseLine;
     PRBool      scriptOwner = PR_FALSE;
+    JSString   *jsstr;
+    size_t      length;
+    const jschar *chars;
     
     if (fun) {
         uintN nargs;
 
-        /* Enter a new block so we can leave before the end of this block */
-        do {
+        {
             JSAutoEnterCompartment ac;
             if (!ac.enter(cx, JS_GetFunctionObject(fun)))
                 return nsnull;
 
             nargs = JS_GetFunctionArgumentCount(cx, fun);
             if (nargs > 12)
                 return nsnull;
             jsstr = JS_DecompileFunctionBody (cx, fun, 4);
             if (!jsstr)
                 return nsnull;
-        } while(false);
-
-        size_t length;
-        const jschar *chars = JS_GetStringCharsAndLength(cx, jsstr, &length);
-        if (!chars)
-            return nsnull;
-    
+
+            if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length)))
+                return nsnull;
+        }
+
+        JS::Anchor<JSString *> kungFuDeathGrip(jsstr);
         const char *argnames[] = {"arg1", "arg2", "arg3", "arg4", 
                                   "arg5", "arg6", "arg7", "arg8",
                                   "arg9", "arg10", "arg11", "arg12" };
         fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames, chars,
                                     length, "x-jsd:ppbuffer?type=function", 3);
         if (!fun || !(script = JS_GetFunctionScript(cx, fun)))
             return nsnull;
         baseLine = 3;
     } else {
-        /* Enter a new block so we can leave before the end of this block */
-        do {
-            script = JSD_GetJSScript(mCx, mScript);
-
+        script = JSD_GetJSScript(mCx, mScript);
+        JSString *jsstr;
+
+        {
             JSAutoEnterCompartment ac;
             if (!ac.enter(cx, script))
                 return nsnull;
 
-            jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript),
-                                        "ppscript", 4);
+            jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript), "ppscript", 4);
             if (!jsstr)
                 return nsnull;
-        } while(false);
-
-        size_t length;
-        const jschar *chars = JS_GetStringCharsAndLength(cx, jsstr, &length);
-        if (!chars)
-            return nsnull;
-
-        script = JS_CompileUCScript (cx, obj, chars, length,
-                                     "x-jsd:ppbuffer?type=script", 1);
+
+            if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length)))
+                return nsnull;
+        }
+
+        JS::Anchor<JSString *> kungFuDeathGrip(jsstr);
+        script = JS_CompileUCScript (cx, obj, chars, length, "x-jsd:ppbuffer?type=script", 1);
         if (!script)
             return nsnull;
         scriptOwner = PR_TRUE;
         baseLine = 1;
     }
 
     PRUint32 scriptExtent = JS_GetScriptLineExtent (cx, script);
     jsbytecode* firstPC = JS_LineNumberToPC (cx, script, 0);
@@ -1185,16 +1182,19 @@ jsdScript::GetJSDScript(JSDScript **_rva
 }
 
 NS_IMETHODIMP
 jsdScript::GetVersion (PRInt32 *_rval)
 {
     ASSERT_VALID_EPHEMERAL;
     JSContext *cx = JSD_GetDefaultJSContext (mCx);
     JSScript *script = JSD_GetJSScript(mCx, mScript);
+    JSAutoEnterCompartment ac;
+    if (!ac.enter(cx, script))
+        return NS_ERROR_FAILURE;
     *_rval = static_cast<PRInt32>(JS_GetScriptVersion(cx, script));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdScript::GetTag(PRUint32 *_rval)
 {
     if (!mTag)
@@ -1283,16 +1283,19 @@ jsdScript::GetParameterNames(PRUint32* c
     JSContext *cx = JSD_GetDefaultJSContext (mCx);
     if (!cx) {
         NS_WARNING("No default context !?");
         return NS_ERROR_FAILURE;
     }
     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
 
     JSAutoRequest ar(cx);
+    JSAutoEnterCompartment ac;
+    if (!ac.enter(cx, JS_GetFunctionObject(fun)))
+        return NS_ERROR_FAILURE;
 
     uintN nargs;
     if (!fun ||
         !JS_FunctionHasLocalNames(cx, fun) ||
         (nargs = JS_GetFunctionArgumentCount(cx, fun)) == 0) {
         *count = 0;
         *paramNames = nsnull;
         return NS_OK;