From 88e93b42bab343ba1b3789bb19d9f187c15fbbcd Mon Sep 17 00:00:00 2001 From: "markh%activestate.com" Date: Tue, 8 Jan 2002 01:58:58 +0000 Subject: [PATCH] * Changes to observers and service manager APIs. * Use nsIComponentManagerObsolete. * Fix weak reference leaks * Cache interface infos better for significant perf increase. * Better tests for leaks Not part of the build. --- extensions/python/xpcom/client/__init__.py | 36 ++++++-- extensions/python/xpcom/components.py | 20 +++-- extensions/python/xpcom/server/__init__.py | 16 +++- extensions/python/xpcom/server/policy.py | 23 ++++-- extensions/python/xpcom/src/ErrorUtils.cpp | 2 +- extensions/python/xpcom/src/PyGBase.cpp | 82 +++++++++++-------- .../python/xpcom/src/PyGWeakReference.cpp | 32 +++++++- .../python/xpcom/src/PyIComponentManager.cpp | 18 ++-- extensions/python/xpcom/src/PyXPCOM.h | 8 +- .../python/xpcom/src/loader/pyloader.cpp | 2 +- extensions/python/xpcom/src/xpcom.cpp | 13 ++- extensions/python/xpcom/test/regrtest.py | 8 +- .../xpcom/test/test_component/makefile.win | 2 +- .../python/xpcom/test/test_weakreferences.py | 32 +++++++- 14 files changed, 217 insertions(+), 77 deletions(-) diff --git a/extensions/python/xpcom/client/__init__.py b/extensions/python/xpcom/client/__init__.py index 70e784a117ee..8dfe0b6cea17 100644 --- a/extensions/python/xpcom/client/__init__.py +++ b/extensions/python/xpcom/client/__init__.py @@ -73,9 +73,13 @@ interface_cache = {} # Keyed by [iid][name], each item is an unbound method. interface_method_cache = {} +# Keyed by clsid from nsIClassInfo - everything ever queried for the CID. +contractid_info_cache = {} + def _shutdown(): interface_cache.clear() interface_method_cache.clear() + contractid_info_cache.clear() # Fully process the named method, generating method code etc. def BuildMethod(method_info, iid): @@ -214,14 +218,27 @@ class Component(_XPCOMBase): except COMException: classinfo = None if classinfo is not None: -# print "YAY - have class info!!", classinfo real_cid = classinfo.contractID - if real_cid is not None: + if real_cid: self.__dict__['_object_name_'] = real_cid - for nominated_iid in classinfo.getInterfaces(): - # Interface may appear twice in the class info list, so check this here. - if not self.__dict__['_interface_infos_'].has_key(nominated_iid): - self._remember_interface_info(nominated_iid) + contractid_info = contractid_info_cache.get(real_cid) + else: + contractid_info = None + if contractid_info is None: +# print "YAY - have class info!!", classinfo + for nominated_iid in classinfo.getInterfaces(): + # Interface may appear twice in the class info list, so check this here. + if not self.__dict__['_interface_infos_'].has_key(nominated_iid): + self._remember_interface_info(nominated_iid) + if real_cid is not None: + contractid_info = {} + contractid_info['_name_to_interface_name_'] = self.__dict__['_name_to_interface_name_'] + contractid_info['_interface_infos_'] = self.__dict__['_interface_infos_'] + contractid_info_cache[real_cid] = contractid_info + else: + for key, val in contractid_info.items(): + self.__dict__[key].update(val) + self.__dict__['_com_classinfo_'] = classinfo def _remember_interface_info(self, iid): @@ -307,7 +324,12 @@ class Component(_XPCOMBase): setattr(interface, attr, val) return raise AttributeError, "XPCOM component '%s' can not set attribute '%s'" % (self._object_name_, attr) - + def __repr__(self): + # We can advantage from nsIClassInfo - use it. + if not self._tried_classinfo_: + self._build_all_supported_interfaces_() + return _XPCOMBase.__repr__(self) + class _Interface(_XPCOMBase): def __init__(self, comobj, iid, method_infos, getters, setters, constants): self.__dict__['_comobj_'] = comobj diff --git a/extensions/python/xpcom/components.py b/extensions/python/xpcom/components.py index 254a823b1455..e744e64c3538 100644 --- a/extensions/python/xpcom/components.py +++ b/extensions/python/xpcom/components.py @@ -37,8 +37,11 @@ manager = xpcom.client.Component(_xpcom.NS_GetGlobalComponentManager(), _xpcom.I # The "interfaceInfoManager" object - JS doesnt have this. interfaceInfoManager = _xpcom.XPTI_GetInterfaceInfoManager() +# The serviceManager - JS doesnt have this either! +serviceManager = _xpcom.GetGlobalServiceManager() + # The "Exception" object -Exception = xpcom.Exception +Exception = xpcom.COMException # Base class for our collections. # It appears that all objects supports "." and "[]" notation. @@ -80,6 +83,7 @@ class _ComponentCollection: return self._dict_data[item] return self._get_one(item) +_constants_by_iid_map = {} class _Interface: # An interface object. @@ -88,7 +92,6 @@ class _Interface: d = self.__dict__ d['_iidobj_'] = iid # Allows the C++ framework to treat this as a native IID. d['name'] = name - d['constants'] = None # Built first time an attribute is asked for. def __cmp__(self, other): this_iid = self._iidobj_ other_iid = getattr(other, "_iidobj_", other) @@ -105,13 +108,13 @@ class _Interface: raise AttributeError, "Can not set attributes on components.Interface objects" def __getattr__(self, attr): # Support constants as attributes. - c = self.constants + c = _constants_by_iid_map.get(self._iidobj_) if c is None: c = {} i = xpt.Interface(self._iidobj_) for c_ob in i.constants: c[c_ob.name] = c_ob.value - self.__dict__['constants'] = c + _constants_by_iid_map[self._iidobj_] = c if c.has_key(attr): return c[attr] raise AttributeError, "'%s' interfaces do not define a constant '%s'" % (self.name, attr) @@ -153,7 +156,7 @@ class _Class: import xpcom.client return xpcom.client.Component(self.contractid, _get_good_iid(iid)) def getService(self, iid = None): - return _xpcom.GetGlobalServiceManager().getServiceByContractID(self.contractid, _get_good_iid(iid)) + return serviceManager.getServiceByContractID(self.contractid, _get_good_iid(iid)) class _Classes(_ComponentCollection): def __init__(self): @@ -186,11 +189,10 @@ ID = _xpcom.IID class _ShutdownObserver: _com_interfaces_ = interfaces.nsIObserver def observe(self, service, topic, extra): - global manager - global interfaceInfoManager - global _shutdownObserver - manager = interfaceInfoManager = _shutdownObserver = None + global manager, interfaceInfoManager, _shutdownObserver, serviceManager, _constants_by_iid_map + manager = interfaceInfoManager = _shutdownObserver = serviceManager = _constants_by_iid_map = None xpcom.client._shutdown() + xpcom.server._shutdown() svc = _xpcom.GetGlobalServiceManager().getServiceByContractID("@mozilla.org/observer-service;1", interfaces.nsIObserverService) # Observers will be QI'd for a weak-reference, so we must keep the diff --git a/extensions/python/xpcom/server/__init__.py b/extensions/python/xpcom/server/__init__.py index 09dbb2c0f271..9f5f45d8e08d 100644 --- a/extensions/python/xpcom/server/__init__.py +++ b/extensions/python/xpcom/server/__init__.py @@ -26,7 +26,10 @@ from xpcom import _xpcom # of the real xpcom object. Presumably this "trace" object will delegate # to the real object, but presumably also taking some other action, such # as calling a profiler or debugger. -tracer = None +# tracer_unwrap is a function used to "unwrap" the tracer object. +# If is expected that tracer_unwrap will be called with an object +# previously returned by "tracer()". +tracer = tracer_unwrap = None # Wrap an instance in an interface (via a policy) def WrapObject(ob, iid, policy = None, bWrapClient = 1): @@ -42,7 +45,12 @@ def WrapObject(ob, iid, policy = None, bWrapClient = 1): # Unwrap a Python object back into the Python object def UnwrapObject(ob): - return _xpcom.UnwrapObject(ob)._obj_ + if ob is None: + return None + ret = _xpcom.UnwrapObject(ob)._obj_ + if tracer_unwrap is not None: + ret = tracer_unwrap(ret) + return ret # Create the main module for the Python loader. # This is a once only init process, and the returned object @@ -54,3 +62,7 @@ def NS_GetModule( serviceManager, nsIFile ): import loader iid = _xpcom.IID_nsIModule return WrapObject(loader.MakePythonComponentLoaderModule(serviceManager, nsIFile), iid, bWrapClient = 0) + +def _shutdown(): + from policy import _shutdown + _shutdown() diff --git a/extensions/python/xpcom/server/policy.py b/extensions/python/xpcom/server/policy.py index 58145673ff58..1ab0f7aa2041 100644 --- a/extensions/python/xpcom/server/policy.py +++ b/extensions/python/xpcom/server/policy.py @@ -68,15 +68,16 @@ def _GetNominatedInterfaces(obj): ## We cache class infos by class class_info_cache = {} -def GetClassInfoForClass(klass): - # Note we do not store the wrapped object in the class - this would - # present us with shutdown problems (ie, needing to clear the - # cache), and also messes with lifetime issues. +def GetClassInfoForObject(ob): + if xpcom.server.tracer_unwrap is not None: + ob = xpcom.server.tracer_unwrap(ob) + klass = ob.__class__ ci = class_info_cache.get(klass) if ci is None: ci = DefaultClassInfo(klass) + ci = xpcom.server.WrapObject(ci, _xpcom.IID_nsIClassInfo, bWrapClient = 0) class_info_cache[klass] = ci - return xpcom.server.WrapObject(ci, _xpcom.IID_nsIClassInfo, bWrapClient = 0) + return ci class DefaultClassInfo: _com_interfaces_ = _xpcom.IID_nsIClassInfo @@ -88,7 +89,12 @@ class DefaultClassInfo: self.implementationLanguage = 3 # Python - avoid lookups just for this self.flags = 0 # what to do here?? self.interfaces = None - + + def get_classID(self): + if self.classID is None: + raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED, "Class '%r' has no class ID" % (self.klass,)) + return self.classID + def getInterfaces(self): if self.interfaces is None: self.interfaces = _GetNominatedInterfaces(self.klass) @@ -133,7 +139,7 @@ class DefaultPolicy: # Always support nsIClassInfo if iid == _xpcom.IID_nsIClassInfo: - return GetClassInfoForClass(self._obj_.__class__) + return GetClassInfoForObject(self._obj_) # See if the instance has a QI # use lower-case "_query_interface_" as win32com does, and it doesnt really matter. @@ -276,3 +282,6 @@ class SupportsPrimitive: raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED) def toString(self): return str(self.get_data()) + +def _shutdown(): + class_info_cache.clear() diff --git a/extensions/python/xpcom/src/ErrorUtils.cpp b/extensions/python/xpcom/src/ErrorUtils.cpp index 705b60feecdc..2b642330fa6b 100644 --- a/extensions/python/xpcom/src/ErrorUtils.cpp +++ b/extensions/python/xpcom/src/ErrorUtils.cpp @@ -71,7 +71,7 @@ void PyXPCOM_LogError(const char *fmt, ...) else { streamout << "Traceback (most recent call last):\n"; streamout << szTraceback; - PyMem_Free((ANY *)szTraceback); + PyMem_Free((void *)szTraceback); } } PyObject *temp = PyObject_Str(exc_typ); diff --git a/extensions/python/xpcom/src/PyGBase.cpp b/extensions/python/xpcom/src/PyGBase.cpp index ecd0f2952885..0bf35f3ce8e5 100644 --- a/extensions/python/xpcom/src/PyGBase.cpp +++ b/extensions/python/xpcom/src/PyGBase.cpp @@ -44,7 +44,7 @@ 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); +PyG_Base *GetDefaultGateway(PyObject *instance); void AddDefaultGateway(PyObject *instance, nsISupports *gateway); PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway); @@ -78,11 +78,33 @@ 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_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 = 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; @@ -293,8 +315,8 @@ PyG_Base::QueryInterface(REFNSIID iid, void** ppv) } // 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; + 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; @@ -341,28 +363,6 @@ PyG_Base::QueryInterface(REFNSIID iid, void** ppv) } // 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; } @@ -370,7 +370,11 @@ nsrefcnt PyG_Base::AddRef(void) { nsrefcnt cnt = (nsrefcnt) PR_AtomicIncrement((PRInt32*)&mRefCnt); - NS_LOG_ADDREF(this, cnt, "PyG_Base", sizeof(*this)); +#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; } @@ -378,7 +382,10 @@ nsrefcnt PyG_Base::Release(void) { nsrefcnt cnt = (nsrefcnt) PR_AtomicDecrement((PRInt32*)&mRefCnt); - NS_LOG_RELEASE(this, cnt, "PyG_Base"); +#ifdef NS_BUILD_REFCNT_LOGGING + if (m_pBaseObject == NULL) + NS_LOG_RELEASE(this, cnt, refcntLogRepr); +#endif if ( cnt == 0 ) delete this; return cnt; @@ -701,10 +708,14 @@ PyObject *PyG_Base::UnwrapPythonObject(void) *********************************************************************/ -nsresult GetDefaultGateway(PyObject *instance, REFNSIID iid, void **ret) +PyG_Base *GetDefaultGateway(PyObject *policy) { - // NOTE: Instance is the real instance, _not_ the 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 pWeakRef; @@ -713,11 +724,16 @@ nsresult GetDefaultGateway(PyObject *instance, REFNSIID iid, void **ret) getter_AddRefs(pWeakRef), PR_FALSE)); Py_DECREF(ob_existing_weak); - if (ok) - return pWeakRef->QueryReferent( iid, ret); + 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 NS_ERROR_FAILURE; + return nsnull; } PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway) diff --git a/extensions/python/xpcom/src/PyGWeakReference.cpp b/extensions/python/xpcom/src/PyGWeakReference.cpp index 197a3ce473da..74073834f88d 100644 --- a/extensions/python/xpcom/src/PyGWeakReference.cpp +++ b/extensions/python/xpcom/src/PyGWeakReference.cpp @@ -34,6 +34,14 @@ PyXPCOM_GatewayWeakReference::PyXPCOM_GatewayWeakReference( PyG_Base *base ) { m_pBase = base; NS_INIT_REFCNT(); + +#ifdef NS_BUILD_REFCNT_LOGGING + // bloat view uses 40 chars - stick "(WR)" at the end of this position. + strncpy(refcntLogRepr, m_pBase->refcntLogRepr, sizeof(refcntLogRepr)); + refcntLogRepr[sizeof(refcntLogRepr)-1] = '\0'; + char *dest = refcntLogRepr + ((strlen(refcntLogRepr) > 36) ? 36 : strlen(refcntLogRepr)); + strcpy(dest, "(WR)"); +#endif // NS_BUILD_REFCNT_LOGGING } PyXPCOM_GatewayWeakReference::~PyXPCOM_GatewayWeakReference() @@ -45,7 +53,29 @@ PyXPCOM_GatewayWeakReference::~PyXPCOM_GatewayWeakReference() m_pBase = NULL; } -NS_IMPL_THREADSAFE_ISUPPORTS1(PyXPCOM_GatewayWeakReference, nsIWeakReference) +nsrefcnt +PyXPCOM_GatewayWeakReference::AddRef(void) +{ + nsrefcnt cnt = (nsrefcnt) PR_AtomicIncrement((PRInt32*)&mRefCnt); +#ifdef NS_BUILD_REFCNT_LOGGING + NS_LOG_ADDREF(this, cnt, refcntLogRepr, sizeof(*this)); +#endif + return cnt; +} + +nsrefcnt +PyXPCOM_GatewayWeakReference::Release(void) +{ + nsrefcnt cnt = (nsrefcnt) PR_AtomicDecrement((PRInt32*)&mRefCnt); +#ifdef NS_BUILD_REFCNT_LOGGING + NS_LOG_RELEASE(this, cnt, refcntLogRepr); +#endif + if ( cnt == 0 ) + delete this; + return cnt; +} + +NS_IMPL_THREADSAFE_QUERY_INTERFACE1(PyXPCOM_GatewayWeakReference, nsIWeakReference); NS_IMETHODIMP PyXPCOM_GatewayWeakReference::QueryReferent(REFNSIID iid, void * *ret) diff --git a/extensions/python/xpcom/src/PyIComponentManager.cpp b/extensions/python/xpcom/src/PyIComponentManager.cpp index 134c7748c536..6fdde120598b 100644 --- a/extensions/python/xpcom/src/PyIComponentManager.cpp +++ b/extensions/python/xpcom/src/PyIComponentManager.cpp @@ -28,16 +28,16 @@ // (c) 2000, ActiveState corp. #include "PyXPCOM_std.h" -#include +#include -static nsIComponentManager *GetI(PyObject *self) { - nsIID iid = NS_GET_IID(nsIComponentManager); +static nsIComponentManagerObsolete *GetI(PyObject *self) { + static const nsIID iid = NS_GET_IID(nsIComponentManagerObsolete); if (!Py_nsISupports::Check(self, iid)) { PyErr_SetString(PyExc_TypeError, "This object is not the correct interface"); return NULL; } - return (nsIComponentManager *)Py_nsISupports::GetI(self); + return (nsIComponentManagerObsolete *)Py_nsISupports::GetI(self); } static PyObject *PyCreateInstanceByContractID(PyObject *self, PyObject *args) @@ -50,7 +50,7 @@ static PyObject *PyCreateInstanceByContractID(PyObject *self, PyObject *args) PyErr_SetString(PyExc_ValueError, "2nd arg must be none"); return NULL; } - nsIComponentManager *pI = GetI(self); + nsIComponentManagerObsolete *pI = GetI(self); if (pI==NULL) return NULL; @@ -78,7 +78,7 @@ static PyObject *PyContractIDToClassID(PyObject *self, PyObject *args) char *pid; if (!PyArg_ParseTuple(args, "s", &pid)) return NULL; - nsIComponentManager *pI = GetI(self); + nsIComponentManagerObsolete *pI = GetI(self); if (pI==NULL) return NULL; @@ -104,7 +104,7 @@ static PyObject *PyCLSIDToContractID(PyObject *self, PyObject *args) return NULL; char *ret_pid = nsnull; char *ret_class = nsnull; - nsIComponentManager *pI = GetI(self); + nsIComponentManagerObsolete *pI = GetI(self); if (pI==NULL) return NULL; @@ -130,7 +130,7 @@ static PyObject *PyEnumerateCLSIDs(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "")) return NULL; - nsIComponentManager *pI = GetI(self); + nsIComponentManagerObsolete *pI = GetI(self); if (pI==NULL) return NULL; @@ -150,7 +150,7 @@ static PyObject *PyEnumerateContractIDs(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "")) return NULL; - nsIComponentManager *pI = GetI(self); + nsIComponentManagerObsolete *pI = GetI(self); if (pI==NULL) return NULL; diff --git a/extensions/python/xpcom/src/PyXPCOM.h b/extensions/python/xpcom/src/PyXPCOM.h index c36544be031f..3bc6aa7e7abe 100644 --- a/extensions/python/xpcom/src/PyXPCOM.h +++ b/extensions/python/xpcom/src/PyXPCOM.h @@ -361,6 +361,9 @@ public: // This means that once we have created it (and while we // are alive) it will never die. nsCOMPtr m_pWeakRef; +#ifdef NS_BUILD_REFCNT_LOGGING + char refcntLogRepr[64]; // sigh - I wish I knew how to use the Moz string classes :( OK for debug only tho. +#endif protected: PyG_Base(PyObject *instance, const nsIID &iid); virtual ~PyG_Base(); @@ -430,6 +433,9 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIWEAKREFERENCE; PyG_Base *m_pBase; // NO REF COUNT!!! +#ifdef NS_BUILD_REFCNT_LOGGING + char refcntLogRepr[41]; +#endif }; @@ -655,7 +661,7 @@ PyXPCOM_TypeObject *ClassName::type = NULL; // And the classes -PyXPCOM_INTERFACE_DECLARE(Py_nsIComponentManager, nsIComponentManager, PyMethods_IComponentManager) +PyXPCOM_INTERFACE_DECLARE(Py_nsIComponentManager, nsIComponentManagerObsolete, PyMethods_IComponentManager) PyXPCOM_INTERFACE_DECLARE(Py_nsIInterfaceInfoManager, nsIInterfaceInfoManager, PyMethods_IInterfaceInfoManager) PyXPCOM_INTERFACE_DECLARE(Py_nsIEnumerator, nsIEnumerator, PyMethods_IEnumerator) PyXPCOM_INTERFACE_DECLARE(Py_nsISimpleEnumerator, nsISimpleEnumerator, PyMethods_ISimpleEnumerator) diff --git a/extensions/python/xpcom/src/loader/pyloader.cpp b/extensions/python/xpcom/src/loader/pyloader.cpp index d231512fd844..c045ebdb68f1 100644 --- a/extensions/python/xpcom/src/loader/pyloader.cpp +++ b/extensions/python/xpcom/src/loader/pyloader.cpp @@ -225,7 +225,7 @@ static void LogError(const char *fmt, ...) else { streamout << "Traceback (most recent call last):\n"; streamout << szTraceback; - PyMem_Free((ANY *)szTraceback); + PyMem_Free((void *)szTraceback); } } PyObject *temp = PyObject_Str(exc_typ); diff --git a/extensions/python/xpcom/src/xpcom.cpp b/extensions/python/xpcom/src/xpcom.cpp index 80a0cfc0873e..af349d2fc640 100644 --- a/extensions/python/xpcom/src/xpcom.cpp +++ b/extensions/python/xpcom/src/xpcom.cpp @@ -56,7 +56,7 @@ extern void PyXPCOM_InterpreterState_Ensure(); #endif // XP_WIN -PyXPCOM_INTERFACE_DEFINE(Py_nsIComponentManager, nsIComponentManager, PyMethods_IComponentManager) +PyXPCOM_INTERFACE_DEFINE(Py_nsIComponentManager, nsIComponentManagerObsolete, PyMethods_IComponentManager) PyXPCOM_INTERFACE_DEFINE(Py_nsIInterfaceInfoManager, nsIInterfaceInfoManager, PyMethods_IInterfaceInfoManager) PyXPCOM_INTERFACE_DEFINE(Py_nsIEnumerator, nsIEnumerator, PyMethods_IEnumerator) PyXPCOM_INTERFACE_DEFINE(Py_nsISimpleEnumerator, nsISimpleEnumerator, PyMethods_ISimpleEnumerator) @@ -170,7 +170,7 @@ PyXPCOMMethod_NS_GetGlobalComponentManager(PyObject *self, PyObject *args) // Return a type based on the IID // Can not auto-wrap the interface info manager as it is critical to // building the support we need for autowrap. - return Py_nsISupports::PyObjectFromInterface(cm, NS_GET_IID(nsIComponentManager), PR_TRUE, PR_FALSE); + return Py_nsISupports::PyObjectFromInterface(cm, NS_GET_IID(nsIComponentManagerObsolete), PR_TRUE, PR_FALSE); } static PyObject * @@ -594,7 +594,14 @@ init_xpcom() { Py_nsIInterfaceInfo::InitType(dict); Py_nsIInputStream::InitType(dict); Py_nsIClassInfo::InitType(dict); - + + // yet another + { // temp scope nsIComponentManagerObsolete hack :( + PyObject *iid_ob = Py_nsIID::PyObjectFromIID(NS_GET_IID(nsIComponentManagerObsolete)); + PyDict_SetItemString(dict, "IID_nsIComponentManager", iid_ob); + Py_DECREF(iid_ob); + } // end temp scope + // We have special support for proxies - may as well add their constants! REGISTER_INT(PROXY_SYNC); REGISTER_INT(PROXY_ASYNC); diff --git a/extensions/python/xpcom/test/regrtest.py b/extensions/python/xpcom/test/regrtest.py index 6cddafcf7dbe..2989a1520dae 100644 --- a/extensions/python/xpcom/test/regrtest.py +++ b/extensions/python/xpcom/test/regrtest.py @@ -29,4 +29,10 @@ for arg in sys.argv[1:]: if arg[0] not in "-/": tests.append(arg) tests = tests or test.regrtest.findtests(path, []) -test.regrtest.main(tests, path) \ No newline at end of file +try: + test.regrtest.main(tests, path) +finally: + from xpcom import _xpcom + _xpcom.NS_ShutdownXPCOM() # To get leak stats and otherwise ensure life is good. + + diff --git a/extensions/python/xpcom/test/test_component/makefile.win b/extensions/python/xpcom/test/test_component/makefile.win index 8758684f26d6..abb9b37a1959 100644 --- a/extensions/python/xpcom/test/test_component/makefile.win +++ b/extensions/python/xpcom/test/test_component/makefile.win @@ -17,7 +17,7 @@ # DEPTH =..\..\..\..\.. -MODULE=pyxpcom_test + ################################################################################ ## exports diff --git a/extensions/python/xpcom/test/test_weakreferences.py b/extensions/python/xpcom/test/test_weakreferences.py index db0523ef695b..74f9e62442e0 100644 --- a/extensions/python/xpcom/test/test_weakreferences.py +++ b/extensions/python/xpcom/test/test_weakreferences.py @@ -19,6 +19,12 @@ from xpcom import components, _xpcom import xpcom.server, xpcom.client +try: + from sys import gettotalrefcount +except ImportError: + # Not a Debug build - assume no references (can't be leaks then :-) + gettotalrefcount = lambda: 0 + num_alive = 0 class koTestSimple: @@ -61,6 +67,30 @@ def test(): if num_alive != 0: raise RuntimeError, "Eeek - there are %d objects alive" % (num_alive,) if wr() is not None: raise RuntimeError, "Our weak-reference is not returning None when it should!" +def test_refcount(num_loops=-1): + # Do the test lots of times - can help shake-out ref-count bugs. + if num_loops == -1: num_loops = 10 + for i in xrange(num_loops): + test() + + if i==0: + # First loop is likely to "leak" as we cache things. + # Leaking after that is a problem. + num_refs = gettotalrefcount() + + lost = gettotalrefcount() - num_refs + # Sometimes we get spurious counts off by 1 or 2. + # This can't indicate a real leak, as we have looped + # more than twice! + if abs(lost)>2: + print "*** Lost %d references" % (lost,) -test() +test_refcount() + print "Weak-reference tests appear to have worked!" +if __name__=='__main__': + _xpcom.NS_ShutdownXPCOM() + ni = xpcom._xpcom._GetInterfaceCount() + ng = xpcom._xpcom._GetGatewayCount() + if ni or ng: + print "********* WARNING - Leaving with %d/%d objects alive" % (ni,ng)