summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/python/src/PyGBase.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libs/xpcom18a4/python/src/PyGBase.cpp852
1 files changed, 852 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/python/src/PyGBase.cpp b/src/libs/xpcom18a4/python/src/PyGBase.cpp
new file mode 100644
index 00000000..e73b2a6d
--- /dev/null
+++ b/src/libs/xpcom18a4/python/src/PyGBase.cpp
@@ -0,0 +1,852 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Python XPCOM language bindings.
+ *
+ * The Initial Developer of the Original Code is
+ * ActiveState Tool Corp.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Mark Hammond <mhammond@skippinet.com.au> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// PyGBase.cpp - implementation of the PyG_Base class
+//
+// This code is part of the XPCOM extensions for Python.
+//
+// Written May 2000 by Mark Hammond.
+//
+// Based heavily on the Python COM support, which is
+// (c) Mark Hammond and Greg Stein.
+//
+// (c) 2000, ActiveState corp.
+
+#include "PyXPCOM_std.h"
+#include <nsIModule.h>
+#include <nsIComponentLoader.h>
+#include <nsIInputStream.h>
+
+static PRInt32 cGateways = 0;
+PRInt32 _PyXPCOM_GetGatewayCount(void)
+{
+ return cGateways;
+}
+
+extern PyG_Base *MakePyG_nsIModule(PyObject *);
+extern PyG_Base *MakePyG_nsIComponentLoader(PyObject *instance);
+extern PyG_Base *MakePyG_nsIInputStream(PyObject *instance);
+
+static char *PyXPCOM_szDefaultGatewayAttributeName = (char*)"_com_instance_default_gateway_";
+PyG_Base *GetDefaultGateway(PyObject *instance);
+void AddDefaultGateway(PyObject *instance, nsISupports *gateway);
+PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway);
+
+/*static*/ nsresult
+PyG_Base::CreateNew(PyObject *pPyInstance, const nsIID &iid, void **ppResult)
+{
+ NS_PRECONDITION(ppResult && *ppResult==NULL, "NULL or uninitialized pointer");
+ if (ppResult==nsnull)
+ return NS_ERROR_NULL_POINTER;
+
+ PyG_Base *ret;
+ // Hack for few extra gateways we support.
+ if (iid.Equals(NS_GET_IID(nsIModule)))
+ ret = MakePyG_nsIModule(pPyInstance);
+ else if (iid.Equals(NS_GET_IID(nsIComponentLoader)))
+ ret = MakePyG_nsIComponentLoader(pPyInstance);
+ else if (iid.Equals(NS_GET_IID(nsIInputStream)))
+ ret = MakePyG_nsIInputStream(pPyInstance);
+ else
+ ret = new PyXPCOM_XPTStub(pPyInstance, iid);
+ if (ret==nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ ret->AddRef(); // The first reference for the caller.
+ *ppResult = ret->ThisAsIID(iid);
+ NS_ABORT_IF_FALSE(*ppResult != NULL, "ThisAsIID() gave NULL, but we know it supports it!");
+ return *ppResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+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
+#if PY_MAJOR_VERSION <= 2
+ szRepr = PyString_AsString(r);
+#else
+ szRepr = PyUnicode_AsUTF8(r);
+#endif
+ 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 ? PyXPCOM_ObTypeName(m_pPyObject) : "<NULL>");
+#endif
+}
+
+PyG_Base::~PyG_Base()
+{
+ PR_AtomicDecrement(&cGateways);
+#ifdef DEBUG_LIFETIMES
+ PYXPCOM_LOG_DEBUG("PyG_Base: deleted %p", this);
+#endif
+ if ( m_pPyObject ) {
+ CEnterLeavePython celp;
+ Py_DECREF(m_pPyObject);
+ }
+ if (m_pBaseObject)
+ m_pBaseObject->Release();
+ if (m_pWeakRef) {
+ // Need to ensure some other thread isnt doing a QueryReferent on
+ // our weak reference at the same time
+ CEnterLeaveXPCOMFramework _celf;
+ PyXPCOM_GatewayWeakReference *p = (PyXPCOM_GatewayWeakReference *)(nsISupports *)m_pWeakRef;
+ p->m_pBase = nsnull;
+ m_pWeakRef = nsnull;
+ }
+ PyXPCOM_DLLRelease();
+}
+
+// Get the correct interface pointer for this object given the IID.
+void *PyG_Base::ThisAsIID( const nsIID &iid )
+{
+ if (this==NULL) return NULL;
+ if (iid.Equals(NS_GET_IID(nsISupports)))
+ return (nsISupports *)(nsIInternalPython *)this;
+ if (iid.Equals(NS_GET_IID(nsISupportsWeakReference)))
+ return (nsISupportsWeakReference *)this;
+ if (iid.Equals(NS_GET_IID(nsIInternalPython)))
+ return (nsISupports *)(nsIInternalPython *)this;
+ return NULL;
+}
+
+// Call back into Python, passing a Python instance, and get back
+// an interface object that wraps the instance.
+/*static*/ PRBool
+PyG_Base::AutoWrapPythonInstance(PyObject *ob, const nsIID &iid, nsISupports **ppret)
+{
+ NS_PRECONDITION(ppret!=NULL, "null pointer when wrapping a Python instance!");
+ NS_PRECONDITION(ob && PyObject_HasAttrString(ob, "__class__"),
+ "AutoWrapPythonInstance is expecting an non-NULL instance!");
+ PRBool ok = PR_FALSE;
+ // XXX - todo - this static object leaks! (but Python on Windows leaks 2000+ objects as it is ;-)
+ static PyObject *func = NULL; // fetch this once and remember!
+ PyObject *obIID = NULL;
+ PyObject *wrap_ret = NULL;
+ PyObject *args = NULL;
+ if (func==NULL) { // not thread-safe, but nothing bad can happen, except an extra reference leak
+ PyObject *mod = PyImport_ImportModule("xpcom.server");
+ if (mod)
+ func = PyObject_GetAttrString(mod, "WrapObject");
+ Py_XDECREF(mod);
+ if (func==NULL) goto done;
+ }
+ // See if the instance has previously been wrapped.
+ if (CheckDefaultGateway(ob, iid, ppret)) {
+ ok = PR_TRUE; // life is good!
+ } else {
+ PyErr_Clear();
+
+ obIID = Py_nsIID::PyObjectFromIID(iid);
+ if (obIID==NULL) goto done;
+ args = Py_BuildValue("OOzi", ob, obIID, NULL, 0);
+ if (args==NULL) goto done;
+ wrap_ret = PyEval_CallObject(func, args);
+ if (wrap_ret==NULL) goto done;
+ ok = Py_nsISupports::InterfaceFromPyObject(wrap_ret, iid, ppret, PR_FALSE, PR_FALSE);
+#ifdef DEBUG
+ if (ok)
+ // Check we _now_ have a default gateway
+ {
+ nsISupports *temp = NULL;
+ NS_ABORT_IF_FALSE(CheckDefaultGateway(ob, iid, &temp), "Auto-wrapped object didnt get a default gateway!");
+ if (temp) temp->Release();
+ }
+#endif
+ }
+done:
+// Py_XDECREF(func); -- func is static for performance reasons.
+ Py_XDECREF(obIID);
+ Py_XDECREF(wrap_ret);
+ Py_XDECREF(args);
+ return 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 {
+ /* HACK ALERT! Dropping the python interpreter lock here while
+ doing QueryInterface because it may involve IPC to a python
+ object in the same interpreter and deadlock. Not at all
+ sure if this is a good idea or not for the internal PyXPCOM
+ state, but it might fix the deadloock... Hoping for the best. */
+ Py_BEGIN_ALLOW_THREADS;
+ iid_check = NS_GET_IID(nsISupports);
+ pis->QueryInterface(iid_check, getter_AddRefs(piswrap));
+ Py_END_ALLOW_THREADS;
+ }
+
+ 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;
+}
+
+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", PyXPCOM_ObTypeName(result));
+ // 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;
+}
+
+nsrefcnt
+PyG_Base::AddRef(void)
+{
+ nsrefcnt cnt = (nsrefcnt) PR_AtomicIncrement((PRInt32*)&mRefCnt);
+#ifdef NS_BUILD_REFCNT_LOGGING
+ // If we have no pBaseObject, then we need to ignore them
+ if (m_pBaseObject == NULL)
+ NS_LOG_ADDREF(this, cnt, refcntLogRepr, sizeof(*this));
+#endif
+ return cnt;
+}
+
+nsrefcnt
+PyG_Base::Release(void)
+{
+ nsrefcnt cnt = (nsrefcnt) PR_AtomicDecrement((PRInt32*)&mRefCnt);
+#ifdef NS_BUILD_REFCNT_LOGGING
+ if (m_pBaseObject == NULL)
+ NS_LOG_RELEASE(this, cnt, refcntLogRepr);
+#endif
+ if ( cnt == 0 )
+ delete this;
+ return cnt;
+}
+
+NS_IMETHODIMP
+PyG_Base::GetWeakReference(nsIWeakReference **ret)
+{
+ // always delegate back to the "base" gateway for the object, as this tear-off
+ // interface may not live as long as the base. So we recurse back to the base.
+ if (m_pBaseObject) {
+ NS_PRECONDITION(m_pWeakRef == nsnull, "Not a base object, but do have a weak-ref!");
+ return m_pBaseObject->GetWeakReference(ret);
+ }
+ NS_PRECONDITION(ret, "null pointer");
+ if (ret==nsnull) return NS_ERROR_INVALID_POINTER;
+ if (!m_pWeakRef) {
+ // First query for a weak reference - create it.
+ // XXX - this looks like it needs thread safety!?
+ m_pWeakRef = new PyXPCOM_GatewayWeakReference(this);
+ NS_ABORT_IF_FALSE(m_pWeakRef, "Shouldn't be able to fail creating a weak reference!");
+ if (!m_pWeakRef)
+ return NS_ERROR_UNEXPECTED;
+ }
+ *ret = m_pWeakRef;
+ (*ret)->AddRef();
+ return NS_OK;
+}
+
+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", PyXPCOM_ObTypeName(err_result));
+ }
+ 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;
+}
+
+static nsresult do_dispatch(
+ PyObject *pPyObject,
+ PyObject **ppResult,
+ const char *szMethodName,
+ const char *szFormat,
+ va_list va
+ )
+{
+ NS_PRECONDITION(ppResult, "Must provide a result buffer");
+ *ppResult = nsnull;
+ // Build the Invoke arguments...
+ PyObject *args = NULL;
+ PyObject *method = NULL;
+ PyObject *real_ob = NULL;
+ nsresult ret = NS_ERROR_FAILURE;
+ if ( szFormat )
+ args = Py_VaBuildValue((char *)szFormat, va);
+ else
+ args = PyTuple_New(0);
+ if ( !args )
+ goto done;
+
+ // make sure a tuple.
+ if ( !PyTuple_Check(args) ) {
+ PyObject *a = PyTuple_New(1);
+ if ( a == NULL )
+ {
+ Py_DECREF(args);
+ goto done;
+ }
+ PyTuple_SET_ITEM(a, 0, args);
+ args = a;
+ }
+ // Bit to a hack here to maintain the use of a policy.
+ // We actually get the policies underlying object
+ // to make the call on.
+ real_ob = PyObject_GetAttrString(pPyObject, "_obj_");
+ if (real_ob == NULL) {
+ PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute.");
+ goto done;
+ }
+ method = PyObject_GetAttrString(real_ob, (char *)szMethodName);
+ if ( !method ) {
+ PyErr_Clear();
+ ret = NS_PYXPCOM_NO_SUCH_METHOD;
+ goto done;
+ }
+ // Make the call
+ *ppResult = PyEval_CallObject(method, args);
+ ret = *ppResult ? NS_OK : NS_ERROR_FAILURE;
+done:
+ Py_XDECREF(method);
+ Py_XDECREF(real_ob);
+ Py_XDECREF(args);
+ return ret;
+}
+
+
+nsresult PyG_Base::InvokeNativeViaPolicyInternal(
+ const char *szMethodName,
+ PyObject **ppResult,
+ const char *szFormat,
+ va_list va
+ )
+{
+ if ( m_pPyObject == NULL || szMethodName == NULL )
+ return NS_ERROR_NULL_POINTER;
+
+ PyObject *temp = nsnull;
+ if (ppResult == nsnull)
+ ppResult = &temp;
+ nsresult nr = do_dispatch(m_pPyObject, ppResult, szMethodName, szFormat, va);
+
+ // If temp is NULL, they provided a buffer, and we dont touch it.
+ // If not NULL, *ppResult = temp, and _we_ do own it.
+ Py_XDECREF(temp);
+ return nr;
+}
+
+nsresult PyG_Base::InvokeNativeViaPolicy(
+ const char *szMethodName,
+ PyObject **ppResult /* = NULL */,
+ const char *szFormat /* = NULL */,
+ ...
+ )
+{
+ va_list va;
+ va_start(va, szFormat);
+ nsresult nr = InvokeNativeViaPolicyInternal(szMethodName, ppResult, szFormat, va);
+ va_end(va);
+
+ if (nr == NS_PYXPCOM_NO_SUCH_METHOD) {
+ // Only problem was missing method.
+ PyErr_Format(PyExc_AttributeError, "The object does not have a '%s' function.", szMethodName);
+ }
+ return nr == NS_OK ? NS_OK : HandleNativeGatewayError(szMethodName);
+}
+
+nsresult PyG_Base::InvokeNativeGetViaPolicy(
+ const char *szPropertyName,
+ PyObject **ppResult /* = NULL */
+ )
+{
+ PyObject *ob_ret = NULL;
+ nsresult ret = NS_OK;
+ PyObject *real_ob = NULL;
+ if ( m_pPyObject == NULL || szPropertyName == NULL )
+ return NS_ERROR_NULL_POINTER;
+ // First see if we have a method of that name.
+ char buf[256];
+ strcpy(buf, "get_");
+ strncat(buf, szPropertyName, sizeof(buf)*sizeof(buf[0])-strlen(buf)-1);
+ buf[sizeof(buf)/sizeof(buf[0])-1] = '\0';
+ ret = InvokeNativeViaPolicyInternal(buf, ppResult, nsnull, nsnull);
+ if (ret == NS_PYXPCOM_NO_SUCH_METHOD) {
+ // No method of that name - just try a property.
+ // Bit to a hack here to maintain the use of a policy.
+ // We actually get the policies underlying object
+ // to make the call on.
+ real_ob = PyObject_GetAttrString(m_pPyObject, "_obj_");
+ if (real_ob == NULL) {
+ PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute.");
+ ret = HandleNativeGatewayError(szPropertyName);
+ goto done;
+ }
+ ob_ret = PyObject_GetAttrString(real_ob, (char *)szPropertyName);
+ if (ob_ret==NULL) {
+ PyErr_Format(PyExc_AttributeError,
+ "The object does not have a 'get_%s' function, or a '%s attribute.",
+ szPropertyName, szPropertyName);
+ } else {
+ ret = NS_OK;
+ if (ppResult)
+ *ppResult = ob_ret;
+ else
+ Py_XDECREF(ob_ret);
+ }
+ }
+ if (ret != NS_OK)
+ ret = HandleNativeGatewayError(szPropertyName);
+
+done:
+ Py_XDECREF(real_ob);
+ return ret;
+}
+
+nsresult PyG_Base::InvokeNativeSetViaPolicy(
+ const char *szPropertyName,
+ ...
+ )
+{
+ if ( m_pPyObject == NULL || szPropertyName == NULL )
+ return NS_ERROR_NULL_POINTER;
+ nsresult ret = NS_OK;
+ PyObject *real_ob = NULL;
+ char buf[256];
+ strcpy(buf, "set_");
+ strncat(buf, szPropertyName, sizeof(buf)*sizeof(buf[0])-strlen(buf)-1);
+ buf[sizeof(buf)/sizeof(buf[0])-1] = '\0';
+ va_list va;
+ va_start(va, szPropertyName);
+ ret = InvokeNativeViaPolicyInternal(buf, NULL, "O", va);
+ va_end(va);
+ if (ret == NS_PYXPCOM_NO_SUCH_METHOD) {
+ // No method of that name - just try a property.
+ // Bit to a hack here to maintain the use of a policy.
+ // We actually get the policies underlying object
+ // to make the call on.
+ real_ob = PyObject_GetAttrString(m_pPyObject, "_obj_");
+ if (real_ob == NULL) {
+ PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute.");
+ ret = HandleNativeGatewayError(szPropertyName);
+ goto done;
+ }
+ va_list va2;
+ va_start(va2, szPropertyName);
+ PyObject *arg = va_arg( va2, PyObject *);
+ va_end(va2);
+ if (PyObject_SetAttrString(real_ob, (char *)szPropertyName, arg) == 0)
+ ret = NS_OK;
+ else {
+ PyErr_Format(PyExc_AttributeError,
+ "The object does not have a 'set_%s' function, or a '%s attribute.",
+ szPropertyName, szPropertyName);
+ }
+ }
+ if (ret != NS_OK)
+ ret = HandleNativeGatewayError(szPropertyName);
+done:
+ Py_XDECREF(real_ob);
+ return ret;
+}
+
+// Get at the underlying Python object.
+PyObject *PyG_Base::UnwrapPythonObject(void)
+{
+ Py_INCREF(m_pPyObject);
+ return m_pPyObject;
+}
+/******************************************************
+
+ Some special support to help with object identity.
+
+ In the simplest case, assume a Python XPCOM object is
+ supporting a function "nsIWhatever GetWhatever()",
+ so implements it as:
+ return self
+ it is almost certain they intend returning
+ the same COM OBJECT to the caller! Thus, if a user of this COM
+ object does:
+
+ p1 = foo.GetWhatever();
+ p2 = foo.GetWhatever();
+
+ We almost certainly expect p1==p2==foo.
+
+ We previously _did_ have special support for the "self"
+ example above, but this implements a generic scheme that
+ works for _all_ objects.
+
+ Whenever we are asked to "AutoWrap" a Python object, the
+ first thing we do is see if it has been auto-wrapped before.
+
+ If not, we create a new wrapper, then make a COM weak reference
+ to that wrapper, and store it directly back into the instance
+ we are auto-wrapping! The use of a weak-reference prevents
+ cycles.
+
+ The existance of this attribute in an instance indicates if it
+ has been previously auto-wrapped.
+
+ If it _has_ previously been auto-wrapped, we de-reference the
+ weak reference, and use that gateway.
+
+*********************************************************************/
+
+PyG_Base *GetDefaultGateway(PyObject *policy)
+{
+ // NOTE: Instance is the policy, not the real instance
+ PyObject *instance = PyObject_GetAttrString(policy, "_obj_");
+ if (instance == nsnull)
+ return nsnull;
+ PyObject *ob_existing_weak = PyObject_GetAttrString(instance, PyXPCOM_szDefaultGatewayAttributeName);
+ Py_DECREF(instance);
+ if (ob_existing_weak != NULL) {
+ PRBool ok = PR_TRUE;
+ nsCOMPtr<nsIWeakReference> pWeakRef;
+ ok = NS_SUCCEEDED(Py_nsISupports::InterfaceFromPyObject(ob_existing_weak,
+ NS_GET_IID(nsIWeakReference),
+ getter_AddRefs(pWeakRef),
+ PR_FALSE));
+ Py_DECREF(ob_existing_weak);
+ nsISupports *pip;
+ if (ok) {
+ nsresult nr = pWeakRef->QueryReferent( NS_GET_IID(nsIInternalPython), (void **)&pip);
+ if (NS_FAILED(nr))
+ return nsnull;
+ return (PyG_Base *)(nsIInternalPython *)pip;
+ }
+ } else
+ PyErr_Clear();
+ return nsnull;
+}
+
+PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway)
+{
+ NS_ABORT_IF_FALSE(real_inst, "Did not have an _obj_ attribute");
+ if (real_inst==NULL) {
+ PyErr_Clear();
+ return PR_FALSE;
+ }
+ PyObject *ob_existing_weak = PyObject_GetAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName);
+ if (ob_existing_weak != NULL) {
+ // We have an existing default, but as it is a weak reference, it
+ // may no longer be valid. Check it.
+ PRBool ok = PR_TRUE;
+ nsCOMPtr<nsIWeakReference> pWeakRef;
+ ok = NS_SUCCEEDED(Py_nsISupports::InterfaceFromPyObject(ob_existing_weak,
+ NS_GET_IID(nsIWeakReference),
+ getter_AddRefs(pWeakRef),
+ PR_FALSE));
+ Py_DECREF(ob_existing_weak);
+ if (ok) {
+ Py_BEGIN_ALLOW_THREADS;
+ ok = NS_SUCCEEDED(pWeakRef->QueryReferent( iid, (void **)(ret_gateway)));
+ Py_END_ALLOW_THREADS;
+ }
+ if (!ok) {
+ // We have the attribute, but not valid - wipe it
+ // before restoring it.
+ if (0 != PyObject_DelAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName))
+ PyErr_Clear();
+ }
+ return ok;
+ }
+ PyErr_Clear();
+ return PR_FALSE;
+}
+
+void AddDefaultGateway(PyObject *instance, nsISupports *gateway)
+{
+ // NOTE: Instance is the _policy_!
+ PyObject *real_inst = PyObject_GetAttrString(instance, "_obj_");
+ NS_ABORT_IF_FALSE(real_inst, "Could not get the '_obj_' element");
+ if (!real_inst) return;
+ if (!PyObject_HasAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName)) {
+ nsCOMPtr<nsISupportsWeakReference> swr( do_QueryInterface((nsISupportsWeakReference *)(gateway)) );
+ NS_ABORT_IF_FALSE(swr, "Our gateway failed with a weak reference query");
+ // Create the new default gateway - get a weak reference for our gateway.
+ if (swr) {
+ nsCOMPtr<nsIWeakReference> pWeakReference;
+ swr->GetWeakReference( getter_AddRefs(pWeakReference) );
+ if (pWeakReference) {
+ PyObject *ob_new_weak = Py_nsISupports::PyObjectFromInterface(pWeakReference,
+ NS_GET_IID(nsIWeakReference),
+ PR_FALSE ); /* bMakeNicePyObject */
+ // pWeakReference reference consumed.
+ if (ob_new_weak) {
+ PyObject_SetAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName, ob_new_weak);
+ Py_DECREF(ob_new_weak);
+ }
+ }
+ }
+ }
+ Py_DECREF(real_inst);
+}