nsresult PyG_Base::HandleNativeGatewayError(const char *szMethodName) { nsresult rc = NS_OK; if (PyErr_Occurred()) { // The error handling - fairly involved, but worth it as // good error reporting is critical for users to know WTF // is going on - especially with TypeErrors etc in their // return values (ie, after the Python code has successfully // exited, but we encountered errors unpacking their // result values for the COM caller - there is literally no // way to catch these exceptions from Python code, as their // is no Python function directly on the call-stack) // First line of attack in an error is to call-back on the policy. // If the callback of the error handler succeeds and returns an // integer (for the nsresult), we take no further action. // If this callback fails, we log _2_ exceptions - the error // handler error, and the original error. PRBool bProcessMainError = PR_TRUE; // set to false if our exception handler does its thing! PyObject *exc_typ, *exc_val, *exc_tb; PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); PyObject *err_result = PyObject_CallMethod(m_pPyObject, (char*)"_GatewayException_", (char*)"z(OOO)", szMethodName, exc_typ ? exc_typ : Py_None, // should never be NULL, but defensive programming... exc_val ? exc_val : Py_None, // may well be NULL. exc_tb ? exc_tb : Py_None); // may well be NULL. if (err_result == NULL) { PyXPCOM_LogError("The exception handler _CallMethodException_ failed!\n"); } else if (err_result == Py_None) { // The exception handler has chosen not to do anything with // this error, so we still need to print it! ; } else if (PyInt_Check(err_result)) { // The exception handler has given us the nresult. rc = PyInt_AsLong(err_result); bProcessMainError = PR_FALSE; } else { // The exception handler succeeded, but returned other than // int or None. PyXPCOM_LogError("The _CallMethodException_ handler returned object of type '%s' - None or an integer expected\n", err_result->ob_type->tp_name); } Py_XDECREF(err_result); PyErr_Restore(exc_typ, exc_val, exc_tb); if (bProcessMainError) { PyXPCOM_LogError("The function '%s' failed\n", szMethodName); rc = PyXPCOM_SetCOMErrorFromPyException(); } PyErr_Clear(); } return rc; }
// Call back into the Python context PyObject * Py_DOMnsISupports::MakeDefaultWrapper(PyObject *pycontext, PyObject *pyis, const nsIID &iid) { NS_PRECONDITION(pyis, "NULL pyobject!"); PyObject *obIID = NULL; PyObject *ret = NULL; obIID = Py_nsIID::PyObjectFromIID(iid); if (obIID==NULL) goto done; ret = PyObject_CallMethod(pycontext, "MakeInterfaceResult", "OO", pyis, obIID); if (ret==NULL) goto done; done: if (PyErr_Occurred()) { NS_ABORT_IF_FALSE(ret==NULL, "Have an error, but also a return val!"); PyXPCOM_LogError("Creating an interface object to be used as a result failed\n"); PyErr_Clear(); } Py_XDECREF(obIID); if (ret==NULL) // eek - error - return the original with no refcount mod. ret = pyis; else // no error - decref the old object Py_DECREF(pyis); // return our obISupports. If NULL, we are really hosed and nothing we can do. return ret; }
// See comments in PyXPCOM.h for why we need this! void PyXPCOM_MakePendingCalls() { while (1) { int rc = Py_MakePendingCalls(); if (rc == 0) break; // An exception - just report it as normal. // Note that a traceback is very unlikely! PyXPCOM_LogError("Unhandled exception detected before entering Python.\n"); PyErr_Clear(); // And loop around again until we are told everything is done! } }
// Call back into Python, passing a raw nsIInterface object, getting back // the object to actually pass to Python. PyObject * Py_nsISupports::MakeDefaultWrapper(PyObject *pyis, const nsIID &iid) { NS_PRECONDITION(pyis, "NULL pyobject!"); PyObject *obIID = NULL; PyObject *args = NULL; PyObject *mod = NULL; PyObject *ret = NULL; obIID = Py_nsIID::PyObjectFromIID(iid); if (obIID==NULL) goto done; if (g_obFuncMakeInterfaceCount==NULL) { PyObject *mod = PyImport_ImportModule("xpcom.client"); if (mod) g_obFuncMakeInterfaceCount = PyObject_GetAttrString(mod, "MakeInterfaceResult"); Py_XDECREF(mod); } if (g_obFuncMakeInterfaceCount==NULL) goto done; args = Py_BuildValue("OO", pyis, obIID); if (args==NULL) goto done; ret = PyEval_CallObject(g_obFuncMakeInterfaceCount, args); done: if (PyErr_Occurred()) { NS_ABORT_IF_FALSE(ret==NULL, "Have an error, but also a return val!"); PyXPCOM_LogError("Creating an interface object to be used as a result failed\n"); PyErr_Clear(); } Py_XDECREF(mod); Py_XDECREF(args); Py_XDECREF(obIID); if (ret==NULL) // eek - error - return the original with no refcount mod. ret = pyis; else // no error - decref the old object Py_DECREF(pyis); // return our obISupports. If NULL, we are really hosed and nothing we can do. return ret; }
PyG_Base::PyG_Base(PyObject *instance, const nsIID &iid) { // Note that "instance" is the _policy_ instance!! PR_AtomicIncrement(&cGateways); m_pBaseObject = GetDefaultGateway(instance); // m_pWeakRef is an nsCOMPtr and needs no init. 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"); m_iid = iid; m_pPyObject = instance; NS_PRECONDITION(instance, "NULL PyObject for PyXPCOM_XPTStub!"); #ifdef NS_BUILD_REFCNT_LOGGING // If XPCOM reference count logging is enabled, then allow us to give the Python class. PyObject *realInstance = PyObject_GetAttrString(instance, "_obj_"); PyObject *r = PyObject_Repr(realInstance); const char *szRepr; if (r==NULL) { PyXPCOM_LogError("Getting the __repr__ of the object failed"); PyErr_Clear(); szRepr = "(repr failed!)"; } else szRepr = PyString_AsString(r); if (szRepr==NULL) szRepr = ""; int reprOffset = *szRepr=='<' ? 1 : 0; static const char *reprPrefix = "component:"; if (strncmp(reprPrefix, szRepr+reprOffset, strlen(reprPrefix)) == 0) reprOffset += strlen(reprPrefix); strncpy(refcntLogRepr, szRepr + reprOffset, sizeof(refcntLogRepr)-1); refcntLogRepr[sizeof(refcntLogRepr)-1] = '\0'; // See if we should get rid of the " at 0x12345" portion. char *lastPos = strstr(refcntLogRepr, " at "); if (lastPos) *lastPos = '\0'; Py_XDECREF(realInstance); Py_XDECREF(r); #endif // NS_BUILD_REFCNT_LOGGING #ifdef DEBUG_LIFETIMES { char *iid_repr; nsCOMPtr<nsIInterfaceInfoManager> iim = XPTI_GetInterfaceInfoManager(); if (iim!=nsnull) iim->GetNameForIID(&iid, &iid_repr); PyObject *real_instance = PyObject_GetAttrString(instance, "_obj_"); PyObject *real_repr = PyObject_Repr(real_instance); PYXPCOM_LOG_DEBUG("PyG_Base created at %p\n instance_repr=%s\n IID=%s\n", this, PyString_AsString(real_repr), iid_repr); nsMemory::Free(iid_repr); Py_XDECREF(real_instance); Py_XDECREF(real_repr); } #endif // DEBUG_LIFETIMES Py_XINCREF(instance); // instance should never be NULL - but whats an X between friends! PyXPCOM_DLLAddRef(); #ifdef DEBUG_FULL LogF("PyGatewayBase: created %s", m_pPyObject ? m_pPyObject->ob_type->tp_name : "<NULL>"); #endif }
NS_IMETHODIMP PyG_Base::QueryInterface(REFNSIID iid, void** ppv) { #ifdef PYXPCOM_DEBUG_FULL { char *sziid = iid.ToString(); LogF("PyGatewayBase::QueryInterface: %s", sziid); Allocator::Free(sziid); } #endif NS_PRECONDITION(ppv, "NULL pointer"); if (ppv==nsnull) return NS_ERROR_NULL_POINTER; *ppv = nsnull; // If one of our native interfaces (but NOT nsISupports if we have a base) // return this. // It is important is that nsISupports come from the base object // to ensure that we live by XPCOM identity rules (other interfaces need // not abide by this rule - only nsISupports.) if ( (m_pBaseObject==NULL || !iid.Equals(NS_GET_IID(nsISupports))) && (*ppv=ThisAsIID(iid)) != NULL ) { AddRef(); return NS_OK; } // If we have a "base object", then we need to delegate _every_ remaining // QI to it. if (m_pBaseObject != NULL) return m_pBaseObject->QueryInterface(iid, ppv); // Call the Python policy to see if it (says it) supports the interface PRBool supports = PR_FALSE; { // temp scope for Python lock CEnterLeavePython celp; PyObject * ob = Py_nsIID::PyObjectFromIID(iid); // must say this is an 'internal' call, else we recurse QI into // oblivion. PyObject * this_interface_ob = Py_nsISupports::PyObjectFromInterface( (nsXPTCStubBase *)this, iid, PR_FALSE, PR_TRUE); if ( !ob || !this_interface_ob) { Py_XDECREF(ob); Py_XDECREF(this_interface_ob); return NS_ERROR_OUT_OF_MEMORY; } PyObject *result = PyObject_CallMethod(m_pPyObject, (char*)"_QueryInterface_", (char*)"OO", this_interface_ob, ob); Py_DECREF(ob); Py_DECREF(this_interface_ob); if ( result ) { if (Py_nsISupports::InterfaceFromPyObject(result, iid, (nsISupports **)ppv, PR_TRUE)) { // If OK, but NULL, _QI_ returned None, which simply means // "no such interface" supports = (*ppv!=NULL); // result has been QI'd and AddRef'd all ready for return. } else { // Dump this message and any Python exception before // reporting the fact that QI failed - this error // may provide clues! PyXPCOM_LogError("The _QueryInterface_ method returned an object of type '%s', but an interface was expected\n", result->ob_type->tp_name); // supports remains false } Py_DECREF(result); } else { NS_ABORT_IF_FALSE(PyErr_Occurred(), "Got NULL result, but no Python error flagged!"); NS_WARN_IF_FALSE(!supports, "Have failure with success flag set!"); PyXPCOM_LogError("The _QueryInterface_ processing failed.\n"); // supports remains false. // We have reported the error, and are returning to COM, // so we should clear it. PyErr_Clear(); } } // end of temp scope for Python lock - lock released here! if ( !supports ) return NS_ERROR_NO_INTERFACE; return NS_OK; }
// Call back into Python, passing a raw nsIInterface object, getting back // the object to actually use as the gateway parameter for this interface. // For example, it is expected that the policy will wrap the interface // object in one of the xpcom.client.Interface objects, allowing // natural usage of the interface from Python clients. // Note that piid will usually be NULL - this is because the runtime // reflection interfaces dont provide this information to me. // In this case, the Python code may choose to lookup the complete // interface info to obtain the IID. // It is expected (but should not be assumed) that the method info // or the IID will be NULL. // Worst case, the code should provide a wrapper for the nsiSupports interface, // so at least the user can simply QI the object. PyObject * PyG_Base::MakeInterfaceParam(nsISupports *pis, const nsIID *piid, int methodIndex /* = -1 */, const XPTParamDescriptor *d /* = NULL */, int paramIndex /* = -1 */) { if (pis==NULL) { Py_INCREF(Py_None); return Py_None; } // This condition is true today, but not necessarily so. // But if it ever triggers, the poor Python code has no real hope // of returning something useful, so we should at least do our // best to provide the useful data. 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!"); PyObject *obIID = NULL; PyObject *obISupports = NULL; PyObject *obParamDesc = NULL; PyObject *result = NULL; // get the basic interface first, as if we fail, we can try and use this. // If we don't know the IID, we must explicitly query for nsISupports. nsCOMPtr<nsISupports> piswrap; nsIID iid_check; if (piid) { iid_check = *piid; piswrap = pis; } else { iid_check = NS_GET_IID(nsISupports); pis->QueryInterface(iid_check, getter_AddRefs(piswrap)); } obISupports = Py_nsISupports::PyObjectFromInterface(piswrap, iid_check, PR_FALSE); if (!obISupports) goto done; if (piid==NULL) { obIID = Py_None; Py_INCREF(Py_None); } else obIID = Py_nsIID::PyObjectFromIID(*piid); if (obIID==NULL) goto done; obParamDesc = PyObject_FromXPTParamDescriptor(d); if (obParamDesc==NULL) goto done; result = PyObject_CallMethod(m_pPyObject, (char*)"_MakeInterfaceParam_", (char*)"OOiOi", obISupports, obIID, methodIndex, obParamDesc, paramIndex); done: if (PyErr_Occurred()) { NS_WARN_IF_FALSE(result==NULL, "Have an error, but also a result!"); PyXPCOM_LogError("Wrapping an interface object for the gateway failed\n"); } Py_XDECREF(obIID); Py_XDECREF(obParamDesc); if (result==NULL) { // we had an error. PyErr_Clear(); // but are not reporting it back to Python itself! // return our obISupports. If NULL, we are really hosed and nothing we can do. return obISupports; } // Dont need to return this - we have a better result. Py_XDECREF(obISupports); return result; }