[pypy-commit] cffi cffi-1.0: start on ffi.include

arigo noreply at buildbot.pypy.org
Mon Apr 27 23:48:52 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r1861:fa462bbfa883
Date: 2015-04-27 18:27 +0200
http://bitbucket.org/cffi/cffi/changeset/fa462bbfa883/

Log:	start on ffi.include

diff --git a/_cffi1/parse_c_type.h b/_cffi1/parse_c_type.h
--- a/_cffi1/parse_c_type.h
+++ b/_cffi1/parse_c_type.h
@@ -80,6 +80,7 @@
 #define _CFFI_F_CHECK_FIELDS  0x02   // complain if fields are not in the
                                      // "standard layout" or if some are missing
 #define _CFFI_F_PACKED        0x04   // for CHECK_FIELDS, assume a packed struct
+#define _CFFI_F_EXTERNAL      0x08   // in some other ffi.include()
 
 struct _cffi_field_s {
     const char *name;
diff --git a/_cffi1/recompiler.py b/_cffi1/recompiler.py
--- a/_cffi1/recompiler.py
+++ b/_cffi1/recompiler.py
@@ -8,9 +8,6 @@
     def __init__(self, ffi, module_name):
         self.ffi = ffi
         self.module_name = module_name
-        #
-        if ']' in self.ffi._cdefsources:
-            raise NotImplementedError("ffi.include()")
 
     def collect_type_table(self):
         self._typesdict = {}
@@ -80,7 +77,8 @@
             if isinstance(tp, model.FunctionPtrType):
                 self._do_collect_type(tp.as_raw_function())
             elif isinstance(tp, model.StructOrUnion):
-                if tp.fldtypes is not None:
+                if tp.fldtypes is not None and (
+                        tp not in self.ffi._parser._included_declarations):
                     for name1, tp1, _ in tp.enumfields():
                         self._do_collect_type(self._field_type(tp, name1, tp1))
             else:
@@ -464,19 +462,24 @@
 
     def _struct_ctx(self, tp, cname, approxname):
         type_index = self._typesdict[tp]
+        reason_for_not_expanding = None
         flags = []
         if isinstance(tp, model.UnionType):
             flags.append("_CFFI_F_UNION")
-        if tp.fldtypes is None:
-            pass    # opaque
-        elif tp.partial or tp.has_anonymous_struct_fields():
-            pass    # the field layout is obtained silently from the C compiler
+        if tp not in self.ffi._parser._included_declarations:
+            if tp.fldtypes is None:
+                reason_for_not_expanding = "opaque"
+            elif tp.partial or tp.has_anonymous_struct_fields():
+                pass    # field layout obtained silently from the C compiler
+            else:
+                flags.append("_CFFI_F_CHECK_FIELDS")
+            if tp.packed:
+                flags.append("_CFFI_F_PACKED")
         else:
-            flags.append("_CFFI_F_CHECK_FIELDS")
-        if tp.packed:
-            flags.append("_CFFI_F_PACKED")
+            flags.append("_CFFI_F_EXTERNAL")
+            reason_for_not_expanding = "external"
         flags = '|'.join(flags) or '0'
-        if tp.fldtypes is not None:
+        if reason_for_not_expanding is None:
             c_field = [approxname]
             enumfields = list(tp.enumfields())
             for fldname, fldtype, fbitsize in enumfields:
@@ -515,7 +518,8 @@
                     '    _cffi_FIELDS_FOR_%s, %d },' % (approxname,
                                                         len(enumfields),))
         else:
-            size_align = ' (size_t)-1, -1, -1, 0 /* opaque */ },'
+            size_align = ' (size_t)-1, -1, -1, 0 /* %s */ },' % (
+                reason_for_not_expanding,)
         self._lsts["struct_union"].append(
             '  { "%s", %d, %s,' % (tp.name, type_index, flags) + size_align)
         self._seen_struct_unions.add(tp)
diff --git a/_cffi1/test_recompiler.py b/_cffi1/test_recompiler.py
--- a/_cffi1/test_recompiler.py
+++ b/_cffi1/test_recompiler.py
@@ -4,8 +4,12 @@
 from _cffi1.udir import udir
 
 
-def check_type_table(input, expected_output):
+def check_type_table(input, expected_output, included=None):
     ffi = FFI()
+    if included:
+        ffi1 = FFI()
+        ffi1.cdef(included)
+        ffi.include(ffi1)
     ffi.cdef(input)
     recomp = recompiler.Recompiler(ffi, 'testmod')
     recomp.collect_type_table()
@@ -92,6 +96,16 @@
     check_type_table("enum foo_e { AA, BB, ... };",
                      "(ENUM 0)")
 
+def test_type_table_include_1():
+    check_type_table("foo_t sin(foo_t);",
+                     "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)",
+                     included="typedef double foo_t;")
+
+def test_type_table_include_2():
+    check_type_table("struct foo_s *sin(struct foo_s *);",
+                     "(FUNCTION 1)(POINTER 3)(FUNCTION_END 0)(STRUCT_UNION 0)",
+                     included="struct foo_s { int x, y; };")
+
 
 def test_math_sin():
     import math
@@ -440,3 +454,25 @@
     ffi.cdef("int glob[];")
     lib = verify(ffi, "test_unspecified_size_of_global", "int glob[10];")
     lib.glob    # does not crash
+
+def test_include_1():
+    ffi1 = FFI(); ffi1.cdef("typedef double foo_t;")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("foo_t ff1(foo_t);")
+    lib = verify(ffi, "test_include_1", "double ff1(double x) { return 42.5; }")
+    assert lib.ff1(0) == 42.5
+
+def test_include_2():
+    ffi1 = FFI(); ffi1.cdef("struct foo_s { int x, y; };")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("struct foo_s *ff2(struct foo_s *);")
+    lib = verify(ffi, "test_include_2",
+                 "struct foo_s { int x, y; };\n"
+                 "struct foo_s *ff2(struct foo_s *p) { p->y++; return p; }")
+    p = ffi.new("struct foo_s *")
+    p.y = 41
+    q = lib.ff2(p)
+    assert q == p
+    assert p.y == 42
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -95,6 +95,7 @@
 
     def __init__(self):
         self._declarations = {}
+        self._included_declarations = set()
         self._anonymous_counter = 0
         self._structnode2type = weakref.WeakKeyDictionary()
         self._override = False
@@ -278,7 +279,7 @@
             raise api.CDefError("unknown identifier '%s'" % (exprnode.name,))
         return self._get_type(exprnode.type)
 
-    def _declare(self, name, obj):
+    def _declare(self, name, obj, included=False):
         if name in self._declarations:
             if self._declarations[name] is obj:
                 return
@@ -288,6 +289,8 @@
                     "try cdef(xx, override=True))" % (name,))
         assert '__dotdotdot__' not in name.split()
         self._declarations[name] = obj
+        if included:
+            self._included_declarations.add(obj)
 
     def _get_type_pointer(self, type, const=False):
         if isinstance(type, model.RawFunctionType):
@@ -601,7 +604,9 @@
     def include(self, other):
         for name, tp in other._declarations.items():
             kind = name.split(' ', 1)[0]
-            if kind in ('typedef', 'struct', 'union', 'enum'):
+            if kind in ('struct', 'union', 'enum'):
+                self._declare(name, tp, included=True)
+            elif kind == 'typedef':
                 self._declare(name, tp)
         for k, v in other._int_constants.items():
             self._add_constants(k, v)


More information about the pypy-commit mailing list