[pypy-svn] r45021 - in pypy/dist/pypy/rpython/lltypesystem: . test

arigo at codespeak.net arigo at codespeak.net
Fri Jul 13 15:27:34 CEST 2007


Author: arigo
Date: Fri Jul 13 15:27:33 2007
New Revision: 45021

Modified:
   pypy/dist/pypy/rpython/lltypesystem/compactlltype.py
   pypy/dist/pypy/rpython/lltypesystem/lltype.py
   pypy/dist/pypy/rpython/lltypesystem/test/test_compactlltype.py
Log:
(lac, arigo)
First test passes: allocating and freeing ctypes-based structs and arrays.


Modified: pypy/dist/pypy/rpython/lltypesystem/compactlltype.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/compactlltype.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/compactlltype.py	Fri Jul 13 15:27:33 2007
@@ -1,51 +1,46 @@
-import ctypes, sys, weakref
-from pypy.rpython.lltypesystem.lltype import Signed, Struct, GcStruct, Ptr
-from pypy.rpython.lltypesystem.lltype import ContainerType, Array, GcArray
-from pypy.rpython.lltypesystem.lltype import typeOf, castable
+import sys
+import ctypes
+from pypy.rpython.lltypesystem import lltype
+from pypy.tool.uid import fixid
 
 _Ctypes_PointerType = type(ctypes.POINTER(ctypes.c_int))
 
-
-def cast_pointer(PTRTYPE, ptr):
-    CURTYPE = typeOf(ptr)
-    if not isinstance(CURTYPE, Ptr) or not isinstance(PTRTYPE, Ptr):
-        raise TypeError, "can only cast pointers to other pointers"
-    return ptr._cast_to(PTRTYPE)
-
-
-class _parentable(ctypes.Structure):   # won't work with Union
-    __slots__ = ()
+def uaddressof(obj):
+    return fixid(ctypes.addressof(obj))
 
 # ____________________________________________________________
 
+_allocated = []
+
 _ctypes_cache = {
-    Signed: ctypes.c_long,
+    lltype.Signed: ctypes.c_long,
     }
 
-def _build_ctypes_struct(S, max_n=None):
+def build_ctypes_struct(S, max_n=None):
     fields = []
     for fieldname in S._names:
         FIELDTYPE = S._flds[fieldname]
         if max_n is not None and fieldname == S._arrayfld:
-            cls = _build_ctypes_array(FIELDTYPE, max_n)
+            cls = build_ctypes_array(FIELDTYPE, max_n)
         else:
-            cls = _get_ctypes_type(FIELDTYPE)
+            cls = get_ctypes_type(FIELDTYPE)
         fields.append((fieldname, cls))
 
-    class CStruct(_parentable):
+    class CStruct(ctypes.Structure):
         _fields_ = fields
-        _TYPE    = S
 
         def malloc(cls, n=None):
-            S = cls._TYPE
             if S._arrayfld is None:
                 if n is not None:
                     raise TypeError("%r is not variable-sized" % (S,))
-                return ctypes.pointer(cls())
+                storage = cls()
+                _allocated.append(storage)
+                return _ctypes_struct(S, storage)
             else:
+                XXX
                 if n is None:
                     raise TypeError("%r is variable-sized" % (S,))
