From 5fe0aca9c79ac59725099d516792d4d46d95700f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 18 Jul 2018 23:48:40 +0200 Subject: [PATCH] Fixed a segfault on help(pya.Box) Needed to refactor the class hierarchy of the Python classes. Basically the module specific base class was removed as it does not provide any benefit. The object layout of the PyObject specialization was modified such that the payload is attached to the end. This is compatible with the hidden extensions which Python adds to normal objects. --- src/pya/pya/pyaConvert.cc | 23 +++++---- src/pya/pya/pyaConvert.h | 2 +- src/pya/pya/pyaMarshal.cc | 14 +++--- src/pya/pya/pyaModule.cc | 102 ++++++++++++++++---------------------- src/pya/pya/pyaModule.h | 2 - src/pya/pya/pyaObject.cc | 30 +++++------ src/pya/pya/pyaObject.h | 39 +++++++++++++-- testdata/python/basic.py | 1 + 8 files changed, 114 insertions(+), 99 deletions(-) diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc index 2d771a2e5..41d28cf2c 100644 --- a/src/pya/pya/pyaConvert.cc +++ b/src/pya/pya/pyaConvert.cc @@ -291,7 +291,7 @@ tl::Variant python2c_func::operator() (PyObject *rval) const gsi::ClassBase *cls = PythonModule::cls_for_type (Py_TYPE (rval)); if (cls) { - PYAObjectBase *p = (PYAObjectBase *) rval; + PYAObjectBase *p = PYAObjectBase::from_pyobject (rval); // employ the tl::Variant binding capabilities of the Expression binding to derive the // variant value. @@ -359,9 +359,8 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ArgType &atype) * object turns into a non-const one. This may be confusing but provides a certain level * of "constness", at least until there is another non-const reference for that object. */ -void correct_constness (PyObject *obj, bool const_required) +void correct_constness (PYAObjectBase *p, bool const_required) { - PYAObjectBase *p = (PYAObjectBase *) obj; if (p->const_ref () && ! const_required) { // promote to non-const object p->set_const_ref (false); @@ -421,20 +420,21 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo tl_assert (type != NULL); // create a instance and copy the value - PYAObjectBase *new_object = (PYAObjectBase *) type->tp_alloc (type, 0); - new (new_object) PYAObjectBase (clsact); + PyObject *new_pyobject = type->tp_alloc (type, 0); + PYAObjectBase *new_object = PYAObjectBase::from_pyobject_unsafe (new_pyobject); + new (new_object) PYAObjectBase (clsact, new_pyobject); clsact->assign (new_object->obj (), obj); - return new_object; + return new_pyobject; } else if (pya_object) { // we have a that is located in C++ space but is supposed to get attached // a Python object. If it already has, we simply return a reference to this - Py_INCREF (pya_object); + Py_INCREF (pya_object->py_object ()); correct_constness (pya_object, is_const); - return pya_object; + return pya_object->py_object (); } else { @@ -442,10 +442,11 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo tl_assert (type != NULL); // create a instance and copy the value - PYAObjectBase *new_object = (PYAObjectBase *) type->tp_alloc (type, 0); - new (new_object) PYAObjectBase (clsact); + PyObject *new_pyobject = type->tp_alloc (type, 0); + PYAObjectBase *new_object = PYAObjectBase::from_pyobject_unsafe (new_pyobject); + new (new_object) PYAObjectBase (clsact, new_pyobject); new_object->set (obj, pass_obj, is_const, can_destroy); - return new_object; + return new_pyobject; } } diff --git a/src/pya/pya/pyaConvert.h b/src/pya/pya/pyaConvert.h index 57e0dccf2..8aab23136 100644 --- a/src/pya/pya/pyaConvert.h +++ b/src/pya/pya/pyaConvert.h @@ -352,7 +352,7 @@ template struct python2c_func tl_assert (cls_decl != 0); tl_assert (is_derived_from (cls_decl, typeid (T))); - PYAObjectBase *p = (PYAObjectBase *) (rval); + PYAObjectBase *p = PYAObjectBase::from_pyobject (rval); return *((T *)p->obj ()); } }; diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 719ff51e1..0b265280d 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -187,7 +187,7 @@ struct get_boxed_value_func throw tl::Exception (tl::sprintf (tl::to_string (tr ("Passing an object to pointer or reference requires a boxed type (pya.%s)")), bt->name ())); } - PYAObjectBase *p = (PYAObjectBase *) arg; + PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); gsi::Value *bo = reinterpret_cast (p->obj ()); if (bo) { *ret = bo->value ().template morph ().native_ptr (); @@ -389,7 +389,7 @@ struct writer if (cls_decl->is_derived_from (atype.cls ())) { - PYAObjectBase *p = (PYAObjectBase *) (arg); + PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); if (cls_decl->adapted_type_info ()) { // resolved adapted type @@ -400,7 +400,7 @@ struct writer } else if (cls_decl->can_convert_to (atype.cls ())) { - PYAObjectBase *p = (PYAObjectBase *) (arg); + PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); // We can convert objects for cref and cptr, but ownership over these objects is not transferred. // Hence we have to keep them on the heap. @@ -421,7 +421,7 @@ struct writer if (cls_decl->is_derived_from (atype.cls ())) { - PYAObjectBase *p = (PYAObjectBase *) (arg); + PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); if (cls_decl->adapted_type_info ()) { // resolved adapted type @@ -432,7 +432,7 @@ struct writer } else if (cls_decl->can_convert_to (atype.cls ())) { - PYAObjectBase *p = (PYAObjectBase *) (arg); + PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); aa->write (atype.cls ()->create_obj_from (cls_decl, p->obj ())); } else { @@ -471,7 +471,7 @@ push_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PyObject *arg, tl template struct reader { - void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &arg, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &arg, tl::Heap *heap) { if (arg.is_ref ()) { *ret = c2python (rr->template read (*heap)); @@ -506,7 +506,7 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &arg, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &arg, tl::Heap *heap) { tl_assert (! arg.is_cref ()); tl_assert (! arg.is_ref ()); diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 40b5ed262..793f4f2ad 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -575,9 +575,9 @@ static std::string extract_python_name (const std::string &name) static void pya_object_deallocate (PyObject *self) { - PYAObjectBase *p = (PYAObjectBase *) self; + PYAObjectBase *p = PYAObjectBase::from_pyobject (self); p->~PYAObjectBase (); - Py_TYPE (self)->tp_free ((PyObject*)self); + Py_TYPE (self)->tp_free (self); } /** @@ -602,9 +602,10 @@ static PyObject * pya_object_new (PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/) { // create the object - PYAObjectBase *self = (PYAObjectBase *) type->tp_alloc (type, 0); - new (self) PYAObjectBase (PythonModule::cls_for_type (type)); - return (PyObject *) self; + PyObject *self_pyobject = type->tp_alloc (type, 0); + PYAObjectBase *self = PYAObjectBase::from_pyobject_unsafe (self_pyobject); + new (self) PYAObjectBase (PythonModule::cls_for_type (type), self_pyobject); + return self_pyobject; } // -------------------------------------------------------------------------- @@ -619,7 +620,7 @@ method_name_from_id (int mid, PyObject *self) const gsi::ClassBase *cls_decl = 0; if (! PyType_Check (self)) { - PYAObjectBase *p = (PYAObjectBase *) self; + PYAObjectBase *p = PYAObjectBase::from_pyobject (self); cls_decl = p->cls_decl (); } else { cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self); @@ -652,7 +653,7 @@ property_name_from_id (int mid, PyObject *self) const gsi::ClassBase *cls_decl = 0; if (! PyType_Check (self)) { - PYAObjectBase *p = (PYAObjectBase *) self; + PYAObjectBase *p = PYAObjectBase::from_pyobject (self); cls_decl = p->cls_decl (); } else { cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self); @@ -684,7 +685,7 @@ get_return_value (PYAObjectBase *self, gsi::SerialArgs &retlist, const gsi::Meth if (meth->ret_type ().is_iter ()) { gsi::IterAdaptorAbstractBase *iter = (gsi::IterAdaptorAbstractBase *) retlist.read (heap); - ret = (PyObject *) PYAIteratorObject::create (self, iter, &meth->ret_type ()); + ret = (PyObject *) PYAIteratorObject::create (self ? self->py_object () : 0, iter, &meth->ret_type ()); } else { @@ -702,7 +703,7 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict) PYAObjectBase *p = 0; if (! PyType_Check (self)) { - p = (PYAObjectBase *) self; + p = PYAObjectBase::from_pyobject (self); cls_decl = p->cls_decl (); } else { cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self); @@ -869,10 +870,11 @@ object_dup (PyObject *self, PyObject *args) throw tl::Exception (tl::to_string (tr ("No copy constructor provided for class '%s'")), cls_decl_self->name ()); } - PYAObjectBase *new_object = (PYAObjectBase *) Py_TYPE (self)->tp_alloc (Py_TYPE (self), 0); + PyObject *new_object = Py_TYPE (self)->tp_alloc (Py_TYPE (self), 0); PythonRef obj (new_object); - new (new_object) PYAObjectBase (cls_decl_self); - new_object->set (cls_decl_self->clone (((PYAObjectBase *) self)->obj ()), true, false, false); + PYAObjectBase *new_pya_base = PYAObjectBase::from_pyobject_unsafe (new_object); + new (new_pya_base) PYAObjectBase (cls_decl_self, new_object); + new_pya_base->set (cls_decl_self->clone (PYAObjectBase::from_pyobject (self)->obj ()), true, false, false); return obj.release (); } @@ -901,7 +903,7 @@ object_assign (PyObject *self, PyObject *args) throw tl::Exception (tl::to_string (tr ("No assignment provided for class '%s'")), cls_decl_self->name ()); } - cls_decl_self->assign (((PYAObjectBase *) self)->obj (), ((PYAObjectBase *) src)->obj ()); + cls_decl_self->assign ((PYAObjectBase::from_pyobject (self))->obj (), (PYAObjectBase::from_pyobject (src))->obj ()); Py_INCREF (self); return self; @@ -997,7 +999,7 @@ object_create (PyObject *self, PyObject *args) return NULL; } - ((PYAObjectBase *) self)->obj (); + (PYAObjectBase::from_pyobject (self))->obj (); Py_RETURN_NONE; } @@ -1011,7 +1013,7 @@ object_release (PyObject *self, PyObject *args) return NULL; } - ((PYAObjectBase *) self)->release (); + (PYAObjectBase::from_pyobject (self))->release (); Py_RETURN_NONE; } @@ -1025,7 +1027,7 @@ object_keep (PyObject *self, PyObject *args) return NULL; } - ((PYAObjectBase *) self)->keep (); + (PYAObjectBase::from_pyobject (self))->keep (); Py_RETURN_NONE; } @@ -1039,7 +1041,7 @@ object_destroy (PyObject *self, PyObject *args) return NULL; } - ((PYAObjectBase *) self)->destroy (); + (PYAObjectBase::from_pyobject (self))->destroy (); Py_RETURN_NONE; } @@ -1053,7 +1055,7 @@ object_destroyed (PyObject *self, PyObject *args) return NULL; } - return c2python (((PYAObjectBase *) self)->destroyed ()); + return c2python (PYAObjectBase::from_pyobject (self)->destroyed ()); } /** @@ -1066,7 +1068,7 @@ object_is_const (PyObject *self, PyObject *args) return NULL; } - return c2python (((PYAObjectBase *) self)->const_ref ()); + return c2python (PYAObjectBase::from_pyobject (self)->const_ref ()); } static PyObject * @@ -1112,7 +1114,7 @@ method_adaptor (int mid, PyObject *self, PyObject *args) PYAObjectBase *p = 0; if (! PyType_Check (self)) { // non-static method - p = (PYAObjectBase *) self; + p = PYAObjectBase::from_pyobject (self); } tl::Heap heap; @@ -1666,7 +1668,7 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args) { PYA_TRY - PYAObjectBase *p = (PYAObjectBase *) self; + PYAObjectBase *p = PYAObjectBase::from_pyobject (self); // delete any object which we may have already if (p->is_attached ()) { @@ -1888,7 +1890,7 @@ property_getter_impl (int mid, PyObject *self) PYAObjectBase *p = 0; if (! PyType_Check (self)) { - p = (PYAObjectBase *) self; + p = PYAObjectBase::from_pyobject (self); cls_decl = p->cls_decl (); } else { cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self); @@ -1969,7 +1971,7 @@ property_setter_impl (int mid, PyObject *self, PyObject *value) PYAObjectBase *p = 0; if (! PyType_Check (self)) { - p = (PYAObjectBase *) self; + p = PYAObjectBase::from_pyobject (self); cls_decl = p->cls_decl (); } else { cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self); @@ -2156,7 +2158,6 @@ PythonModule::~PythonModule () // the Python objects were probably deleted by Python itself as it exited - // don't try to delete them again. mp_module.release (); - mp_base_class.release (); while (!m_methods_heap.empty ()) { delete m_methods_heap.back (); @@ -2339,36 +2340,6 @@ PythonModule::make_classes (const char *mod_name) PyObject_SetAttrString (module, "__doc__", PythonRef (c2python (m_mod_description)).get ()); - // Create a (built-in) base class for all objects exposed by this module - - m_base_class_name = m_mod_name + ".__Base"; - - PyTypeObject *base_class = (PyTypeObject *) PyType_Type.tp_alloc (&PyType_Type, 0); - tl_assert (base_class != NULL); - mp_base_class = PythonRef ((PyObject *) base_class); - - base_class->tp_base = &PyBaseObject_Type; - base_class->tp_name = m_base_class_name.c_str (); - base_class->tp_basicsize = sizeof (PYAObjectBase); - base_class->tp_init = &pya_object_init; - base_class->tp_new = &pya_object_new; - base_class->tp_dealloc = (destructor) &pya_object_deallocate; - base_class->tp_setattro = PyObject_GenericSetAttr; - base_class->tp_getattro = PyObject_GenericGetAttr; -#if PY_MAJOR_VERSION < 3 - base_class->tp_flags = Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES; -#else - base_class->tp_flags = Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; -#endif - - if (PyType_Ready (base_class) < 0) { - check_error (); - return; - } - - PyModule_AddObject (module, "__Base", (PyObject *) base_class); - - // Build a class for descriptors for static attributes PYAStaticAttributeDescriptorObject::make_class (module); @@ -2434,15 +2405,17 @@ PythonModule::make_classes (const char *mod_name) m_classes.push_back (c.operator-> ()); - PythonRef bases (PyTuple_New (1)); - PyObject *base = mp_base_class.get (); + PythonRef bases; if (c->base () != 0) { + bases = PythonRef (PyTuple_New (1)); PyTypeObject *pt = PythonClassClientData::py_type (*c->base ()); tl_assert (pt != 0); - base = (PyObject *) pt; + PyObject *base = (PyObject *) pt; + Py_INCREF (base); + PyTuple_SetItem (bases.get (), 0, base); + } else { + bases = PythonRef (PyTuple_New (0)); } - Py_INCREF (base); - PyTuple_SetItem (bases.get (), 0, base); PythonRef dict (PyDict_New ()); PyDict_SetItemString (dict.get (), "__module__", PythonRef (c2python (m_mod_name)).get ()); @@ -2459,9 +2432,18 @@ PythonModule::make_classes (const char *mod_name) check_error (); tl_assert (false); } + + // Customize + type->tp_basicsize += sizeof (PYAObjectBase); + type->tp_init = &pya_object_init; + type->tp_new = &pya_object_new; + type->tp_dealloc = (destructor) &pya_object_deallocate; + type->tp_setattro = PyObject_GenericSetAttr; + type->tp_getattro = PyObject_GenericGetAttr; + PythonClassClientData::initialize (*c, type); - tl_assert (cls_for_type (type) == c.operator-> ()); // @@@ + tl_assert (cls_for_type (type) == c.operator-> ()); PyList_Append (all_list.get (), PythonRef (c2python (c->name ())).get ()); PyModule_AddObject (module, c->name ().c_str (), (PyObject *) type); diff --git a/src/pya/pya/pyaModule.h b/src/pya/pya/pyaModule.h index 85515e7d6..f06ab4676 100644 --- a/src/pya/pya/pyaModule.h +++ b/src/pya/pya/pyaModule.h @@ -120,9 +120,7 @@ private: std::vector m_getseters_heap; std::string m_mod_name, m_mod_description; - std::string m_base_class_name; PythonRef mp_module; - PythonRef mp_base_class; char *mp_mod_def; static std::map m_python_doc; diff --git a/src/pya/pya/pyaObject.cc b/src/pya/pya/pyaObject.cc index ca247173e..ae9fd624d 100644 --- a/src/pya/pya/pyaObject.cc +++ b/src/pya/pya/pyaObject.cc @@ -129,8 +129,8 @@ Callee::call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const PythonRef argv (PyTuple_New (arg4self + std::distance (meth->begin_arguments (), meth->end_arguments ()))); // Put self into first argument - PyTuple_SetItem (argv.get (), 0, mp_obj); - Py_INCREF (mp_obj); + PyTuple_SetItem (argv.get (), 0, mp_obj->py_object ()); + Py_INCREF (mp_obj->py_object ()); // TODO: callbacks with default arguments? for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) { @@ -169,8 +169,9 @@ Callee::call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const // -------------------------------------------------------------------------- // Implementation of PYAObjectBase -PYAObjectBase::PYAObjectBase(const gsi::ClassBase *_cls_decl) - : m_listener (new pya::StatusChangedListener (this)), +PYAObjectBase::PYAObjectBase(const gsi::ClassBase *_cls_decl, PyObject *py_object) + : mp_py_object (py_object), + m_listener (new pya::StatusChangedListener (this)), m_callee (new pya::Callee (this)), m_cls_decl (_cls_decl), m_obj (0), @@ -179,6 +180,7 @@ PYAObjectBase::PYAObjectBase(const gsi::ClassBase *_cls_decl) m_destroyed (false), m_can_destroy (false) { + // .. nothing yet .. } PYAObjectBase::~PYAObjectBase () @@ -223,7 +225,7 @@ PYAObjectBase::object_destroyed () // NOTE: this may delete "this"! if (!prev_owner) { - Py_DECREF (this); + Py_DECREF (py_object ()); } } @@ -246,7 +248,7 @@ PYAObjectBase::release () if (!m_owned) { m_owned = true; // NOTE: this may delete "this"! TODO: this should not happen. Can we assert that somehow? - Py_DECREF (this); + Py_DECREF (py_object ()); } } @@ -254,7 +256,7 @@ void PYAObjectBase::keep_internal () { if (m_owned) { - Py_INCREF (this); + Py_INCREF (py_object ()); m_owned = false; } } @@ -328,7 +330,7 @@ PYAObjectBase::set (void *obj, bool owned, bool const_ref, bool can_destroy) } if (!m_owned) { - Py_INCREF (this); + Py_INCREF (py_object ()); } } @@ -353,7 +355,7 @@ PYAObjectBase::initialize_callbacks () // TODO: caching appears to create some leaks ... #if 1 - PythonRef type_ref ((PyObject *) Py_TYPE (this), false /*borrowed*/); + PythonRef type_ref ((PyObject *) Py_TYPE (py_object ()), false /*borrowed*/); // Locate the callback-enabled methods set by Python tpye object (pointer) // NOTE: I'm not quite sure whether the type object pointer is a good key @@ -391,7 +393,7 @@ PYAObjectBase::initialize_callbacks () // TOOD: That may happen too often, i.e. if the Python class does not reimplement the virtual // method, but the C++ class defines a method hook that the reimplementation can call. // We don't want to produce a lot of overhead for the Qt classes here. - PythonRef py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (this), nstr); + PythonRef py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (py_object ()), nstr); if (! py_attr) { // because PyObject_GetAttrString left an error @@ -423,7 +425,7 @@ PYAObjectBase::initialize_callbacks () PythonRef py_attr; const char *nstr = (*m)->primary_name ().c_str (); - py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (this), nstr); + py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (py_object ()), nstr); int id = m_callee->add_callback (CallbackFunction (py_attr, *m)); (*m)->set_callback (m_obj, gsi::Callback (id, m_callee.get (), (*m)->argsize (), (*m)->retsize ())); @@ -458,7 +460,7 @@ PYAObjectBase::initialize_callbacks () // TOOD: That may happen too often, i.e. if the Python class does not reimplement the virtual // method, but the C++ class defines a method hook that the reimplementation can call. // We don't want to produce a lot of overhead for the Qt classes here. - PythonRef py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (this), nstr); + PythonRef py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (py_object ()), nstr); if (! py_attr) { // because PyObject_GetAttrString left an error @@ -471,7 +473,7 @@ PYAObjectBase::initialize_callbacks () // may create issues with callbacks during destruction (i.e. QWidget-destroyed signal) if (! PyCFunction_Check (py_attr.get ())) { - PyObject *py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (this), nstr); + PyObject *py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (py_object ()), nstr); tl_assert (py_attr != NULL); int id = m_callee.add_callback (CallbackFunction (py_attr, *m)); (*m)->set_callback (m_obj, gsi::Callback (id, &m_callee, (*m)->argsize (), (*m)->retsize ())); @@ -501,7 +503,7 @@ PYAObjectBase::clear_callbacks_cache () void PYAObjectBase::detach_callbacks () { - PythonRef type_ref ((PyObject *) Py_TYPE (this), false /*borrowed*/); + PythonRef type_ref ((PyObject *) Py_TYPE (py_object ()), false /*borrowed*/); callbacks_cache::iterator cb = s_callbacks_cache.find (type_ref); if (cb != s_callbacks_cache.end ()) { diff --git a/src/pya/pya/pyaObject.h b/src/pya/pya/pyaObject.h index fa0023f8b..9e59c8790 100644 --- a/src/pya/pya/pyaObject.h +++ b/src/pya/pya/pyaObject.h @@ -29,6 +29,8 @@ #include "pyaRefs.h" #include "pyaCommon.h" +#include "tlAssert.h" + #include #include #include @@ -50,23 +52,43 @@ class StatusChangedListener; /** * @brief The Python object representing a GSI object * - * Note: the PYAObjectBase must be directly derived from PyObject so that - * a PyObject pointer can be cast to a PYAObjectBase pointer. + * NOTE: this memory block is attached to the actual structure + * and obtained by taking the last sizeof(PYAObjectBase) bytes. + * It's basically a connector between GSI objects and the Python + * objects. */ class PYA_PUBLIC PYAObjectBase - : public PyObject { public: /** * @brief Constructor - creates a new object for the given GSI class */ - PYAObjectBase (const gsi::ClassBase *_cls_decl); + PYAObjectBase (const gsi::ClassBase *_cls_decl, PyObject *py_object); /** * @brief Destructor */ ~PYAObjectBase (); + /** + * @brief Gets the PYAObjectBase pointer from a PyObject pointer + */ + static PYAObjectBase *from_pyobject (PyObject *py_object) + { + PYAObjectBase *pya_object = (PYAObjectBase *)((char *) py_object + Py_TYPE (py_object)->tp_basicsize - sizeof (PYAObjectBase)); + tl_assert (pya_object->py_object () == py_object); + return pya_object; + } + + /** + * @brief Gets the PYAObjectBase pointer from a PyObject pointer + * This version doesn't check anything. + */ + static PYAObjectBase *from_pyobject_unsafe (PyObject *py_object) + { + return (PYAObjectBase *)((char *) py_object + Py_TYPE (py_object)->tp_basicsize - sizeof (PYAObjectBase)); + } + /** * @brief Indicates that a C++ object is present */ @@ -142,6 +164,14 @@ public: m_const_ref = c; } + /** + * @brief Gets the Python object for this bridge object + */ + PyObject *py_object () const + { + return mp_py_object; + } + /** * @brief Returns the C++ object reference */ @@ -189,6 +219,7 @@ private: void object_destroyed (); void keep_internal (); + PyObject *mp_py_object; std::auto_ptr m_listener; std::auto_ptr m_callee; const gsi::ClassBase *m_cls_decl; diff --git a/testdata/python/basic.py b/testdata/python/basic.py index 1fca4191e..eb495d0c8 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -2718,6 +2718,7 @@ class BasicTest(unittest.TestCase): go = None self.assertEqual(pya.GObject.g_inst_count(), gc) + # run unit tests if __name__ == '__main__': suite = unittest.TestSuite()