Back to index

lightning-sunbird  0.9+nobinonly
PyISupports.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Python XPCOM language bindings.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * ActiveState Tool Corp.
00018  * Portions created by the Initial Developer are Copyright (C) 2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Mark Hammond <mhammond@skippinet.com.au> (original author)
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 //
00039 // This code is part of the XPCOM extensions for Python.
00040 //
00041 // Written May 2000 by Mark Hammond.
00042 //
00043 // Based heavily on the Python COM support, which is
00044 // (c) Mark Hammond and Greg Stein.
00045 //
00046 // (c) 2000, ActiveState corp.
00047 
00048 #include "PyXPCOM_std.h"
00049 
00050 static PRInt32 cInterfaces=0;
00051 static PyObject *g_obFuncMakeInterfaceCount = NULL; // XXX - never released!!!
00052 
00053 PRInt32 
00054 _PyXPCOM_GetInterfaceCount(void)
00055 {
00056        return cInterfaces;
00057 }
00058 
00059 Py_nsISupports::Py_nsISupports(nsISupports *punk, const nsIID &iid, PyTypeObject *this_type)
00060 {
00061        ob_type = this_type;
00062        m_obj = punk;
00063        m_iid = iid;
00064        // refcnt of object managed by caller.
00065        PR_AtomicIncrement(&cInterfaces);
00066        PyXPCOM_DLLAddRef();
00067        _Py_NewReference(this);
00068 }
00069 
00070 Py_nsISupports::~Py_nsISupports()
00071 {
00072        SafeRelease(this);   
00073        PR_AtomicDecrement(&cInterfaces);
00074        PyXPCOM_DLLRelease();
00075 }
00076 
00077 /*static*/ nsISupports *
00078 Py_nsISupports::GetI(PyObject *self, nsIID *ret_iid)
00079 {
00080        if (self==NULL) {
00081               PyErr_SetString(PyExc_ValueError, "The Python object is invalid");
00082               return NULL;
00083        }
00084        Py_nsISupports *pis = (Py_nsISupports *)self;
00085        if (pis->m_obj==NULL) {
00086               // This should never be able to happen.
00087               PyErr_SetString(PyExc_ValueError, "Internal Error - The XPCOM object has been released.");
00088               return NULL;
00089        }
00090        if (ret_iid)
00091               *ret_iid = pis->m_iid;
00092        return pis->m_obj;
00093 }
00094 
00095 /*static*/ void
00096 Py_nsISupports::SafeRelease(Py_nsISupports *ob)
00097 {
00098        if (!ob)
00099               return;
00100        if (ob->m_obj)
00101        {
00102               long rcnt;
00103               Py_BEGIN_ALLOW_THREADS;
00104               rcnt = ob->m_obj->Release();
00105               Py_END_ALLOW_THREADS;
00106 
00107 #ifdef _DEBUG_LIFETIMES
00108               LogF(buf, "   SafeRelease(%ld) -> %s at 0x%0lx, nsISupports at 0x%0lx - Release() returned %ld",GetCurrentThreadId(), ob->ob_type->tp_name,ob, ob->m_obj,rcnt);
00109 #endif
00110               ob->m_obj = NULL;
00111        }
00112 }
00113 
00114 /* virtual */ PyObject *
00115 Py_nsISupports::getattr(const char *name)
00116 {
00117        if (strcmp(name, "IID")==0)
00118               return Py_nsIID::PyObjectFromIID( m_iid );
00119 
00120        PyXPCOM_TypeObject *this_type = (PyXPCOM_TypeObject *)ob_type;
00121        return Py_FindMethodInChain(&this_type->chain, this, (char *)name);
00122 }
00123 
00124 /* virtual */ int
00125 Py_nsISupports::setattr(const char *name, PyObject *v)
00126 {
00127        char buf[128];
00128        sprintf(buf, "%s has read-only attributes", ob_type->tp_name );
00129        PyErr_SetString(PyExc_TypeError, buf);
00130        return -1;
00131 }
00132 
00133 /*static*/ Py_nsISupports *
00134 Py_nsISupports::Constructor(nsISupports *pInitObj, const nsIID &iid)
00135 {
00136        return new Py_nsISupports(pInitObj, 
00137                                    iid, 
00138                                    type);
00139 }
00140 
00141 PRBool
00142 Py_nsISupports::InterfaceFromPyISupports(PyObject *ob, 
00143                                          const nsIID &iid, 
00144                                          nsISupports **ppv)
00145 {
00146        nsISupports *pis;
00147        PRBool rc = PR_FALSE;
00148        if ( !Check(ob) )
00149        {
00150               PyErr_Format(PyExc_TypeError, "Objects of type '%s' can not be used as COM objects", ob->ob_type->tp_name);
00151               goto done;
00152        }
00153        nsIID already_iid;
00154        pis = GetI(ob, &already_iid);
00155        if ( !pis )
00156               goto done;    /* exception was set by GetI() */
00157        /* note: we don't (yet) explicitly hold a reference to pis */
00158        if (iid.Equals(Py_nsIID_NULL)) {
00159               // a bit of a hack - we are asking for the arbitary interface
00160               // wrapped by this object, not some other specific interface - 
00161               // so no QI, just an AddRef();
00162               Py_BEGIN_ALLOW_THREADS
00163               pis->AddRef();
00164               Py_END_ALLOW_THREADS
00165               *ppv = pis;
00166        } else {
00167               // specific interface requested - if it is not already the
00168               // specific interface, QI for it and discard pis.
00169               if (iid.Equals(already_iid)) {
00170                      *ppv = pis;
00171                      pis->AddRef();
00172               } else {
00173                      nsresult r;
00174                      Py_BEGIN_ALLOW_THREADS
00175                      r = pis->QueryInterface(iid, (void **)ppv);
00176                      Py_END_ALLOW_THREADS
00177                      if ( NS_FAILED(r) )
00178                      {
00179                             PyXPCOM_BuildPyException(r);
00180                             goto done;
00181                      }
00182                      /* note: the QI added a ref for the return value */
00183               }
00184        }
00185        rc = PR_TRUE;
00186 done:
00187        return rc;
00188 }
00189 
00190 PRBool
00191 Py_nsISupports::InterfaceFromPyObject(PyObject *ob, 
00192                                       const nsIID &iid, 
00193                                       nsISupports **ppv, 
00194                                       PRBool bNoneOK,
00195                                       PRBool bTryAutoWrap /* = PR_TRUE */)
00196 {
00197        if ( ob == NULL )
00198        {
00199               // don't overwrite an error message
00200               if ( !PyErr_Occurred() )
00201                      PyErr_SetString(PyExc_TypeError, "The Python object is invalid");
00202               return PR_FALSE;
00203        }
00204        if ( ob == Py_None )
00205        {
00206               if ( bNoneOK )
00207               {
00208                      *ppv = NULL;
00209                      return PR_TRUE;
00210               }
00211               else
00212               {
00213                      PyErr_SetString(PyExc_TypeError, "None is not a invalid interface object in this context");
00214                      return PR_FALSE;
00215               }
00216        }
00217 
00218        // support nsIVariant
00219        if (iid.Equals(NS_GET_IID(nsIVariant)) || iid.Equals(NS_GET_IID(nsIWritableVariant))) {
00220               // Check it is not already nsIVariant
00221               if (PyInstance_Check(ob)) {
00222                      PyObject *sub_ob = PyObject_GetAttrString(ob, "_comobj_");
00223                      if (sub_ob==NULL) {
00224                             PyErr_Clear();
00225                      } else {
00226                             if (InterfaceFromPyISupports(sub_ob, iid, ppv)) {
00227                                    Py_DECREF(sub_ob);
00228                                    return PR_TRUE;
00229                             }
00230                             PyErr_Clear();
00231                             Py_DECREF(sub_ob);
00232                      }
00233               }
00234               *ppv = PyObject_AsVariant(ob);
00235               return *ppv != NULL;
00236        }
00237        // end of variant support.
00238 
00239        if (PyInstance_Check(ob)) {
00240               // Get the _comobj_ attribute
00241               PyObject *use_ob = PyObject_GetAttrString(ob, "_comobj_");
00242               if (use_ob==NULL) {
00243                      PyErr_Clear();
00244                      if (bTryAutoWrap)
00245                             // Try and auto-wrap it - errors will leave Py exception set,
00246                             return PyXPCOM_XPTStub::AutoWrapPythonInstance(ob, iid, ppv);
00247                      PyErr_SetString(PyExc_TypeError, "The Python instance can not be converted to an XPCOM object");
00248                      return PR_FALSE;
00249               } else
00250                      ob = use_ob;
00251 
00252        } else {
00253               Py_INCREF(ob);
00254        }
00255        PRBool rc = InterfaceFromPyISupports(ob, iid, ppv);
00256        Py_DECREF(ob);
00257        return rc;
00258 }
00259 
00260 
00261 // Interface conversions
00262 /*static*/void
00263 Py_nsISupports::RegisterInterface( const nsIID &iid, PyTypeObject *t)
00264 {
00265        if (mapIIDToType==NULL)
00266               mapIIDToType = PyDict_New();
00267 
00268        if (mapIIDToType) {
00269               PyObject *key = Py_nsIID::PyObjectFromIID(iid);
00270               if (key)
00271                      PyDict_SetItem(mapIIDToType, key, (PyObject *)t);
00272               Py_XDECREF(key);
00273        }
00274 }
00275 
00276 /*static */PyObject *
00277 Py_nsISupports::PyObjectFromInterfaceOrVariant(nsISupports *pis, 
00278                                   const nsIID &riid, 
00279                                   PRBool bAddRef, 
00280                                   PRBool bMakeNicePyObject /* = PR_TRUE */)
00281 {
00282        // Quick exit.
00283        if (pis==NULL) {
00284               Py_INCREF(Py_None);
00285               return Py_None;
00286        }
00287        if (riid.Equals(NS_GET_IID(nsIVariant))) {
00288               PyObject *ret = PyObject_FromVariant((nsIVariant *)pis);
00289               // If we were asked not to add a reference, then there
00290               // will be a spare reference on pis() - remove it.
00291               if (!bAddRef)
00292                      pis->Release();
00293               return ret;
00294        }
00295        return PyObjectFromInterface(pis, riid, bAddRef, bMakeNicePyObject);
00296 }
00297 
00298 /*static */PyObject *
00299 Py_nsISupports::PyObjectFromInterface(nsISupports *pis, 
00300                                   const nsIID &riid, 
00301                                   PRBool bAddRef, 
00302                                   PRBool bMakeNicePyObject /* = PR_TRUE */)
00303 {
00304        // Quick exit.
00305        if (pis==NULL) {
00306               Py_INCREF(Py_None);
00307               return Py_None;
00308        }
00309 
00310        // FIXME: if !bAddRef, in all error cases, do NS_RELEASE(pis) before return
00311        // since that means we have a spare reference
00312         
00313        PyTypeObject *createType = NULL;
00314        // If the IID is for nsISupports, dont bother with
00315        // a map lookup as we know the type!
00316        if (!riid.Equals(NS_GET_IID(nsISupports))) {
00317               // Look up the map
00318               PyObject *obiid = Py_nsIID::PyObjectFromIID(riid);
00319               if (!obiid) return NULL;
00320 
00321               if (mapIIDToType != NULL)
00322                      createType = (PyTypeObject *)PyDict_GetItem(mapIIDToType, obiid);
00323               Py_DECREF(obiid);
00324        }
00325        if (createType==NULL)
00326               createType = Py_nsISupports::type;
00327        // Check it is indeed one of our types.
00328        if (!PyXPCOM_TypeObject::IsType(createType)) {
00329               PyErr_SetString(PyExc_RuntimeError, "The type map is invalid");
00330               return NULL;
00331        }
00332        // we can now safely cast the thing to a PyComTypeObject and use it
00333        PyXPCOM_TypeObject *myCreateType = (PyXPCOM_TypeObject *)createType;
00334        if (myCreateType->ctor==NULL) {
00335               PyErr_SetString(PyExc_TypeError, "The type does not declare a PyCom constructor");
00336               return NULL;
00337        }
00338 
00339        Py_nsISupports *ret = (*myCreateType->ctor)(pis, riid);
00340 #ifdef _DEBUG_LIFETIMES
00341        PyXPCOM_LogF("XPCOM Object created at 0x%0xld, nsISupports at 0x%0xld",
00342               ret, ret->m_obj);
00343 #endif
00344        if (ret && bAddRef && pis) pis->AddRef();
00345        if (ret && bMakeNicePyObject)
00346               return MakeInterfaceResult(ret, riid);
00347        return ret;
00348 }
00349 
00350 // Call back into Python, passing a raw nsIInterface object, getting back
00351 // the object to actually pass to Python.
00352 PyObject *
00353 Py_nsISupports::MakeInterfaceResult(PyObject *pyis, 
00354                           const nsIID &iid)
00355 {
00356        NS_PRECONDITION(pyis, "NULL pyobject!");
00357        PyObject *obIID = NULL;
00358        PyObject *args = NULL;
00359        PyObject *mod = NULL;
00360        PyObject *ret = NULL;
00361 
00362        obIID = Py_nsIID::PyObjectFromIID(iid);
00363        if (obIID==NULL)
00364               goto done;
00365 
00366        if (g_obFuncMakeInterfaceCount==NULL) {
00367               PyObject *mod = PyImport_ImportModule("xpcom.client");
00368               if (mod) 
00369                      g_obFuncMakeInterfaceCount = PyObject_GetAttrString(mod, "MakeInterfaceResult");
00370               Py_XDECREF(mod);
00371        }
00372        if (g_obFuncMakeInterfaceCount==NULL) goto done;
00373 
00374        args = Py_BuildValue("OO", pyis, obIID);
00375        if (args==NULL) goto done;
00376        ret = PyEval_CallObject(g_obFuncMakeInterfaceCount, args);
00377 done:
00378        if (PyErr_Occurred()) {
00379               NS_ABORT_IF_FALSE(ret==NULL, "Have an error, but also a return val!");
00380               PyXPCOM_LogError("Creating an interface object to be used as a result failed\n");
00381               PyErr_Clear();
00382        }
00383        Py_XDECREF(mod);
00384        Py_XDECREF(args);
00385        Py_XDECREF(obIID);
00386        if (ret==NULL) // eek - error - return the original with no refcount mod.
00387               ret = pyis; 
00388        else
00389               // no error - decref the old object
00390               Py_DECREF(pyis);
00391        // return our obISupports.  If NULL, we are really hosed and nothing we can do.
00392        return ret;
00393 }
00394 
00395 // @pymethod <o Py_nsISupports>|Py_nsISupports|QueryInterface|Queries an object for a specific interface.
00396 PyObject *
00397 Py_nsISupports::QueryInterface(PyObject *self, PyObject *args)
00398 {
00399        PyObject *obiid;
00400        int bWrap = 1;
00401        // @pyparm IID|iid||The IID requested.
00402        // @rdesc The result is always a <o Py_nsISupports> object.
00403        // Any error (including E_NOINTERFACE) will generate a <o com_error> exception.
00404        if (!PyArg_ParseTuple(args, "O|i:QueryInterface", &obiid, &bWrap))
00405               return NULL;
00406 
00407        nsIID  iid;
00408        if (!Py_nsIID::IIDFromPyObject(obiid, &iid))
00409               return NULL;
00410 
00411        nsISupports *pMyIS = GetI(self);
00412        if (pMyIS==NULL) return NULL;
00413 
00414        nsISupports *pis;
00415        nsresult r;
00416        Py_BEGIN_ALLOW_THREADS;
00417        r = pMyIS->QueryInterface(iid, (void **)&pis);
00418        Py_END_ALLOW_THREADS;
00419 
00420        /* Note that this failure may include E_NOINTERFACE */
00421        if ( NS_FAILED(r) )
00422               return PyXPCOM_BuildPyException(r);
00423 
00424        /* Return a type based on the IID (with no extra ref) */
00425        return PyObjectFromInterface(pis, iid, PR_FALSE, (PRBool)bWrap);
00426 }
00427 
00428 
00429 // @object Py_nsISupports|The base object for all PythonCOM objects.  Wraps a COM nsISupports interface.
00430 /*static*/ struct PyMethodDef 
00431 Py_nsISupports::methods[] =
00432 {
00433        { "queryInterface", Py_nsISupports::QueryInterface, 1, "Queries the object for an interface."},
00434        { "QueryInterface", Py_nsISupports::QueryInterface, 1, "An alias for queryInterface."},
00435        {NULL}
00436 };
00437 
00438 /*static*/void Py_nsISupports::InitType(void)
00439 {
00440        type = new PyXPCOM_TypeObject(
00441               "nsISupports",
00442               NULL,
00443               sizeof(Py_nsISupports),
00444               methods,
00445               Constructor);
00446 }
00447 
00448 PyXPCOM_TypeObject *Py_nsISupports::type = NULL;
00449 PyObject *Py_nsISupports::mapIIDToType = NULL;