Commits

wlav committed 2aee950

object identity conservation

  • Participants
  • Parent commits b3c3607
  • Branches reflex-support

Comments (0)

Files changed (9)

pypy/module/cppyy/capi/__init__.py

 #import cint_capi as backend
 
 
+C_NULL_VOIDP  = lltype.nullptr(rffi.VOIDP.TO)
+
 C_TYPEHANDLE = rffi.LONG
+C_NULL_TYPEHANDLE = rffi.cast(C_TYPEHANDLE, C_NULL_VOIDP)
 C_OBJECT = rffi.VOIDP
+C_NULL_OBJECT = C_NULL_VOIDP
 
 C_METHPTRGETTER = lltype.FuncType([C_OBJECT], rffi.VOIDP)
 C_METHPTRGETTER_PTR = lltype.Ptr(C_METHPTRGETTER)

pypy/module/cppyy/executor.py

         ptr_result = rffi.cast(rffi.VOIDP, long_result)
         return interp_cppyy.new_instance(space, w_returntype, self.cpptype, ptr_result, True)
 
+    def execute_libffi(self, space, w_returntype, libffifunc, argchain):
+        from pypy.module.cppyy.interp_cppyy import FastCallNotPossible
+        raise FastCallNotPossible
+
 
 _executors = {}
 def get_executor(space, name):

pypy/module/cppyy/interp_cppyy.py

 
 from pypy.rpython.lltypesystem import rffi, lltype
 
-from pypy.rlib import libffi, rdynload
+from pypy.rlib import libffi, rdynload, rweakref
 from pypy.rlib import jit, debug
 
 from pypy.module.cppyy import converter, executor, helper
 
+
 class FastCallNotPossible(Exception):
     pass
 
-NULL_VOIDP  = lltype.nullptr(rffi.VOIDP.TO)
-
 def _direct_ptradd(ptr, offset):        # TODO: factor out with convert.py
     address = rffi.cast(rffi.CCHARP, ptr)
     return rffi.cast(rffi.VOIDP, lltype.direct_ptradd(address, offset))
 class State(object):
     def __init__(self, space):
         self.cpptype_cache = {
-            "void" : W_CPPType(space, "void", rffi.cast(capi.C_TYPEHANDLE, NULL_VOIDP)) }
+            "void" : W_CPPType(space, "void", capi.C_NULL_TYPEHANDLE) }
         self.cpptemplatetype_cache = {}
 
 @unwrap_spec(name=str)
                 cppinstance.cppclass.handle, self.scope_handle, cppinstance.rawobject)
             cppthis = _direct_ptradd(cppinstance.rawobject, offset)
         else:
-            cppthis = NULL_VOIDP
+            cppthis = capi.C_NULL_OBJECT
         return cppthis
 
     @jit.unroll_safe
     def destruct(self):
         assert isinstance(self, W_CPPInstance)
         if self.rawobject:
+            memory_regulator.unregister(self)
             capi.c_destruct(self.cppclass.handle, self.rawobject)
-            self.rawobject = NULL_VOIDP
+            self.rawobject = capi.C_NULL_OBJECT
 
     def __del__(self):
         if self.python_owns:
 )
 W_CPPInstance.typedef.acceptable_as_base_class = True
 
-def new_instance(space, w_type, cpptype, rawptr, owns):
+
+class MemoryRegulator:
+    # TODO: (?) An object address is not unique if e.g. the class has a
+    # public data member of class type at the start of its definition and
+    # has no virtual functions. A _key class that hashes on address and
+    # type would be better, but my attempt failed in the rtyper, claiming
+    # a call on None ("None()") and needed a default ctor. (??)
+    # Note that for now, the associated test carries an m_padding to make
+    # a difference in the addresses.
+    def __init__(self):
+        self.objects = rweakref.RWeakValueDictionary(int, W_CPPInstance)
+
+    def register(self, obj):
+        int_address = int(rffi.cast(rffi.LONG, obj.rawobject))
+        self.objects.set(int_address, obj)
+
+    def unregister(self, obj):
+        int_address = int(rffi.cast(rffi.LONG, obj.rawobject))
+        self.objects.set(int_address, None)
+
+    def retrieve(self, address):
+        int_address = int(rffi.cast(rffi.LONG, address))
+        return self.objects.get(int_address)
+
+memory_regulator = MemoryRegulator()
+
+
+def new_instance(space, w_type, cpptype, rawobject, python_owns):
+    obj = memory_regulator.retrieve(rawobject)
+    if obj and obj.cppclass == cpptype:
+        return obj
     w_cppinstance = space.allocate_instance(W_CPPInstance, w_type)
     cppinstance = space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=False)
