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

arigo at codespeak.net arigo at codespeak.net
Tue Sep 18 09:56:09 CEST 2007


Author: arigo
Date: Tue Sep 18 09:56:08 2007
New Revision: 46704

Modified:
   pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py
   pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
Log:
Support for recursive types in ll2ctypes.


Modified: pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py	Tue Sep 18 09:56:08 2007
@@ -21,6 +21,8 @@
 
 
 _ctypes_cache = {}
+_delayed_ptrs = []
+_gettype_recursion = 0
 
 def _setup_ctypes_cache():
     from pypy.rpython.lltypesystem import rffi
@@ -129,32 +131,58 @@
     try:
         return _ctypes_cache[T]
     except KeyError:
-        if isinstance(T, lltype.Ptr):
-            if isinstance(T.TO, lltype.FuncType):
-                argtypes = [get_ctypes_type(ARG) for ARG in T.TO.ARGS]
-                if T.TO.RESULT is lltype.Void:
-                    restype = None
-                else:
-                    restype = get_ctypes_type(T.TO.RESULT)
-                cls = ctypes.CFUNCTYPE(restype, *argtypes)
+        global _gettype_recursion
+        _gettype_recursion += 1
+        try:
+            cls = build_new_ctypes_type(T)
+            if T not in _ctypes_cache:
+                _ctypes_cache[T] = cls
             else:
-                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)
-        elif isinstance(T, lltype.OpaqueType):
-            if T.hints.get('external', None) != 'C':
-                raise TypeError("%s is not external" % T)
-            cls = ctypes.c_char * T.hints['getsize']()
-        else:
-            _setup_ctypes_cache()
-            if T in _ctypes_cache:
-                return _ctypes_cache[T]
-            raise NotImplementedError(T)
-        _ctypes_cache[T] = cls
+                # check for buggy recursive structure logic
+                assert _ctypes_cache[T] is cls
+
+            if _gettype_recursion == 1:
+                complete_pointer_types()
+        finally:
+            _gettype_recursion -= 1
         return cls
 
+def build_new_ctypes_type(T):
+    if isinstance(T, lltype.Ptr):
+        if isinstance(T.TO, lltype.FuncType):
+            argtypes = [get_ctypes_type(ARG) for ARG in T.TO.ARGS]
+            if T.TO.RESULT is lltype.Void:
+                restype = None
+            else:
+                restype = get_ctypes_type(T.TO.RESULT)
+            return ctypes.CFUNCTYPE(restype, *argtypes)
+        elif isinstance(T.TO, lltype.Struct):
+            # for recursive structures: build a forward pointer first
+            uniquename = 'ctypes_%s_%d' % (T.TO.__name__, len(_ctypes_cache))
+            pcls = ctypes.POINTER(uniquename)
+            _delayed_ptrs.append((pcls, T.TO))
+            return pcls
+        else:
+            return ctypes.POINTER(get_ctypes_type(T.TO))
+    elif isinstance(T, lltype.Struct):
+        return build_ctypes_struct(T)
+    elif isinstance(T, lltype.Array):
+        return build_ctypes_array(T)
+    elif isinstance(T, lltype.OpaqueType):
+        if T.hints.get('external', None) != 'C':
+            raise TypeError("%s is not external" % T)
+        return ctypes.c_char * T.hints['getsize']()
+    else:
+        _setup_ctypes_cache()
+        if T in _ctypes_cache:
+            return _ctypes_cache[T]
+        raise NotImplementedError(T)
+
+def complete_pointer_types():
+    while _delayed_ptrs:
+        pcls, S = _delayed_ptrs.pop()
+        ctypes.SetPointerType(pcls, get_ctypes_type(S))
+
 
 def convert_struct(container, cstruct=None):
     STRUCT = container._TYPE

Modified: pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py	Tue Sep 18 09:56:08 2007
@@ -51,6 +51,28 @@
         assert sc.contents.y == 52
         lltype.free(s, flavor='raw')
 
