First checkin of the Python XPCOM bindings.
git-svn-id: svn://10.0.0.236/trunk@87331 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
757
mozilla/extensions/python/xpcom/src/PyGBase.cpp
Normal file
757
mozilla/extensions/python/xpcom/src/PyGBase.cpp
Normal file
@@ -0,0 +1,757 @@
|
||||
/* Copyright (c) 2000-2001 ActiveState Tool Corporation.
|
||||
See the file LICENSE.txt for licensing information. */
|
||||
|
||||
// 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 = "_com_instance_default_gateway_";
|
||||
nsresult GetDefaultGateway(PyObject *instance, REFNSIID iid, void **ret);
|
||||
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!!
|
||||
NS_INIT_REFCNT();
|
||||
PR_AtomicIncrement(&cGateways);
|
||||
m_pBaseObject = NULL;
|
||||
// m_pWeakRef is an nsCOMPtr and needs no init.
|
||||
m_iid = iid;
|
||||
m_pPyObject = instance;
|
||||
NS_PRECONDITION(instance, "NULL PyObject for PyXPCOM_XPTStub!");
|
||||
#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);
|
||||
nsAllocator::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
|
||||
}
|
||||
|
||||
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 && PyInstance_Check(ob), "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("OO", ob, obIID);
|
||||
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.
|
||||
nsIID iid_check = piid ? *piid : NS_GET_IID(nsISupports);
|
||||
obISupports = Py_nsISupports::PyObjectFromInterface(pis, iid_check, PR_TRUE, 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,
|
||||
"_MakeInterfaceParam_",
|
||||
"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.
|
||||
// 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 && (m_pBaseObject->QueryInterface(iid, ppv)==NS_OK))
|
||||
return NS_OK;
|
||||
|
||||
// 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);
|
||||
PyObject * this_interface_ob = Py_nsISupports::PyObjectFromInterface((nsIInternalPython *)this, NS_GET_IID(nsISupports), PR_TRUE, PR_FALSE);
|
||||
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, "_QueryInterface_",
|
||||
"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;
|
||||
|
||||
// Now setup the base object pointer back to me.
|
||||
// We do a QI on our internal one to ensure we can safely cast
|
||||
// the result to a PyG_Base (both from the POV that is may not
|
||||
// be a Python object, and that the vtables offsets may screw
|
||||
// us even if it is!)
|
||||
nsISupports *pLook = (nsISupports *)(*ppv);
|
||||
nsIInternalPython *pTemp;
|
||||
if (pLook->QueryInterface(NS_GET_IID(nsIInternalPython), (void **)&pTemp)==NS_OK) {
|
||||
// One of our objects, so set the base object if it doesnt already have one
|
||||
PyG_Base *pG = (PyG_Base *)pTemp;
|
||||
// Eeek - just these few next lines need to be thread-safe :-(
|
||||
CEnterLeaveXPCOMFramework _celf;
|
||||
if (pG->m_pBaseObject==NULL && pG != (PyG_Base *)this) {
|
||||
pG->m_pBaseObject = this;
|
||||
pG->m_pBaseObject->AddRef();
|
||||
#ifdef DEBUG_LIFETIMES
|
||||
PYXPCOM_LOG_DEBUG("PyG_Base setting BaseObject of %p to %p\n", pG, this);
|
||||
#endif
|
||||
}
|
||||
pTemp->Release();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsrefcnt
|
||||
PyG_Base::AddRef(void)
|
||||
{
|
||||
nsrefcnt cnt = (nsrefcnt) PR_AtomicIncrement((PRInt32*)&mRefCnt);
|
||||
NS_LOG_ADDREF(this, cnt, "PyG_Base", sizeof(*this));
|
||||
return cnt;
|
||||
}
|
||||
|
||||
nsrefcnt
|
||||
PyG_Base::Release(void)
|
||||
{
|
||||
nsrefcnt cnt = (nsrefcnt) PR_AtomicDecrement((PRInt32*)&mRefCnt);
|
||||
NS_LOG_RELEASE(this, cnt, "PyG_Base");
|
||||
if ( cnt == 0 )
|
||||
delete this;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PyG_Base::GetWeakReference(nsIWeakReference **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.
|
||||
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
|
||||
// existed, 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 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,
|
||||
"_GatewayException_",
|
||||
"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;
|
||||
}
|
||||
|
||||
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_COMFALSE;
|
||||
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_COMFALSE) {
|
||||
// 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_COMFALSE) {
|
||||
// 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;
|
||||
PyObject *ob_ret = NULL;
|
||||
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_COMFALSE) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
|
||||
Some special support to help with object identity.
|
||||
|
||||
In the simplest case, assume a Python COM object is
|
||||
supporting a function "nsIWhatever GetWhatever()",
|
||||
so implements it as:
|
||||
return this
|
||||
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 "this"
|
||||
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.
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
nsresult GetDefaultGateway(PyObject *instance, REFNSIID iid, void **ret)
|
||||
{
|
||||
// NOTE: Instance is the real instance, _not_ the policy.
|
||||
PyObject *ob_existing_weak = PyObject_GetAttrString(instance, PyXPCOM_szDefaultGatewayAttributeName);
|
||||
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);
|
||||
if (ok)
|
||||
return pWeakRef->QueryReferent( iid, ret);
|
||||
} else
|
||||
PyErr_Clear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
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) {
|
||||
nsIWeakReference *pWeakReference = NULL;
|
||||
swr->GetWeakReference( &pWeakReference );
|
||||
if (pWeakReference) {
|
||||
PyObject *ob_new_weak = Py_nsISupports::PyObjectFromInterface(pWeakReference,
|
||||
NS_GET_IID(nsIWeakReference),
|
||||
PR_FALSE, /* bAddRef */
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user