-                smallercls = _build_ctypes_struct(S, n)
+                smallercls = build_ctypes_struct(S, n)
                 smallstruct = smallercls()
                 getattr(smallstruct, S._arrayfld).length = n
                 structptr = ctypes.cast(ctypes.pointer(smallstruct),
@@ -56,150 +51,152 @@
     CStruct.__name__ = 'ctypes_%s' % (S,)
     return CStruct
 
-def _build_ctypes_array(A, max_n=None):
+def build_ctypes_array(A, max_n=0):
+    assert max_n >= 0
     ITEM = A.OF
-    ctypes_item = _get_ctypes_type(ITEM)
-    if max_n is None:
-        max_n = sys.maxint // ctypes.sizeof(ctypes_item)
-        max_n //= 2   # XXX better safe than sorry about ctypes bugs
+    ctypes_item = get_ctypes_type(ITEM)
 
-    class CArray(_parentable):
+    class CArray(ctypes.Structure):
         _fields_ = [('length', ctypes.c_int),
                     ('items',  max_n * ctypes_item)]
-        _TYPE    = A
 
         def malloc(cls, n=None):
             if not isinstance(n, int):
                 raise TypeError, "array length must be an int"
-            smallercls = _build_ctypes_array(cls._TYPE, n)
-            smallarray = smallercls()
-            smallarray.length = n
-            arrayptr = ctypes.cast(ctypes.pointer(smallarray),
-                                   ctypes.POINTER(cls))
-            return arrayptr
+            biggercls = build_ctypes_array(A, n)
+            bigarray = biggercls()
+            _allocated.append(bigarray)
+            bigarray.length = n
+            return _ctypes_array(A, bigarray)
         malloc = classmethod(malloc)
 
-    CArray.__name__ = 'ctypes_%s' % (A,)
+    CArray.__name__ = 'ctypes_%s*%d' % (A, max_n)
     return CArray
 
-def _get_ctypes_type(T):
+def get_ctypes_type(T):
     try:
         return _ctypes_cache[T]
     except KeyError:
-        if isinstance(T, Ptr):
-            cls = ctypes.POINTER(_get_ctypes_type(T.TO))
-        elif isinstance(T, Struct):
-            cls = _build_ctypes_struct(T)
-        elif isinstance(T, Array):
-            cls = _build_ctypes_array(T)
+        if isinstance(T, lltype.Ptr):
+            cls = ctypes.POINTER(get_ctypes_type(T.TO))
+        elif isinstance(T, lltype.Struct):
+            cls = build_ctypes_struct(T)
+        elif isinstance(T, lltype.Array):
+            cls = build_ctypes_array(T)
         else:
             raise NotImplementedError(T)
         _ctypes_cache[T] = cls
         return cls
 
-def _expose(val):
-    if isinstance(type(val), _Ctypes_PointerType):
-        val = val.contents
-    T = typeOf(val)
-    if isinstance(T, ContainerType):
-        val = _ptr(ctypes.pointer(val))
+def ctypes2lltype(val, RESTYPE):
+    if isinstance(RESTYPE, lltype.Struct):
+        return _ctypes_struct(RESTYPE, val)
+    if isinstance(RESTYPE, lltype.Array):
+        return _ctypes_array(RESTYPE, val)
+    if isinstance(RESTYPE, lltype.Ptr):
+        if isinstance(RESTYPE.TO, lltype.Array):
+            ArrayType = build_ctypes_array(RESTYPE.TO,
+                                           max_n=val.contents.length)
+            val = ctypes.cast(val, ctypes.POINTER(ArrayType))
+        obj = ctypes2lltype(val.contents, RESTYPE.TO)
+        return lltype._ptr(RESTYPE, obj, solid=True)
     return val
 
-def _lltype2ctypes(val):
-    T = typeOf(val)
-    if isinstance(T, Ptr):
-        return val._storageptr
+def lltype2ctypes(val):
+    T = lltype.typeOf(val)
+    if isinstance(T, lltype.Ptr):
+        return val._obj._convert_to_ctypes_pointer()
     return val
 
-class _ptr(object):
-    __slots__ = ['_storageptr', '_TYPE']
 
-    def __init__(self, storageptr):
-        assert isinstance(type(storageptr), _Ctypes_PointerType)
-        _ptr._storageptr.__set__(self, storageptr)
-        _ptr._TYPE.__set__(self, Ptr(type(storageptr)._type_._TYPE))
+class _ctypes_parentable(lltype._parentable):
 
-    def __getattr__(self, field_name):
-        if isinstance(self._TYPE.TO, Struct):
-            if field_name in self._TYPE.TO._flds:
-                return _expose(getattr(self._storageptr.contents, field_name))
-        raise AttributeError("%r instance has no field %r" % (self._TYPE.TO,
-                                                              field_name))
+    __slots__ = ('_ctypes_storage',)
+
+    def __init__(self, TYPE, ctypes_storage):
+        lltype._parentable.__init__(self, TYPE)
+        self._ctypes_storage = ctypes_storage
+
+    def _free(self):
+        # XXX REALLY SLOW!
+        myaddress = ctypes.addressof(self._ctypes_storage)
+        for i, obj in enumerate(_allocated):
+            if ctypes.addressof(obj) == myaddress:
+                # found it
+                del _allocated[i]
+                lltype._parentable._free(self)
+                break
+        else:
+            raise RuntimeError("lltype.free() on a pointer that was not "
+                               "obtained by lltype.malloc()")
+
+
+class _ctypes_struct(_ctypes_parentable):
+    _kind = "structure"
+    __slots__ = ()
+
+    def __repr__(self):
+        return '<ctypes struct %s at 0x%x>' % (
+            self._TYPE._name,
+            uaddressof(self._ctypes_storage))
 
     def __setattr__(self, field_name, value):
-        if isinstance(self._TYPE.TO, Struct):
-            if field_name in self._TYPE.TO._flds:
-                setattr(self._storageptr.contents, field_name,
-                        _lltype2ctypes(value))
-                return
-        raise AttributeError("%r instance has no field %r" % (self._TYPE.TO,
-                                                              field_name))
-
-    def __nonzero__(self):
-        return bool(self._storageptr)
-
-    def __len__(self):
-        T = self._TYPE.TO
-        if isinstance(T, Array):# ,FixedSizeArray)):
-            #if self._T._hints.get('nolength', False):
-            #    raise TypeError("%r instance has no length attribute" %
-            #                        (self._T,))
-            return self._storageptr.contents.length
-        raise TypeError("%r instance is not an array" % (T,))
-
-    def __getitem__(self, i):
-        T = self._TYPE.TO
-        if isinstance(T, Array):
-            start, stop = 0, self._storageptr.contents.length
-            if not (start <= i < stop):
-                if isinstance(i, slice):
-                    raise TypeError("array slicing not supported")
-                raise IndexError("array index out of bounds")
-            return _expose(self._storageptr.contents.items[i])
-        raise TypeError("%r instance is not an array" % (T,))
-
-    def _cast_to(self, PTRTYPE):
-        CURTYPE = self._TYPE
-        down_or_up = castable(PTRTYPE, CURTYPE)
-        if down_or_up == 0:
-            return self
-        if not self: # null pointer cast
-            return PTRTYPE._defl()
-        WAAA
-        if isinstance(self._obj, int):
-            return _ptr(PTRTYPE, self._obj, solid=True)
-        if down_or_up > 0:
-            p = self
-            while down_or_up:
-                p = getattr(p, typeOf(p).TO._names[0])
-                down_or_up -= 1
-            return _ptr(PTRTYPE, p._obj, solid=self._solid)
-        u = -down_or_up
-        struc = self._obj
-        while u:
-            parent = struc._parentstructure()
-            if parent is None:
-                raise RuntimeError("widening to trash: %r" % self)
-            PARENTTYPE = struc._parent_type
-            if getattr(parent, PARENTTYPE._names[0]) is not struc:
-                raise InvalidCast(CURTYPE, PTRTYPE) # xxx different exception perhaps?
-            struc = parent
-            u -= 1
-        if PARENTTYPE != PTRTYPE.TO:
-            raise RuntimeError("widening %r inside %r instead of %r" % (CURTYPE, PARENTTYPE, PTRTYPE.TO))
-        return _ptr(PTRTYPE, struc, solid=self._solid)
+        if field_name.startswith('_'):
+            lltype._parentable.__setattr__(self, field_name, value)
+        else:
+            setattr(self._ctypes_storage, field_name, lltype2ctypes(value))
+
+    def _getattr(self, field_name, uninitialized_ok=False):
+        return getattr(self, field_name)
+
+    def __getattr__(self, field_name):
+        if field_name.startswith('_'):
+            return lltype._parentable.__getattr__(self, field_name)
+        else:
+            return ctypes2lltype(getattr(self._ctypes_storage, field_name),
+                                 getattr(self._TYPE, field_name))
+
+    def _convert_to_ctypes_pointer(self):
+        return ctypes.pointer(self._ctypes_storage)
+
+class _ctypes_array(_ctypes_parentable):
+    _kind = "array"
+    __slots__ = ()
+
+    def __init__(self, TYPE, ctypes_storage):
+        _ctypes_parentable.__init__(self, TYPE, ctypes_storage)
+        assert ctypes_storage.length == len(ctypes_storage.items)
+
+    def __repr__(self):
+        return '<ctypes array at 0x%x>' % (
+            uaddressof(self._ctypes_storage),)
+
+    def getlength(self):
+        length = self._ctypes_storage.length
+        assert length == len(self._ctypes_storage.items)
+        return length
+
+    def getbounds(self):
+        return 0, self.getlength()
+
+    def getitem(self, index):
+        return ctypes2lltype(self._ctypes_storage.items[index],
+                             self._TYPE.OF)
+
+    def _convert_to_ctypes_pointer(self):
+        PtrType = ctypes.POINTER(get_ctypes_type(self._TYPE))
+        return ctypes.cast(ctypes.pointer(self._ctypes_storage), PtrType)
 
 # ____________________________________________________________
 
-def malloc(T, n=None, immortal=False):
-    if T._gckind != 'gc' and not immortal:# and flavor.startswith('gc'):
-        raise TypeError, "gc flavor malloc of a non-GC non-immortal structure"
-    if isinstance(T, (Struct, Array)):
-        cls = _get_ctypes_type(T)
-        return _ptr(cls.malloc(n))
-    else:
-        raise TypeError, "malloc for Structs and Arrays only"
-
-def nullptr(T):
-    cls = _get_ctypes_type(T)
-    return _ptr(ctypes.POINTER(cls)())
+def malloc(T, n=None, flavor='gc', immortal=False):
+    # XXX for now, let's only worry about raw malloc
+    assert flavor == 'raw'
+    assert T._gckind == 'raw'
+    assert isinstance(T, (lltype.Struct, lltype.Array))
+    cls = get_ctypes_type(T)
+    container = cls.malloc(n)
+    return lltype._ptr(lltype.Ptr(T), container, solid=True)
+
+def getobjcount():
+    return len(_allocated)

Modified: pypy/dist/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/lltype.py	Fri Jul 13 15:27:33 2007
@@ -1199,6 +1199,7 @@
         self._dead = False
 
     def _free(self):
+        self._check()   # no double-frees
         self._dead = True
 
     def _setparentstructure(self, parent, parentindex):
@@ -1664,7 +1665,7 @@
     T = typeOf(p)
     if not isinstance(T, Ptr) or p._togckind() != 'raw':
         raise TypeError, "free(): only for pointers to non-gc containers"
-    p._obj._free()
+    p._obj0._free()
 
 def functionptr(TYPE, name, **attrs):
     if not isinstance(TYPE, FuncType):

Modified: pypy/dist/pypy/rpython/lltypesystem/test/test_compactlltype.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/test/test_compactlltype.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/test/test_compactlltype.py	Fri Jul 13 15:27:33 2007
@@ -1,64 +1,69 @@
-import py
-from pypy.rpython.lltypesystem.compactlltype import *
+from pypy.rpython.lltypesystem.lltype import *
+from pypy.rpython.lltypesystem.compactlltype import malloc, getobjcount
 
-def isweak(p, T):
-    return True# p._weak and typeOf(p).TO == T
+py.test.skip("in-progress")
 
-def test_nullptr():
-    S = Struct('s')
-    p0 = nullptr(S)
-    assert not p0
-    assert typeOf(p0) == Ptr(S)
+def isctypes(p):
+    return hasattr(p._obj, '_ctypes_storage')
 
 def test_basics():
-    S0 = GcStruct("s0", ('a', Signed), ('b', Signed))
+    objcount = getobjcount()
+    S0 = Struct("s0", ('a', Signed), ('b', Signed))
     assert S0.a == Signed
     assert S0.b == Signed
-    s0 = malloc(S0)
+    s0 = malloc(S0, flavor='raw')
     print s0
+    assert isctypes(s0)
     assert typeOf(s0) == Ptr(S0)
-    #py.test.raises(UninitializedMemoryAccess, "s0.a") XXX!
+    #py.test.raises(UninitializedMemoryAccess, "s0.a")
     s0.a = 1
     s0.b = s0.a
     assert s0.a == 1
     assert s0.b == 1
     assert typeOf(s0.a) == Signed
+    free(s0, flavor='raw')
+    py.test.raises(RuntimeError, "s0.a")   # accessing freed struct s0
+    py.test.raises(RuntimeError, "free(s0, flavor='raw')")   # double free
     # simple array
-    Ar = GcArray(('v', Signed))
-    x = malloc(Ar,0)
+    Ar = Array(('v', Signed))
+    x = malloc(Ar, 0, flavor='raw')
     print x
+    assert isctypes(x)
     assert len(x) == 0
-    x = malloc(Ar,3)
+    free(x, flavor='raw')
+    x = malloc(Ar, 3, flavor='raw')
     print x
+    assert isctypes(x)
     assert typeOf(x) == Ptr(Ar)
     assert len(x) == 3
-    assert isweak(x[0], Ar.OF)
     assert typeOf(x[0]) == Ptr(Ar.OF)
     x[0].v = 1
     x[1].v = 2
     x[2].v = 3
     assert typeOf(x[0].v) == Signed
     assert [x[z].v for z in range(3)] == [1, 2, 3]
+    free(x, flavor='raw')
     #
     def define_list(T):
-        List_typ = GcStruct("list",
-                ("items", Ptr(GcArray(('item',T)))))
+        List_typ = Struct("list",
+                ("items", Ptr(Array(('item',T)))))
         def newlist():
-            l = malloc(List_typ)
-            items = malloc(List_typ.items.TO, 0)
+            l = malloc(List_typ, flavor='raw')
+            items = malloc(List_typ.items.TO, 0, flavor='raw')
             l.items = items
             return l
 
         def append(l, newitem):
             length = len(l.items)
             print 'append! length', length, 'newitem', newitem
-            newitems = malloc(List_typ.items.TO, length+1)
+            newitems = malloc(List_typ.items.TO, length+1, flavor='raw')
             i = 0
             while i<length:
               newitems[i].item = l.items[i].item
               i += 1
             newitems[length].item = newitem
             print len(newitems)
+            free(l.items, flavor='raw')
             l.items = newitems
             del newitems
             print len(l.items)
@@ -66,9 +71,13 @@
         def item(l, i):
             return l.items[i].item
 
-        return List_typ, newlist, append, item
+        def freelist(l):
+            free(l.items, flavor='raw')
+            free(l, flavor='raw')
 
-    List_typ, inewlist, iappend, iitem = define_list(Signed)
+        return List_typ, newlist, append, item, freelist
+
+    List_typ, inewlist, iappend, iitem, ifreelist = define_list(Signed)
 
     l = inewlist()
     assert typeOf(l) == Ptr(List_typ)
@@ -77,14 +86,15 @@
     assert len(l.items) == 2
     assert iitem(l, 0) == 2
     assert iitem(l, 1) == 3
+    ifreelist(l)
 
-    IWrap = GcStruct("iwrap", ('v', Signed))
-    List_typ, iwnewlist, iwappend, iwitem = define_list(Ptr(IWrap))
+    IWrap = Struct("iwrap", ('v', Signed))
+    List_typ, iwnewlist, iwappend, iwitem, iwfreelist = define_list(Ptr(IWrap))
 
     l = iwnewlist()
     assert typeOf(l) == Ptr(List_typ)
-    iw2 = malloc(IWrap)
-    iw3 = malloc(IWrap)
+    iw2 = malloc(IWrap, flavor='raw')
+    iw3 = malloc(IWrap, flavor='raw')
     iw2.v = 2
     iw3.v = 3
     assert iw3.v == 3
@@ -93,13 +103,21 @@
     assert len(l.items) == 2
     assert iwitem(l, 0).v == 2
     assert iwitem(l, 1).v == 3
+    iwfreelist(l)
+    free(iw2, flavor='raw')
+    free(iw3, flavor='raw')
+
+    assert objcount == getobjcount()   # test for leak
 
     # not allowed
     S = Struct("s", ('v', Signed))
-    List_typ, iwnewlistzzz, iwappendzzz, iwitemzzz = define_list(S) # works but
+    List_typ, iwnewlistzzz, iwappendzzz, iwitemzzz, iwfreelist = define_list(S)
+    # ^^^ works but
     l = iwnewlistzzz()
-    S1 = GcStruct("strange", ('s', S))
-    py.test.raises(TypeError, "iwappendzzz(l, malloc(S1).s)")
+    s1 = malloc(S, flavor='raw')
+    py.test.raises(TypeError, "iwappendzzz(l, s1)")  # a leak - too bad for now
+    free(s1, flavor='raw')
+    iwfreelist(l)
 
 def test_varsizestruct():
     S1 = GcStruct("s1", ('a', Signed), ('rest', Array(('v', Signed))))



More information about the Pypy-commit mailing list