A little black magic... When the C++ object (for a window or

whatever) is deleted there is no way to force the Python shadow object
to also be destroyed and clean up all references to it.  This leads to
crashes if the shadow object tries to call a method with the old C++
pointer...  The black magic I've done is to replace the __class__ in the
Python instanc object with a class that raises an exception whenever a
method call is attempted.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@15059 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2002-04-09 22:01:45 +00:00
parent 31fa82942c
commit 4acff284f9
18 changed files with 207 additions and 84 deletions

View File

@@ -172,7 +172,7 @@ PyObject* wxPyMake_wxShapeEvtHandler(wxShapeEvtHandler* source) {
// already be a pointer to a Python object that we can use
// in the OOR data.
wxShapeEvtHandler* seh = (wxShapeEvtHandler*)source;
wxPyClientData* data = (wxPyClientData*)seh->GetClientObject();
wxPyOORClientData* data = (wxPyOORClientData*)seh->GetClientObject();
if (data) {
target = data->m_obj;
Py_INCREF(target);
@@ -181,7 +181,7 @@ PyObject* wxPyMake_wxShapeEvtHandler(wxShapeEvtHandler* source) {
if (! target) {
target = wxPyMake_wxObject2(source, FALSE);
if (target != Py_None)
((wxShapeEvtHandler*)source)->SetClientObject(new wxPyClientData(target));
((wxShapeEvtHandler*)source)->SetClientObject(new wxPyOORClientData(target));
}
return target;
}

View File

@@ -221,7 +221,7 @@ PyObject* wxPyMake_wxShapeEvtHandler(wxShapeEvtHandler* source) {
// already be a pointer to a Python object that we can use
// in the OOR data.
wxShapeEvtHandler* seh = (wxShapeEvtHandler*)source;
wxPyClientData* data = (wxPyClientData*)seh->GetClientObject();
wxPyOORClientData* data = (wxPyOORClientData*)seh->GetClientObject();
if (data) {
target = data->m_obj;
Py_INCREF(target);
@@ -230,7 +230,7 @@ PyObject* wxPyMake_wxShapeEvtHandler(wxShapeEvtHandler* source) {
if (! target) {
target = wxPyMake_wxObject2(source, FALSE);
if (target != Py_None)
((wxShapeEvtHandler*)source)->SetClientObject(new wxPyClientData(target));
((wxShapeEvtHandler*)source)->SetClientObject(new wxPyOORClientData(target));
}
return target;
}

View File

@@ -1249,7 +1249,7 @@ static PyObject *_wrap_wxPyShapeEvtHandler_Destroy(PyObject *self, PyObject *arg
}
static void wxPyShapeEvtHandler__setOORInfo(wxPyShapeEvtHandler *self,PyObject * _self) {
self->SetClientObject(new wxPyClientData(_self));
self->SetClientObject(new wxPyOORClientData(_self));
}
static PyObject *_wrap_wxPyShapeEvtHandler__setOORInfo(PyObject *self, PyObject *args, PyObject *kwargs) {
PyObject * _resultobj;

View File

@@ -99,7 +99,7 @@ public:
%addmethods { void Destroy() { delete self; } }
%addmethods {
void _setOORInfo(PyObject* _self) {
self->SetClientObject(new wxPyClientData(_self));
self->SetClientObject(new wxPyOORClientData(_self));
}
}

View File

@@ -647,6 +647,26 @@ def wxPyTypeCast(obj, typeStr):
return theObj
#----------------------------------------------------------------------------
class _wxPyDeadObject:
"""
Instances of wx objects that are OOR capable will have their __class__
changed to this class when the C++ object is deleted. This should help
prevent crashes due to referencing a bogus C++ pointer.
"""
def __repr__( self ):
if not hasattr(self, "_name"):
self._name = "[unknown]"
return 'wxPython wrapper for deleted %s object!!! Programming logic error' % self._name
def __getattr__( self, *args ):
if not hasattr(self, "_name"):
self._name = "[unknown]"
raise ValueError, 'Attempt to access attribute of a deleted %s object' % self._name
#----------------------------------------------------------------------
#----------------------------------------------------------------------
@@ -763,10 +783,10 @@ class wxPyWidgetTester(wxApp):
self.frame.Show(true)
#----------------------------------------------------------------------------
# DO NOT hold any other references to this object. This is how we know when
# to cleanup system resources that wxWin is holding. When this module is
# unloaded, the refcount on __cleanMeUp goes to zero and it calls the
# wxApp_CleanUp function.
# DO NOT hold any other references to this object. This is how we
# know when to cleanup system resources that wxWin is holding. When
# the sys module is unloaded, the refcount on sys.__wxPythonCleanup
# goes to zero and it calls the wxApp_CleanUp function.
class __wxPyCleanup:
def __init__(self):

View File

@@ -74,6 +74,9 @@ static void wxPyCoreAPI_IMPORT() {
#define wxArrayString2PyList_helper(a) (wxPyCoreAPIPtr->p_wxArrayString2PyList_helper(a))
#define wxArrayInt2PyList_helper(a) (wxPyCoreAPIPtr->p_wxArrayInt2PyList_helper(a))
#define wxPyClientData_dtor(a) (wxPyCoreAPIPtr->p_wxPyClientData_dtor(a))
#define wxPyUserData_dtor(a) (wxPyCoreAPIPtr->p_wxPyUserData_dtor(a))
#define wxPyOORClientData_dtor(a) (wxPyCoreAPIPtr->p_wxPyOORClientData_dtor(a))
// This one is special. It's the first function called in SWIG generated
// modules, so we'll use it to also import the API.

View File

@@ -295,6 +295,7 @@ void __wxCleanup() {
static PyObject* wxPython_dict = NULL;
static PyObject* wxPyPtrTypeMap = NULL;
PyObject* __wxSetDictionary(PyObject* /* self */, PyObject* args)
{
@@ -334,6 +335,52 @@ PyObject* __wxSetDictionary(PyObject* /* self */, PyObject* args)
return Py_None;
}
//---------------------------------------------------------------------------
void wxPyClientData_dtor(wxPyClientData* self) {
wxPyBeginBlockThreads();
Py_DECREF(self->m_obj);
wxPyEndBlockThreads();
}
void wxPyUserData_dtor(wxPyUserData* self) {
wxPyBeginBlockThreads();
Py_DECREF(self->m_obj);
wxPyEndBlockThreads();
}
// This is called when an OOR controled object is being destroyed. Although
// the C++ object is going away there is no way to force the Python object
// (and all references to it) to die too. This causes problems (crashes) in
// wxPython when a python shadow object attempts to call a C++ method using
// the now bogus pointer... So to try and prevent this we'll do a little black
// magic and change the class of the python instance to a class that will
// raise an exception for any attempt to call methods with it. See
// _wxPyDeadObject in _extras.py for the implementation of this class.
void wxPyOORClientData_dtor(wxPyOORClientData* self) {
static PyObject* deadObjectClass = NULL;
wxPyBeginBlockThreads();
if (deadObjectClass == NULL) {
deadObjectClass = PyDict_GetItemString(wxPython_dict, "_wxPyDeadObject");
wxASSERT_MSG(deadObjectClass != NULL, wxT("Can't get _wxPyDeadObject class!"));
Py_INCREF(deadObjectClass);
}
// Clear the instance's dictionary, put the name of the old class into the
// instance, and then reset the class to be the dead class.
if (self->m_obj->ob_refcnt > 1) { // but only if there is more than one reference
wxASSERT_MSG(PyInstance_Check(self->m_obj), wxT("m_obj not an instance!?!?!"));
PyInstanceObject* inst = (PyInstanceObject*)self->m_obj;
PyDict_Clear(inst->in_dict);
PyDict_SetItemString(inst->in_dict, "_name", inst->in_class->cl_name);
inst->in_class = (PyClassObject*)deadObjectClass;
Py_INCREF(deadObjectClass);
}
wxPyEndBlockThreads();
}
//---------------------------------------------------------------------------
// Stuff used by OOR to find the right wxPython class type to return and to
@@ -344,6 +391,7 @@ PyObject* __wxSetDictionary(PyObject* /* self */, PyObject* args)
// is not the same as the shadow class name, for example wxPyTreeCtrl
// vs. wxTreeCtrl. It needs to be referenced in Python as well as from C++,
// so we'll just make it a Python dictionary in the wx module's namespace.
// (See __wxSetDictionary)
void wxPyPtrTypeMap_Add(const char* commonName, const char* ptrName) {
if (! wxPyPtrTypeMap)
wxPyPtrTypeMap = PyDict_New();
@@ -379,7 +427,7 @@ PyObject* wxPyMake_wxObject(wxObject* source, bool checkEvtHandler) {
if (checkEvtHandler && wxIsKindOf(source, wxEvtHandler)) {
isEvtHandler = TRUE;
wxEvtHandler* eh = (wxEvtHandler*)source;
wxPyClientData* data = (wxPyClientData*)eh->GetClientObject();
wxPyOORClientData* data = (wxPyOORClientData*)eh->GetClientObject();
if (data) {
target = data->m_obj;
Py_INCREF(target);
@@ -400,9 +448,9 @@ PyObject* wxPyMake_wxObject(wxObject* source, bool checkEvtHandler) {
if (info) {
target = wxPyConstructObject(source, name, klass, FALSE);
if (target && isEvtHandler)
((wxEvtHandler*)source)->SetClientObject(new wxPyClientData(target));
((wxEvtHandler*)source)->SetClientObject(new wxPyOORClientData(target));
} else {
wxString msg("wxPython class not found for ");
wxString msg(wxT("wxPython class not found for "));
msg += source->GetClassInfo()->GetClassName();
PyErr_SetString(PyExc_NameError, msg.mbc_str());
target = NULL;
@@ -423,7 +471,7 @@ PyObject* wxPyMake_wxSizer(wxSizer* source) {
// already be a pointer to a Python object that we can use
// in the OOR data.
wxSizer* sz = (wxSizer*)source;
wxPyClientData* data = (wxPyClientData*)sz->GetClientObject();
wxPyOORClientData* data = (wxPyOORClientData*)sz->GetClientObject();
if (data) {
target = data->m_obj;
Py_INCREF(target);
@@ -432,7 +480,7 @@ PyObject* wxPyMake_wxSizer(wxSizer* source) {
if (! target) {
target = wxPyMake_wxObject(source, FALSE);
if (target != Py_None)
((wxSizer*)source)->SetClientObject(new wxPyClientData(target));
((wxSizer*)source)->SetClientObject(new wxPyOORClientData(target));
}
return target;
}

View File

@@ -101,6 +101,9 @@ bool wxRealPoint_helper(PyObject* source, wxRealPoint** obj);
bool wxRect_helper(PyObject* source, wxRect** obj);
bool wxColour_helper(PyObject* source, wxColour** obj);
//----------------------------------------------------------------------
// Other helpful stuff
#if PYTHON_API_VERSION < 1009
#define PySequence_Fast_GET_ITEM(o, i) \
(PyList_Check(o) ? PyList_GET_ITEM(o, i) : PyTuple_GET_ITEM(o, i))
@@ -113,9 +116,9 @@ bool _4int_seq_helper(PyObject* source, int* i1, int* i2, int* i3, int* i4);
PyObject* wxArrayString2PyList_helper(const wxArrayString& arr);
PyObject* wxArrayInt2PyList_helper(const wxArrayInt& arr);
#define RETURN_NONE() { Py_INCREF(Py_None); return Py_None; }
#define DECLARE_DEF_STRING(name) static wxString wxPy##name(wx##name)
#define DECLARE_DEF_STRING(name) static const wxString wxPy##name(wx##name)
#define DECLARE_DEF_STRING2(name,val) static const wxString wxPy##name(val)
//----------------------------------------------------------------------
@@ -201,6 +204,18 @@ public:
};
//----------------------------------------------------------------------
// Forward decalre a few things used in the exported API
class wxPyClientData;
class wxPyUserData;
class wxPyOORClientData;
void wxPyClientData_dtor(wxPyClientData* self);
void wxPyUserData_dtor(wxPyUserData* self);
void wxPyOORClientData_dtor(wxPyOORClientData* self);
//---------------------------------------------------------------------------
// Export a C API in a struct. Other modules will be able to load this from
// the wxc module and will then have safe access to these functions, even if
@@ -256,12 +271,70 @@ struct wxPyCoreAPI {
void (*p_wxPyPtrTypeMap_Add)(const char* commonName, const char* ptrName);
PyObject* (*p_wxArrayString2PyList_helper)(const wxArrayString& arr);
PyObject* (*p_wxArrayInt2PyList_helper)(const wxArrayInt& arr);
void (*p_wxPyClientData_dtor)(wxPyClientData*);
void (*p_wxPyUserData_dtor)(wxPyUserData*);
void (*p_wxPyOORClientData_dtor)(wxPyOORClientData*);
};
#ifdef wxPyUSE_EXPORT
static wxPyCoreAPI* wxPyCoreAPIPtr = NULL; // Each module needs one, but may not use it.
static wxPyCoreAPI* wxPyCoreAPIPtr = NULL; // Each module needs one, but doesn't have to use it.
#endif
//---------------------------------------------------------------------------
class wxPyUserData : public wxObject {
public:
wxPyUserData(PyObject* obj) {
m_obj = obj;
Py_INCREF(m_obj);
}
~wxPyUserData() {
#ifdef wxPyUSE_EXPORT
wxPyCoreAPIPtr->p_wxPyUserData_dtor(this);
#else
wxPyUserData_dtor(this);
#endif
}
PyObject* m_obj;
};
class wxPyClientData : public wxClientData {
public:
wxPyClientData(PyObject* obj) {
m_obj = obj;
Py_INCREF(m_obj);
}
~wxPyClientData() {
#ifdef wxPyUSE_EXPORT
wxPyCoreAPIPtr->p_wxPyClientData_dtor(this);
#else
wxPyClientData_dtor(this);
#endif
}
PyObject* m_obj;
};
class wxPyOORClientData : public wxPyClientData {
public:
wxPyOORClientData(PyObject* obj)
: wxPyClientData(obj) {}
~wxPyOORClientData() {
#ifdef wxPyUSE_EXPORT
wxPyCoreAPIPtr->p_wxPyOORClientData_dtor(this);
#else
wxPyOORClientData_dtor(this);
#endif
}
};
//---------------------------------------------------------------------------
// This class holds an instance of a Python Shadow Class object and assists
// with looking up and invoking Python callback methods from C++ virtual
@@ -312,53 +385,6 @@ void wxPyCBH_delete(wxPyCallbackHelper* cbh);
//----------------------------------------------------------------------
class wxPyUserData : public wxObject {
public:
wxPyUserData(PyObject* obj) {
m_obj = obj;
Py_INCREF(m_obj);
}
~wxPyUserData() {
#ifdef wxPyUSE_EXPORT
wxPyCoreAPIPtr->p_wxPyBeginBlockThreads();
Py_DECREF(m_obj);
wxPyCoreAPIPtr->p_wxPyEndBlockThreads();
#else
wxPyBeginBlockThreads();
Py_DECREF(m_obj);
wxPyEndBlockThreads();
#endif
}
PyObject* m_obj;
};
class wxPyClientData : public wxClientData {
public:
wxPyClientData(PyObject* obj) {
m_obj = obj;
Py_INCREF(m_obj);
}
~wxPyClientData() {
#ifdef wxPyUSE_EXPORT
wxPyCoreAPIPtr->p_wxPyBeginBlockThreads();
Py_DECREF(m_obj);
wxPyCoreAPIPtr->p_wxPyEndBlockThreads();
#else
wxPyBeginBlockThreads();
Py_DECREF(m_obj);
wxPyEndBlockThreads();
#endif
}
PyObject* m_obj;
};
//---------------------------------------------------------------------------
// These macros are used to implement the virtual methods that should

View File

@@ -866,14 +866,14 @@ class wxBufferedDC(wxBufferedDCPtr):
def __init__(self,*_args,**_kwargs):
self.this = apply(gdic.new_wxBufferedDC,_args,_kwargs)
self.thisown = 1
self._dc = _args[0] # save a ref so it won't be deleted before self
self._dc = _args[0] # save a ref so the other dc won't be deleted before self
def wxBufferedDCInternalBuffer(*_args,**_kwargs):
val = wxBufferedDCPtr(apply(gdic.new_wxBufferedDCInternalBuffer,_args,_kwargs))
val.thisown = 1
val._dc = _args[0] # save a ref so it won't be deleted before self
val._dc = _args[0] # save a ref so the other dc won't be deleted before self
return val

View File

@@ -860,7 +860,7 @@ static void *SwigwxSizerTowxObject(void *ptr) {
}
static void wxSizer__setOORInfo(wxSizer *self,PyObject * _self) {
self->SetClientObject(new wxPyClientData(_self));
self->SetClientObject(new wxPyOORClientData(_self));
}
static PyObject *_wrap_wxSizer__setOORInfo(PyObject *self, PyObject *args, PyObject *kwargs) {
PyObject * _resultobj;

View File

@@ -55,14 +55,13 @@ extern PyObject *SWIG_newvarlink(void);
#define SWIG_name "utilsc"
#include "export.h"
#include "helpers.h"
#include <wx/config.h>
#include <wx/fileconf.h>
#include <wx/datetime.h>
// Put some wx default wxChar* values into wxStrings.
static const wxChar* wxDateFormatStr = wxT("sashWindow");
DECLARE_DEF_STRING(DateFormatStr);
DECLARE_DEF_STRING2(DateFormatStr, wxT("sashWindow"));
static const wxString wxPyEmptyString(wxT(""));

View File

@@ -548,7 +548,7 @@ static PyObject *_wrap_wxEvtHandler_Disconnect(PyObject *self, PyObject *args, P
}
static void wxEvtHandler__setOORInfo(wxEvtHandler *self,PyObject * _self) {
self->SetClientObject(new wxPyClientData(_self));
self->SetClientObject(new wxPyOORClientData(_self));
}
static PyObject *_wrap_wxEvtHandler__setOORInfo(PyObject *self, PyObject *args, PyObject *kwargs) {
PyObject * _resultobj;

View File

@@ -677,7 +677,11 @@ static wxPyCoreAPI API = {
wxPyMake_wxSizer,
wxPyPtrTypeMap_Add,
wxArrayString2PyList_helper,
wxArrayInt2PyList_helper
wxArrayInt2PyList_helper,
wxPyClientData_dtor,
wxPyUserData_dtor,
wxPyOORClientData_dtor
};

View File

@@ -1554,6 +1554,26 @@ def wxPyTypeCast(obj, typeStr):
return theObj
#----------------------------------------------------------------------------
class _wxPyDeadObject:
"""
Instances of wx objects that are OOR capable will have their __class__
changed to this class when the C++ object is deleted. This should help
prevent crashes due to referencing a bogus C++ pointer.
"""
def __repr__( self ):
if not hasattr(self, "_name"):
self._name = "[unknown]"
return 'wxPython wrapper for deleted %s object!!! Programming logic error' % self._name
def __getattr__( self, *args ):
if not hasattr(self, "_name"):
self._name = "[unknown]"
raise ValueError, 'Attempt to access attribute of a deleted %s object' % self._name
#----------------------------------------------------------------------
#----------------------------------------------------------------------
@@ -1670,10 +1690,10 @@ class wxPyWidgetTester(wxApp):
self.frame.Show(true)
#----------------------------------------------------------------------------
# DO NOT hold any other references to this object. This is how we know when
# to cleanup system resources that wxWin is holding. When this module is
# unloaded, the refcount on __cleanMeUp goes to zero and it calls the
# wxApp_CleanUp function.
# DO NOT hold any other references to this object. This is how we
# know when to cleanup system resources that wxWin is holding. When
# the sys module is unloaded, the refcount on sys.__wxPythonCleanup
# goes to zero and it calls the wxApp_CleanUp function.
class __wxPyCleanup:
def __init__(self):

View File

@@ -97,7 +97,7 @@ public:
%addmethods {
void _setOORInfo(PyObject* _self) {
self->SetClientObject(new wxPyClientData(_self));
self->SetClientObject(new wxPyOORClientData(_self));
}
}

View File

@@ -14,7 +14,7 @@
%module utils
%{
#include "export.h"
#include "helpers.h"
#include <wx/config.h>
#include <wx/fileconf.h>
#include <wx/datetime.h>
@@ -23,8 +23,7 @@
//---------------------------------------------------------------------------
%{
// Put some wx default wxChar* values into wxStrings.
static const wxChar* wxDateFormatStr = wxT("sashWindow");
DECLARE_DEF_STRING(DateFormatStr);
DECLARE_DEF_STRING2(DateFormatStr, wxT("sashWindow"));
static const wxString wxPyEmptyString(wxT(""));
%}

View File

@@ -87,7 +87,7 @@ public:
%addmethods {
void _setOORInfo(PyObject* _self) {
self->SetClientObject(new wxPyClientData(_self));
self->SetClientObject(new wxPyOORClientData(_self));
}
}
};

View File

@@ -202,7 +202,11 @@ static wxPyCoreAPI API = {
wxPyMake_wxSizer,
wxPyPtrTypeMap_Add,
wxArrayString2PyList_helper,
wxArrayInt2PyList_helper
wxArrayInt2PyList_helper,
wxPyClientData_dtor,
wxPyUserData_dtor,
wxPyOORClientData_dtor
};