-    W_CPPInstance.__init__(cppinstance, space, cpptype, rawptr, owns)
+    W_CPPInstance.__init__(cppinstance, space, cpptype, rawobject, python_owns)
+    memory_regulator.register(cppinstance)
     return w_cppinstance
 
-
 @unwrap_spec(cppinstance=W_CPPInstance)
 def addressof(space, cppinstance):
      address = rffi.cast(rffi.LONG, cppinstance.rawobject)
     rawobject = rffi.cast(rffi.VOIDP, address)
     w_cpptype = space.findattr(w_type, space.wrap("_cpp_proxy"))
     cpptype = space.interp_w(W_CPPType, w_cpptype, can_be_None=False)
+
+    obj = memory_regulator.retrieve(rawobject)
+    if obj and obj.cppclass == cpptype:
+        return obj
+
     return new_instance(space, w_type, cpptype, rawobject, owns)

pypy/module/cppyy/test/advancedcpp.cxx

 double my_global_array[500];
 
 
-// for life-line testing
+// for life-line and identity testing
 int some_class_with_data::some_data::s_num_data = 0;
 
 

pypy/module/cppyy/test/advancedcpp.h

 
 
 //===========================================================================
-class some_class_with_data {       // for life-line testing
+class some_class_with_data {       // for life-line and identity testing
 public:
    class some_data {
    public:
       return m_data;
    }
 
+   int m_padding;
    some_data m_data;
 };
 

pypy/module/cppyy/test/advancedcpp.xml

 
   <class name="some_abstract_class" />
   <class name="some_concrete_class" />
+  <class name="some_convertible" />
+  <class name="some_class_with_data" />
+  <class name="some_class_with_data::some_data" />
 
   <class name="pointer_pass" />
 

pypy/module/cppyy/test/advancedcpp_LinkDef.h

 
 #pragma link C++ class some_abstract_class;
 #pragma link C++ class some_concrete_class;
+#pragma link C++ class some_convertible;
+#pragma link C++ class some_class_with_data;
+#pragma link C++ class some_class_with_data::some_data;
 
 #pragma link C++ class pointer_pass;
 

pypy/module/cppyy/test/test_advancedcpp.py

         assert o == cppyy.bind_object(addr, o.__class__)
         #assert o == cppyy.bind_object(addr, "some_concrete_class")
 
-    def test10_multi_methods(self):
+    def test10_object_identity(self):
+        """Test object identity"""
+
+        import cppyy
+        some_concrete_class  = cppyy.gbl.some_concrete_class
+        some_class_with_data = cppyy.gbl.some_class_with_data
+
+        o = some_concrete_class()
+        addr = cppyy.addressof(o)
+
+        o2 = cppyy.bind_object(addr, some_concrete_class)
+        assert o is o2
+
+        o3 = cppyy.bind_object(addr, some_class_with_data)
+        assert not o is o3
+
+        d1 = some_class_with_data()
+        d2 = d1.gime_copy()
+        assert not d1 is d2
+
+        dd1a = d1.gime_data()
+        dd1b = d1.gime_data()
+        assert dd1a is dd1b
+
+        dd2 = d2.gime_data()
+        assert not dd1a is dd2
+        assert not dd1b is dd2
+
+        d2.destruct()
+        d1.destruct()
+
+    def test11_multi_methods(self):
         """Test calling of methods from multiple inheritance"""
 
         import cppyy

pypy/module/cppyy/test/test_pythonify.py

         pl_a = example01_class.staticCyclePayload(pl, 66.)
         pl_a.getData() == 66.
         assert payload_class.count == 1
+        pl_a = None
         pl = None
         gc.collect()
         assert payload_class.count == 0