Back to index

lightning-sunbird  0.9+nobinonly
PyGBase.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 // PyGBase.cpp - implementation of the PyG_Base class
00039 //
00040 // This code is part of the XPCOM extensions for Python.
00041 //
00042 // Written May 2000 by Mark Hammond.
00043 //
00044 // Based heavily on the Python COM support, which is
00045 // (c) Mark Hammond and Greg Stein.
00046 //
00047 // (c) 2000, ActiveState corp.
00048 
00049 #include "PyXPCOM_std.h"
00050 #include <nsIModule.h>
00051 #include <nsIComponentLoader.h>
00052 #include <nsIInputStream.h>
00053 
00054 static PRInt32 cGateways = 0;
00055 PRInt32 _PyXPCOM_GetGatewayCount(void)
00056 {
00057        return cGateways;
00058 }
00059 
00060 extern PyG_Base *MakePyG_nsIModule(PyObject *);
00061 extern PyG_Base *MakePyG_nsIComponentLoader(PyObject *instance);
00062 extern PyG_Base *MakePyG_nsIInputStream(PyObject *instance);
00063 
00064 static char *PyXPCOM_szDefaultGatewayAttributeName = "_com_instance_default_gateway_";
00065 PyG_Base *GetDefaultGateway(PyObject *instance);
00066 void AddDefaultGateway(PyObject *instance, nsISupports *gateway);
00067 PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway);
00068 
00069 /*static*/ nsresult 
00070 PyG_Base::CreateNew(PyObject *pPyInstance, const nsIID &iid, void **ppResult)
00071 {
00072        NS_PRECONDITION(ppResult && *ppResult==NULL, "NULL or uninitialized pointer");
00073        if (ppResult==nsnull)
00074               return NS_ERROR_NULL_POINTER;
00075 
00076        PyG_Base *ret;
00077        // Hack for few extra gateways we support.
00078        if (iid.Equals(NS_GET_IID(nsIModule)))
00079               ret = MakePyG_nsIModule(pPyInstance);
00080        else if (iid.Equals(NS_GET_IID(nsIComponentLoader)))
00081               ret = MakePyG_nsIComponentLoader(pPyInstance);
00082        else if (iid.Equals(NS_GET_IID(nsIInputStream)))
00083               ret = MakePyG_nsIInputStream(pPyInstance);
00084        else
00085               ret = new PyXPCOM_XPTStub(pPyInstance, iid);
00086        if (ret==nsnull)
00087               return NS_ERROR_OUT_OF_MEMORY;
00088        ret->AddRef(); // The first reference for the caller.
00089        *ppResult = ret->ThisAsIID(iid);
00090        NS_ABORT_IF_FALSE(*ppResult != NULL, "ThisAsIID() gave NULL, but we know it supports it!");
00091        return *ppResult ? NS_OK : NS_ERROR_FAILURE;
00092 }
00093 
00094 PyG_Base::PyG_Base(PyObject *instance, const nsIID &iid)
00095 {
00096        // Note that "instance" is the _policy_ instance!!
00097        PR_AtomicIncrement(&cGateways);
00098        m_pBaseObject = GetDefaultGateway(instance);
00099        // m_pWeakRef is an nsCOMPtr and needs no init.
00100 
00101        NS_ABORT_IF_FALSE(!(iid.Equals(NS_GET_IID(nsISupportsWeakReference)) || iid.Equals(NS_GET_IID(nsIWeakReference))),"Should not be creating gateways with weak-ref interfaces");
00102        m_iid = iid;
00103        m_pPyObject = instance;
00104        NS_PRECONDITION(instance, "NULL PyObject for PyXPCOM_XPTStub!");
00105 
00106 #ifdef NS_BUILD_REFCNT_LOGGING
00107        // If XPCOM reference count logging is enabled, then allow us to give the Python class.
00108        PyObject *realInstance = PyObject_GetAttrString(instance, "_obj_");
00109        PyObject *r = PyObject_Repr(realInstance);
00110        const char *szRepr;
00111        if (r==NULL) {
00112               PyXPCOM_LogError("Getting the __repr__ of the object failed");
00113               PyErr_Clear();
00114               szRepr = "(repr failed!)";
00115        }
00116        else
00117               szRepr = PyString_AsString(r);
00118        if (szRepr==NULL) szRepr = "";
00119        int reprOffset = *szRepr=='<' ? 1 : 0;
00120        static const char *reprPrefix = "component:";
00121        if (strncmp(reprPrefix, szRepr+reprOffset, strlen(reprPrefix)) == 0)
00122               reprOffset += strlen(reprPrefix);
00123        strncpy(refcntLogRepr, szRepr + reprOffset, sizeof(refcntLogRepr)-1);
00124        refcntLogRepr[sizeof(refcntLogRepr)-1] = '\0';
00125        // See if we should get rid of the " at 0x12345" portion.
00126        char *lastPos = strstr(refcntLogRepr, " at ");
00127        if (lastPos) *lastPos = '\0';
00128        Py_XDECREF(realInstance);
00129        Py_XDECREF(r);
00130 #endif // NS_BUILD_REFCNT_LOGGING
00131 
00132 #ifdef DEBUG_LIFETIMES
00133        {
00134               char *iid_repr;
00135               nsCOMPtr<nsIInterfaceInfoManager> iim = XPTI_GetInterfaceInfoManager();
00136               if (iim!=nsnull)
00137                      iim->GetNameForIID(&iid, &iid_repr);
00138               PyObject *real_instance = PyObject_GetAttrString(instance, "_obj_");
00139               PyObject *real_repr = PyObject_Repr(real_instance);
00140 
00141               PYXPCOM_LOG_DEBUG("PyG_Base created at %p\n  instance_repr=%s\n  IID=%s\n", this, PyString_AsString(real_repr), iid_repr);
00142               nsMemory::Free(iid_repr);
00143               Py_XDECREF(real_instance);
00144               Py_XDECREF(real_repr);
00145        }
00146 #endif // DEBUG_LIFETIMES
00147        Py_XINCREF(instance); // instance should never be NULL - but whats an X between friends!
00148 
00149        PyXPCOM_DLLAddRef();
00150 
00151 #ifdef DEBUG_FULL
00152        LogF("PyGatewayBase: created %s", m_pPyObject ? m_pPyObject->ob_type->tp_name : "<NULL>");
00153 #endif
00154 }
00155 
00156 PyG_Base::~PyG_Base()
00157 {
00158        PR_AtomicDecrement(&cGateways);
00159 #ifdef DEBUG_LIFETIMES
00160        PYXPCOM_LOG_DEBUG("PyG_Base: deleted %p", this);
00161 #endif
00162        if ( m_pPyObject ) {
00163               CEnterLeavePython celp;
00164               Py_DECREF(m_pPyObject);
00165        }
00166        if (m_pBaseObject)
00167               m_pBaseObject->Release();
00168        if (m_pWeakRef) {
00169               // Need to ensure some other thread isnt doing a QueryReferent on
00170               // our weak reference at the same time
00171               CEnterLeaveXPCOMFramework _celf;
00172               PyXPCOM_GatewayWeakReference *p = (PyXPCOM_GatewayWeakReference *)(nsISupports *)m_pWeakRef;
00173               p->m_pBase = nsnull;
00174               m_pWeakRef = nsnull;
00175        }
00176        PyXPCOM_DLLRelease();
00177 }
00178 
00179 // Get the correct interface pointer for this object given the IID.
00180 void *PyG_Base::ThisAsIID( const nsIID &iid )
00181 {
00182        if (this==NULL) return NULL;
00183        if (iid.Equals(NS_GET_IID(nsISupports)))
00184               return (nsISupports *)(nsIInternalPython *)this;
00185        if (iid.Equals(NS_GET_IID(nsISupportsWeakReference)))
00186               return (nsISupportsWeakReference *)this;
00187        if (iid.Equals(NS_GET_IID(nsIInternalPython))) 
00188               return (nsISupports *)(nsIInternalPython *)this;
00189        return NULL;
00190 }
00191 
00192 // Call back into Python, passing a Python instance, and get back
00193 // an interface object that wraps the instance.
00194 /*static*/ PRBool 
00195 PyG_Base::AutoWrapPythonInstance(PyObject *ob, const nsIID &iid, nsISupports **ppret)
00196 {
00197        NS_PRECONDITION(ppret!=NULL, "null pointer when wrapping a Python instance!");
00198        NS_PRECONDITION(ob && PyInstance_Check(ob), "AutoWrapPythonInstance is expecting an non-NULL instance!");
00199        PRBool ok = PR_FALSE;
00200     // XXX - todo - this static object leaks! (but Python on Windows leaks 2000+ objects as it is ;-)
00201        static PyObject *func = NULL; // fetch this once and remember!
00202        PyObject *obIID = NULL;
00203        PyObject *wrap_ret = NULL;
00204        PyObject *args = NULL;
00205        if (func==NULL) { // not thread-safe, but nothing bad can happen, except an extra reference leak
00206               PyObject *mod = PyImport_ImportModule("xpcom.server");
00207               if (mod)
00208                      func = PyObject_GetAttrString(mod, "WrapObject");
00209               Py_XDECREF(mod);
00210               if (func==NULL) goto done;
00211        }
00212        // See if the instance has previously been wrapped.
00213        if (CheckDefaultGateway(ob, iid, ppret)) {
00214               ok = PR_TRUE; // life is good!
00215        } else {
00216               PyErr_Clear();
00217 
00218               obIID = Py_nsIID::PyObjectFromIID(iid);
00219               if (obIID==NULL) goto done;
00220               args = Py_BuildValue("OOzi", ob, obIID, NULL, 0);
00221               if (args==NULL) goto done;
00222               wrap_ret = PyEval_CallObject(func, args);
00223               if (wrap_ret==NULL) goto done;
00224               ok = Py_nsISupports::InterfaceFromPyObject(wrap_ret, iid, ppret, PR_FALSE, PR_FALSE);
00225 #ifdef DEBUG
00226               if (ok)
00227               // Check we _now_ have a default gateway
00228               {
00229                      nsISupports *temp = NULL;
00230                      NS_ABORT_IF_FALSE(CheckDefaultGateway(ob, iid, &temp), "Auto-wrapped object didnt get a default gateway!");
00231                      if (temp) temp->Release();
00232               }
00233 #endif
00234        }
00235 done:
00236 //     Py_XDECREF(func); -- func is static for performance reasons.
00237        Py_XDECREF(obIID);
00238        Py_XDECREF(wrap_ret);
00239        Py_XDECREF(args);
00240        return ok;
00241 }
00242 
00243 // Call back into Python, passing a raw nsIInterface object, getting back
00244 // the object to actually use as the gateway parameter for this interface.
00245 // For example, it is expected that the policy will wrap the interface
00246 // object in one of the xpcom.client.Interface objects, allowing 
00247 // natural usage of the interface from Python clients.
00248 // Note that piid will usually be NULL - this is because the runtime
00249 // reflection interfaces dont provide this information to me.
00250 // In this case, the Python code may choose to lookup the complete
00251 // interface info to obtain the IID.
00252 // It is expected (but should not be assumed) that the method info
00253 // or the IID will be NULL.
00254 // Worst case, the code should provide a wrapper for the nsiSupports interface,
00255 // so at least the user can simply QI the object.
00256 PyObject *
00257 PyG_Base::MakeInterfaceParam(nsISupports *pis, 
00258                           const nsIID *piid, 
00259                           int methodIndex /* = -1 */,
00260                           const XPTParamDescriptor *d /* = NULL */, 
00261                           int paramIndex /* = -1 */)
00262 {
00263        if (pis==NULL) {
00264               Py_INCREF(Py_None);
00265               return Py_None;
00266        }
00267        // This condition is true today, but not necessarily so.
00268        // But if it ever triggers, the poor Python code has no real hope
00269        // of returning something useful, so we should at least do our
00270        // best to provide the useful data.
00271        NS_WARN_IF_FALSE( ((piid != NULL) ^ (d != NULL)) == 1, "No information on the interface available - Python's gunna have a hard time doing much with it!");
00272        PyObject *obIID = NULL;
00273        PyObject *obISupports = NULL;
00274        PyObject *obParamDesc = NULL;
00275        PyObject *result = NULL;
00276 
00277        // get the basic interface first, as if we fail, we can try and use this.
00278        nsIID iid_check = piid ? *piid : NS_GET_IID(nsISupports);
00279        obISupports = Py_nsISupports::PyObjectFromInterface(pis, iid_check, PR_TRUE, PR_FALSE);
00280        if (!obISupports)
00281               goto done;
00282        if (piid==NULL) {
00283               obIID = Py_None;
00284               Py_INCREF(Py_None);
00285        } else
00286               obIID = Py_nsIID::PyObjectFromIID(*piid);
00287        if (obIID==NULL)
00288               goto done;
00289        obParamDesc = PyObject_FromXPTParamDescriptor(d);
00290        if (obParamDesc==NULL)
00291               goto done;
00292 
00293        result = PyObject_CallMethod(m_pPyObject, 
00294                                       "_MakeInterfaceParam_",
00295                                    "OOiOi",
00296                                    obISupports,
00297                                    obIID,
00298                                    methodIndex,
00299                                    obParamDesc,
00300                                    paramIndex);
00301 done:
00302        if (PyErr_Occurred()) {
00303               NS_WARN_IF_FALSE(result==NULL, "Have an error, but also a result!");
00304               PyXPCOM_LogError("Wrapping an interface object for the gateway failed\n");
00305        }
00306        Py_XDECREF(obIID);
00307        Py_XDECREF(obParamDesc);
00308        if (result==NULL) { // we had an error.
00309               PyErr_Clear(); // but are not reporting it back to Python itself!
00310               // return our obISupports.  If NULL, we are really hosed and nothing we can do.
00311               return obISupports;
00312        }
00313        // Dont need to return this - we have a better result.
00314        Py_XDECREF(obISupports);
00315        return result;
00316 }
00317 
00318 NS_IMETHODIMP
00319 PyG_Base::QueryInterface(REFNSIID iid, void** ppv)
00320 {
00321 #ifdef PYXPCOM_DEBUG_FULL
00322        {
00323               char *sziid = iid.ToString();
00324               LogF("PyGatewayBase::QueryInterface: %s", sziid);
00325               Allocator::Free(sziid);
00326        }
00327 #endif
00328        NS_PRECONDITION(ppv, "NULL pointer");
00329        if (ppv==nsnull)
00330               return NS_ERROR_NULL_POINTER;
00331        *ppv = nsnull;
00332        // If one of our native interfaces (but NOT nsISupports if we have a base)
00333        // return this.
00334        // It is important is that nsISupports come from the base object
00335        // to ensure that we live by XPCOM identity rules (other interfaces need
00336        // not abide by this rule - only nsISupports.)
00337        if ( (m_pBaseObject==NULL || !iid.Equals(NS_GET_IID(nsISupports)))
00338           && (*ppv=ThisAsIID(iid)) != NULL ) {
00339               AddRef();
00340               return NS_OK;
00341        }
00342        // If we have a "base object", then we need to delegate _every_ remaining 
00343        // QI to it.
00344        if (m_pBaseObject != NULL)
00345               return m_pBaseObject->QueryInterface(iid, ppv);
00346 
00347        // Call the Python policy to see if it (says it) supports the interface
00348        PRBool supports = PR_FALSE;
00349        { // temp scope for Python lock
00350               CEnterLeavePython celp;
00351 
00352               PyObject * ob = Py_nsIID::PyObjectFromIID(iid);
00353               PyObject * this_interface_ob = Py_nsISupports::PyObjectFromInterface((nsIInternalPython *)this, NS_GET_IID(nsISupports), PR_TRUE, PR_FALSE);
00354               if ( !ob || !this_interface_ob) {
00355                      Py_XDECREF(ob);
00356                      Py_XDECREF(this_interface_ob);
00357                      return NS_ERROR_OUT_OF_MEMORY;
00358               }
00359 
00360               PyObject *result = PyObject_CallMethod(m_pPyObject, "_QueryInterface_",
00361                                                                   "OO", 
00362                                                             this_interface_ob, ob);
00363               Py_DECREF(ob);
00364               Py_DECREF(this_interface_ob);
00365 
00366               if ( result ) {
00367                      if (Py_nsISupports::InterfaceFromPyObject(result, iid, (nsISupports **)ppv, PR_TRUE)) {
00368                             // If OK, but NULL, _QI_ returned None, which simply means
00369                             // "no such interface"
00370                             supports = (*ppv!=NULL);
00371                             // result has been QI'd and AddRef'd all ready for return.
00372                      } else {
00373                             // Dump this message and any Python exception before
00374                             // reporting the fact that QI failed - this error
00375                             // may provide clues!
00376                             PyXPCOM_LogError("The _QueryInterface_ method returned an object of type '%s', but an interface was expected\n", result->ob_type->tp_name);
00377                             // supports remains false
00378                      }
00379                      Py_DECREF(result);
00380               } else {
00381                      NS_ABORT_IF_FALSE(PyErr_Occurred(), "Got NULL result, but no Python error flagged!");
00382                      NS_WARN_IF_FALSE(!supports, "Have failure with success flag set!");
00383                      PyXPCOM_LogError("The _QueryInterface_ processing failed.\n");
00384                      // supports remains false.
00385                      // We have reported the error, and are returning to COM,
00386                      // so we should clear it.
00387                      PyErr_Clear();
00388               }
00389        } // end of temp scope for Python lock - lock released here!
00390        if ( !supports )
00391               return NS_ERROR_NO_INTERFACE;
00392        return NS_OK;
00393 }
00394 
00395 nsrefcnt
00396 PyG_Base::AddRef(void)
00397 {
00398        nsrefcnt cnt = (nsrefcnt) PR_AtomicIncrement((PRInt32*)&mRefCnt);
00399 #ifdef NS_BUILD_REFCNT_LOGGING
00400        // If we have no pBaseObject, then we need to ignore them
00401        if (m_pBaseObject == NULL)
00402               NS_LOG_ADDREF(this, cnt, refcntLogRepr, sizeof(*this));
00403 #endif
00404        return cnt;
00405 }
00406 
00407 nsrefcnt
00408 PyG_Base::Release(void)
00409 {
00410        nsrefcnt cnt = (nsrefcnt) PR_AtomicDecrement((PRInt32*)&mRefCnt);
00411 #ifdef NS_BUILD_REFCNT_LOGGING
00412        if (m_pBaseObject == NULL)
00413               NS_LOG_RELEASE(this, cnt, refcntLogRepr);
00414 #endif
00415        if ( cnt == 0 )
00416               delete this;
00417        return cnt;
00418 }
00419 
00420 NS_IMETHODIMP
00421 PyG_Base::GetWeakReference(nsIWeakReference **ret)
00422 {
00423        // always delegate back to the "base" gateway for the object, as this tear-off
00424        // interface may not live as long as the base.  So we recurse back to the base.
00425        if (m_pBaseObject) {
00426               NS_PRECONDITION(m_pWeakRef == nsnull, "Not a base object, but do have a weak-ref!");
00427               return m_pBaseObject->GetWeakReference(ret);
00428        }
00429        NS_PRECONDITION(ret, "null pointer");
00430        if (ret==nsnull) return NS_ERROR_INVALID_POINTER;
00431        if (!m_pWeakRef) {
00432               // First query for a weak reference - create it.
00433               m_pWeakRef = new PyXPCOM_GatewayWeakReference(this);
00434               NS_ABORT_IF_FALSE(m_pWeakRef, "Shouldn't be able to fail creating a weak reference!");
00435               if (!m_pWeakRef)
00436                      return NS_ERROR_UNEXPECTED;
00437        }
00438        *ret = m_pWeakRef;
00439        (*ret)->AddRef();
00440        return NS_OK;
00441 }
00442 
00443 nsresult PyG_Base::HandleNativeGatewayError(const char *szMethodName)
00444 {
00445        nsresult rc = NS_OK;
00446        if (PyErr_Occurred()) {
00447               // The error handling - fairly involved, but worth it as
00448               // good error reporting is critical for users to know WTF 
00449               // is going on - especially with TypeErrors etc in their
00450               // return values (ie, after the Python code has successfully
00451               // existed, but we encountered errors unpacking their
00452               // result values for the COM caller - there is literally no 
00453               // way to catch these exceptions from Python code, as their
00454               // is no Python function on the call-stack)
00455 
00456               // First line of attack in an error is to call-back on the policy.
00457               // If the callback of the error handler succeeds and returns an
00458               // integer (for the nsresult), we take no further action.
00459 
00460               // If this callback fails, we log _2_ exceptions - the error handler
00461               // error, and the original error.
00462 
00463               PRBool bProcessMainError = PR_TRUE; // set to false if our exception handler does its thing!
00464               PyObject *exc_typ, *exc_val, *exc_tb;
00465               PyErr_Fetch(&exc_typ, &exc_val, &exc_tb);
00466 
00467               PyObject *err_result = PyObject_CallMethod(m_pPyObject, 
00468                                               "_GatewayException_",
00469                                           "z(OOO)",
00470                                           szMethodName,
00471                                              exc_typ ? exc_typ : Py_None, // should never be NULL, but defensive programming...
00472                                              exc_val ? exc_val : Py_None, // may well be NULL.
00473                                           exc_tb ? exc_tb : Py_None); // may well be NULL.
00474               if (err_result == NULL) {
00475                      PyXPCOM_LogError("The exception handler _CallMethodException_ failed!\n");
00476               } else if (err_result == Py_None) {
00477                      // The exception handler has chosen not to do anything with
00478                      // this error, so we still need to print it!
00479                      ;
00480               } else if (PyInt_Check(err_result)) {
00481                      // The exception handler has given us the nresult.
00482                      rc = PyInt_AsLong(err_result);
00483                      bProcessMainError = PR_FALSE;
00484               } else {
00485                      // The exception handler succeeded, but returned other than
00486                      // int or None.
00487                      PyXPCOM_LogError("The _CallMethodException_ handler returned object of type '%s' - None or an integer expected\n", err_result->ob_type->tp_name);
00488               }
00489               Py_XDECREF(err_result);
00490               PyErr_Restore(exc_typ, exc_val, exc_tb);
00491               if (bProcessMainError) {
00492                      PyXPCOM_LogError("The function '%s' failed\n", szMethodName);
00493                      rc = PyXPCOM_SetCOMErrorFromPyException();
00494               }
00495               PyErr_Clear();
00496        }
00497        return rc;
00498 }
00499 
00500 static nsresult do_dispatch(
00501        PyObject *pPyObject,
00502        PyObject **ppResult,
00503        const char *szMethodName,
00504        const char *szFormat,
00505        va_list va
00506        )
00507 {
00508        NS_PRECONDITION(ppResult, "Must provide a result buffer");
00509        *ppResult = nsnull;
00510        // Build the Invoke arguments...
00511        PyObject *args = NULL;
00512        PyObject *method = NULL;
00513        PyObject *real_ob = NULL;
00514        nsresult ret = NS_ERROR_FAILURE;
00515        if ( szFormat )
00516               args = Py_VaBuildValue((char *)szFormat, va);
00517        else
00518               args = PyTuple_New(0);
00519        if ( !args )
00520               goto done;
00521 
00522        // make sure a tuple.
00523        if ( !PyTuple_Check(args) ) {
00524               PyObject *a = PyTuple_New(1);
00525               if ( a == NULL )
00526               {
00527                      Py_DECREF(args);
00528                      goto done;
00529               }
00530               PyTuple_SET_ITEM(a, 0, args);
00531               args = a;
00532        }
00533        // Bit to a hack here to maintain the use of a policy.
00534        // We actually get the policies underlying object
00535        // to make the call on.
00536        real_ob = PyObject_GetAttrString(pPyObject, "_obj_");
00537        if (real_ob == NULL) {
00538               PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute.");
00539               goto done;
00540        }
00541        method = PyObject_GetAttrString(real_ob, (char *)szMethodName);
00542        if ( !method ) {
00543               PyErr_Clear();
00544               ret = NS_PYXPCOM_NO_SUCH_METHOD;
00545               goto done;
00546        }
00547        // Make the call
00548        *ppResult = PyEval_CallObject(method, args);
00549        ret = *ppResult ? NS_OK : NS_ERROR_FAILURE;
00550 done:
00551        Py_XDECREF(method);
00552        Py_XDECREF(real_ob);
00553        Py_XDECREF(args);
00554        return ret;
00555 }
00556 
00557 
00558 nsresult PyG_Base::InvokeNativeViaPolicyInternal(
00559        const char *szMethodName,
00560        PyObject **ppResult,
00561        const char *szFormat,
00562        va_list va
00563        )
00564 {
00565        if ( m_pPyObject == NULL || szMethodName == NULL )
00566               return NS_ERROR_NULL_POINTER;
00567 
00568        PyObject *temp = nsnull;
00569        if (ppResult == nsnull)
00570               ppResult = &temp;
00571        nsresult nr = do_dispatch(m_pPyObject, ppResult, szMethodName, szFormat, va);
00572 
00573        // If temp is NULL, they provided a buffer, and we dont touch it.
00574        // If not NULL, *ppResult = temp, and _we_ do own it.
00575        Py_XDECREF(temp);
00576        return nr;
00577 }
00578 
00579 nsresult PyG_Base::InvokeNativeViaPolicy(
00580        const char *szMethodName,
00581        PyObject **ppResult /* = NULL */,
00582        const char *szFormat /* = NULL */,
00583        ...
00584        )
00585 {
00586        va_list va;
00587        va_start(va, szFormat);
00588        nsresult nr = InvokeNativeViaPolicyInternal(szMethodName, ppResult, szFormat, va);
00589        va_end(va);
00590 
00591        if (nr == NS_PYXPCOM_NO_SUCH_METHOD) {
00592               // Only problem was missing method.
00593               PyErr_Format(PyExc_AttributeError, "The object does not have a '%s' function.", szMethodName);
00594        }
00595        return nr == NS_OK ? NS_OK : HandleNativeGatewayError(szMethodName);
00596 }
00597 
00598 nsresult PyG_Base::InvokeNativeGetViaPolicy(
00599        const char *szPropertyName,
00600        PyObject **ppResult /* = NULL */
00601        )
00602 {
00603        PyObject *ob_ret = NULL;
00604        nsresult ret = NS_OK;
00605        PyObject *real_ob = NULL;
00606        if ( m_pPyObject == NULL || szPropertyName == NULL )
00607               return NS_ERROR_NULL_POINTER;
00608        // First see if we have a method of that name.
00609        char buf[256];
00610        strcpy(buf, "get_");
00611        strncat(buf, szPropertyName, sizeof(buf)*sizeof(buf[0])-strlen(buf)-1);
00612        buf[sizeof(buf)/sizeof(buf[0])-1] = '\0';
00613        ret = InvokeNativeViaPolicyInternal(buf, ppResult, nsnull, nsnull);
00614        if (ret == NS_PYXPCOM_NO_SUCH_METHOD) {
00615               // No method of that name - just try a property.
00616               // Bit to a hack here to maintain the use of a policy.
00617               // We actually get the policies underlying object
00618               // to make the call on.
00619               real_ob = PyObject_GetAttrString(m_pPyObject, "_obj_");
00620               if (real_ob == NULL) {
00621                      PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute.");
00622                      ret = HandleNativeGatewayError(szPropertyName);
00623                      goto done;
00624               }
00625               ob_ret = PyObject_GetAttrString(real_ob, (char *)szPropertyName);
00626               if (ob_ret==NULL) {
00627                      PyErr_Format(PyExc_AttributeError, 
00628                                  "The object does not have a 'get_%s' function, or a '%s attribute.", 
00629                                  szPropertyName, szPropertyName);
00630               } else {
00631                      ret = NS_OK;
00632                      if (ppResult)
00633                             *ppResult = ob_ret;
00634                      else
00635                             Py_XDECREF(ob_ret);
00636               }
00637        }
00638        if (ret != NS_OK)
00639               ret = HandleNativeGatewayError(szPropertyName);
00640 
00641 done:
00642        Py_XDECREF(real_ob);
00643        return ret;
00644 }
00645 
00646 nsresult PyG_Base::InvokeNativeSetViaPolicy(
00647        const char *szPropertyName,
00648        ...
00649        )
00650 {
00651        if ( m_pPyObject == NULL || szPropertyName == NULL )
00652               return NS_ERROR_NULL_POINTER;
00653        nsresult ret = NS_OK;
00654        PyObject *real_ob = NULL;
00655        char buf[256];
00656        strcpy(buf, "set_");
00657        strncat(buf, szPropertyName, sizeof(buf)*sizeof(buf[0])-strlen(buf)-1);
00658        buf[sizeof(buf)/sizeof(buf[0])-1] = '\0';
00659        va_list va;
00660        va_start(va, szPropertyName);
00661        ret = InvokeNativeViaPolicyInternal(buf, NULL, "O", va);
00662        va_end(va);
00663        if (ret == NS_PYXPCOM_NO_SUCH_METHOD) {
00664               // No method of that name - just try a property.
00665               // Bit to a hack here to maintain the use of a policy.
00666               // We actually get the policies underlying object
00667               // to make the call on.
00668               real_ob = PyObject_GetAttrString(m_pPyObject, "_obj_");
00669               if (real_ob == NULL) {
00670                      PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute.");
00671                      ret = HandleNativeGatewayError(szPropertyName);
00672                      goto done;
00673               }
00674               va_list va2;
00675               va_start(va2, szPropertyName);
00676               PyObject *arg = va_arg( va2, PyObject *);
00677               va_end(va2);
00678               if (PyObject_SetAttrString(real_ob, (char *)szPropertyName, arg) == 0)
00679                      ret = NS_OK;
00680               else {
00681                      PyErr_Format(PyExc_AttributeError, 
00682                                  "The object does not have a 'set_%s' function, or a '%s attribute.", 
00683                                  szPropertyName, szPropertyName);
00684               }
00685        }
00686        if (ret != NS_OK)
00687               ret = HandleNativeGatewayError(szPropertyName);
00688 done:
00689        Py_XDECREF(real_ob);
00690        return ret;
00691 }
00692 
00693 // Get at the underlying Python object.
00694 PyObject *PyG_Base::UnwrapPythonObject(void)
00695 {
00696     Py_INCREF(m_pPyObject);
00697     return m_pPyObject;
00698 }
00699 /******************************************************
00700 
00701  Some special support to help with object identity.
00702 
00703   In the simplest case, assume a Python COM object is
00704   supporting a function "nsIWhatever GetWhatever()",
00705   so implements it as:
00706     return this
00707   it is almost certain they intend returning
00708   the same COM OBJECT to the caller!  Thus, if a user of this COM
00709   object does:
00710 
00711      p1 = foo.GetWhatever();
00712      p2 = foo.GetWhatever();
00713 
00714   We almost certainly expect p1==p2==foo.
00715 
00716   We previously _did_ have special support for the "this"
00717   example above, but this implements a generic scheme that
00718   works for _all_ objects.
00719 
00720   Whenever we are asked to "AutoWrap" a Python object, the
00721   first thing we do is see if it has been auto-wrapped before.
00722 
00723   If not, we create a new wrapper, then make a COM weak reference 
00724   to that wrapper, and store it directly back into the instance
00725   we are auto-wrapping!  The use of a weak-reference prevents
00726   cycles.
00727 
00728   The existance of this attribute in an instance indicates if it
00729   has been previously auto-wrapped.
00730 
00731   If it _has_ previously been auto-wrapped, we de-reference the
00732   weak reference, and use that gateway.
00733 
00734 *********************************************************************/
00735 
00736 PyG_Base *GetDefaultGateway(PyObject *policy)
00737 {
00738        // NOTE: Instance is the policy, not the real instance
00739        PyObject *instance = PyObject_GetAttrString(policy, "_obj_");
00740        if (instance == nsnull)
00741               return nsnull;
00742        PyObject *ob_existing_weak = PyObject_GetAttrString(instance, PyXPCOM_szDefaultGatewayAttributeName);
00743        Py_DECREF(instance);
00744        if (ob_existing_weak != NULL) {
00745               PRBool ok = PR_TRUE;
00746               nsCOMPtr<nsIWeakReference> pWeakRef;
00747               ok = NS_SUCCEEDED(Py_nsISupports::InterfaceFromPyObject(ob_existing_weak, 
00748                                                      NS_GET_IID(nsIWeakReference), 
00749                                                      getter_AddRefs(pWeakRef),
00750                                                      PR_FALSE));
00751               Py_DECREF(ob_existing_weak);
00752               nsISupports *pip;
00753               if (ok) {
00754                      nsresult nr = pWeakRef->QueryReferent( NS_GET_IID(nsIInternalPython), (void **)&pip);
00755                      if (NS_FAILED(nr))
00756                             return nsnull;
00757                      return (PyG_Base *)(nsIInternalPython *)pip;
00758               }
00759        } else
00760               PyErr_Clear();
00761        return nsnull;
00762 }
00763 
00764 PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway)
00765 {
00766        NS_ABORT_IF_FALSE(real_inst, "Did not have an _obj_ attribute");
00767        if (real_inst==NULL) {
00768               PyErr_Clear();
00769               return PR_FALSE;
00770        }
00771        PyObject *ob_existing_weak = PyObject_GetAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName);
00772        if (ob_existing_weak != NULL) {
00773               // We have an existing default, but as it is a weak reference, it
00774               // may no longer be valid.  Check it.
00775               PRBool ok = PR_TRUE;
00776               nsCOMPtr<nsIWeakReference> pWeakRef;
00777               ok = NS_SUCCEEDED(Py_nsISupports::InterfaceFromPyObject(ob_existing_weak, 
00778                                                      NS_GET_IID(nsIWeakReference), 
00779                                                      getter_AddRefs(pWeakRef), 
00780                                                      PR_FALSE));
00781               Py_DECREF(ob_existing_weak);
00782               if (ok) {
00783                      Py_BEGIN_ALLOW_THREADS;
00784                      ok = NS_SUCCEEDED(pWeakRef->QueryReferent( iid, (void **)(ret_gateway)));
00785                      Py_END_ALLOW_THREADS;
00786               }
00787               if (!ok) {
00788                      // We have the attribute, but not valid - wipe it
00789                      // before restoring it.
00790                      if (0 != PyObject_DelAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName))
00791                             PyErr_Clear();
00792               }
00793               return ok;
00794        }
00795        PyErr_Clear();
00796        return PR_FALSE;
00797 }
00798 
00799 void AddDefaultGateway(PyObject *instance, nsISupports *gateway)
00800 {
00801        // NOTE: Instance is the _policy_!
00802        PyObject *real_inst = PyObject_GetAttrString(instance, "_obj_");
00803        NS_ABORT_IF_FALSE(real_inst, "Could not get the '_obj_' element");
00804        if (!real_inst) return;
00805        if (!PyObject_HasAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName)) {
00806               nsCOMPtr<nsISupportsWeakReference> swr( do_QueryInterface((nsISupportsWeakReference *)(gateway)) );
00807               NS_ABORT_IF_FALSE(swr, "Our gateway failed with a weak reference query");
00808               // Create the new default gateway - get a weak reference for our gateway.
00809               if (swr) {
00810                      nsIWeakReference *pWeakReference = NULL;
00811                      swr->GetWeakReference( &pWeakReference );
00812                      if (pWeakReference) {
00813                             PyObject *ob_new_weak = Py_nsISupports::PyObjectFromInterface(pWeakReference, 
00814                                                                          NS_GET_IID(nsIWeakReference),
00815                                                                          PR_FALSE, /* bAddRef */
00816                                                                          PR_FALSE ); /* bMakeNicePyObject */
00817                             // pWeakReference reference consumed.
00818                             if (ob_new_weak) {
00819                                    PyObject_SetAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName, ob_new_weak);
00820                                    Py_DECREF(ob_new_weak);
00821                             }
00822                      }
00823               }
00824        }
00825        Py_DECREF(real_inst);
00826 }