+    def test_struct_ptrs(self):
+        S2 = lltype.Struct('S2', ('y', lltype.Signed))
+        S1 = lltype.Struct('S', ('x', lltype.Signed), ('p', lltype.Ptr(S2)))
+        s1 = lltype.malloc(S1, flavor='raw')
+        s2a = lltype.malloc(S2, flavor='raw')
+        s2b = lltype.malloc(S2, flavor='raw')
+        s2a.y = ord('a')
+        s2b.y = ord('b')
+        sc1 = lltype2ctypes(s1)
+        sc1.contents.x = 50
+        assert s1.x == 50
+        sc1.contents.p = lltype2ctypes(s2a)
+        assert s1.p == s2a
+        s1.p.y -= 32
+        assert sc1.contents.p.contents.y == ord('A')
+        s1.p = s2b
+        sc1.contents.p.contents.y -= 32
+        assert s2b.y == ord('B')
+        lltype.free(s1, flavor='raw')
+        lltype.free(s2a, flavor='raw')
+        lltype.free(s2b, flavor='raw')
+
     def test_simple_array(self):
         A = lltype.Array(lltype.Signed)
         a = lltype.malloc(A, 10, flavor='raw')
@@ -406,9 +428,78 @@
         lltype.free(s, flavor='raw')
 
     def test_recursive_struct(self):
-        py.test.skip("Not implemented")
         SX = lltype.ForwardReference()
-        S1 = lltype.Struct('S1', ('x', lltype.Ptr(SX)))
-        S1.x.TO.become(S1)
-        s = lltype.malloc(S1, flavor='raw')
-        sc = lltype2ctypes(s)
+        S1 = lltype.Struct('S1', ('p', lltype.Ptr(SX)), ('x', lltype.Signed))
+        SX.become(S1)
+        # a chained list
+        s1 = lltype.malloc(S1, flavor='raw')
+        s2 = lltype.malloc(S1, flavor='raw')
+        s3 = lltype.malloc(S1, flavor='raw')
+        s1.x = 111
+        s2.x = 222
+        s3.x = 333
+        s1.p = s2
+        s2.p = s3
+        s3.p = lltype.nullptr(S1)
+        sc1 = lltype2ctypes(s1)
+        sc2 = sc1.contents.p
+        sc3 = sc2.contents.p
+        assert not sc3.contents.p
+        assert sc1.contents.x == 111
+        assert sc2.contents.x == 222
+        assert sc3.contents.x == 333
+        sc3.contents.x += 1
+        assert s3.x == 334
+        s3.x += 2
+        assert sc3.contents.x == 336
+        lltype.free(s1, flavor='raw')
+        lltype.free(s2, flavor='raw')
+        lltype.free(s3, flavor='raw')
+        # a self-cycle
+        s1 = lltype.malloc(S1, flavor='raw')
+        s1.x = 12
+        s1.p = s1
+        sc1 = lltype2ctypes(s1)
+        assert sc1.contents.x == 12
+        assert (ctypes.addressof(sc1.contents.p.contents) ==
+                ctypes.addressof(sc1.contents))
+        s1.x *= 5
+        assert sc1.contents.p.contents.p.contents.p.contents.x == 60
+        lltype.free(s1, flavor='raw')
+        # a longer cycle
+        s1 = lltype.malloc(S1, flavor='raw')
+        s2 = lltype.malloc(S1, flavor='raw')
+        s1.x = 111
+        s1.p = s2
+        s2.x = 222
+        s2.p = s1
+        sc1 = lltype2ctypes(s1)
+        assert sc1.contents.x == 111
+        assert sc1.contents.p.contents.x == 222
+        assert (ctypes.addressof(sc1.contents.p.contents) !=
+                ctypes.addressof(sc1.contents))
+        assert (ctypes.addressof(sc1.contents.p.contents.p.contents) ==
+                ctypes.addressof(sc1.contents))
+        lltype.free(s1, flavor='raw')
+        lltype.free(s2, flavor='raw')
+
+    def test_indirect_recursive_struct(self):
+        S2Forward = lltype.ForwardReference()
+        S1 = lltype.Struct('S1', ('p', lltype.Ptr(S2Forward)))
+        A2 = lltype.Array(lltype.Ptr(S1), hints={'nolength': True})
+        S2 = lltype.Struct('S2', ('a', lltype.Ptr(A2)))
+        S2Forward.become(S2)
+        s1 = lltype.malloc(S1, flavor='raw')
+        a2 = lltype.malloc(A2, 10, flavor='raw')
+        s2 = lltype.malloc(S2, flavor='raw')
+        s2.a = a2
+        a2[5] = s1
+        s1.p = s2
+        ac2 = lltype2ctypes(a2, normalize=False)
+        sc1 = ac2.contents.items[5]
+        sc2 = sc1.contents.p
+        assert (ctypes.addressof(sc2.contents.a.contents) ==
+                ctypes.addressof(ac2.contents))
+        lltype.free(s1, flavor='raw')
+        lltype.free(a2, flavor='raw')
+        lltype.free(s2, flavor='raw')



More information about the Pypy-commit mailing list