Commits

Armin Rigo  committed 92e67ab

Fix the tests about "typedef enum".

  • Participants
  • Parent commits a5db434

Comments (0)

Files changed (7)

File cffi/cparser.py

             if isinstance(node, pycparser.c_ast.Struct):
                 # XXX do we need self._declare in any of those?
                 if node.decls is not None:
-                    self._get_struct_or_union_type('struct', node)
+                    self._get_struct_union_enum_type('struct', node)
             elif isinstance(node, pycparser.c_ast.Union):
                 if node.decls is not None:
-                    self._get_struct_or_union_type('union', node)
+                    self._get_struct_union_enum_type('union', node)
             elif isinstance(node, pycparser.c_ast.Enum):
                 if node.values is not None:
-                    self._get_enum_type(node)
+                    self._get_struct_union_enum_type('enum', node)
             elif not decl.name:
                 raise api.CDefError("construct does not declare any variable",
                                     decl)
             #
             if isinstance(type, pycparser.c_ast.Struct):
                 # 'struct foobar'
-                return self._get_struct_or_union_type('struct', type, name)
+                return self._get_struct_union_enum_type('struct', type, name)
             #
             if isinstance(type, pycparser.c_ast.Union):
                 # 'union foobar'
-                return self._get_struct_or_union_type('union', type, name)
+                return self._get_struct_union_enum_type('union', type, name)
             #
             if isinstance(type, pycparser.c_ast.Enum):
                 # 'enum foobar'
-                return self._get_enum_type(type)
+                return self._get_struct_union_enum_type('enum', type, name)
         #
         if isinstance(typenode, pycparser.c_ast.FuncDecl):
             # a function type
             return const or 'const' in typenode.quals
         return False
 
-    def _get_struct_or_union_type(self, kind, type, name=None):
+    def _get_struct_union_enum_type(self, kind, type, name=None):
         # First, a level of caching on the exact 'type' node of the AST.
         # This is obscure, but needed because pycparser "unrolls" declarations
         # such as "typedef struct { } foo_t, *foo_p" and we end up with
                 tp = model.StructType(explicit_name, None, None, None)
             elif kind == 'union':
                 tp = model.UnionType(explicit_name, None, None, None)
+            elif kind == 'enum':
+                tp = self._build_enum_type(explicit_name, type.values)
             else:
                 raise AssertionError("kind = %r" % (kind,))
             if name is not None:
                 self._declare(key, tp)
+        else:
+            if kind == 'enum' and type.values is not None:
+                raise NotImplementedError(
+                    "enum %s: the '{}' declaration should appear on the first "
+                    "time the enum is mentioned, not later" % explicit_name)
         tp.forcename = tp.forcename or force_name
         if tp.forcename and '$' in tp.name:
             self._declare('anonymous %s' % tp.forcename, tp)
         #
         self._structnode2type[type] = tp
         #
+        # enums: done here
+        if kind == 'enum':
+            return tp
+        #
         # is there a 'type.decls'?  If yes, then this is the place in the
         # C sources that declare the fields.  If no, then just return the
         # existing type, possibly still incomplete.
         raise api.FFIError("unsupported non-constant or "
                            "not immediately constant expression")
 
-    def _get_enum_type(self, type):
-        # See _get_struct_or_union_type() for the reason of the
-        # complicated logic here.  This is still a simplified version,
-        # assuming that it's ok to assume the more complicated cases
-        # don't occur...
-        try:
-            return self._structnode2type[type]
-        except KeyError:
-            pass
-        name = type.name
-        if name is None:
-            self._anonymous_counter += 1
-            explicit_name = '$%d' % self._anonymous_counter
-            key = None
-        else:
-            explicit_name = name
-            key = 'enum %s' % (name,)
-            tp = self._declarations.get(key, None)
-            if tp is not None:
-                return tp
-        #
-        decls = type.values
+    def _build_enum_type(self, explicit_name, decls):
         if decls is not None:
             enumerators = [enum.name for enum in decls.enumerators]
             partial = False
             enumvalues = tuple(enumvalues) 
             tp = model.EnumType(explicit_name, enumerators, enumvalues)
             tp.partial = partial
-            if key is not None:
-                self._declare(key, tp)
         else:   # opaque enum
-            enumerators = ()
-            enumvalues = ()
             tp = model.EnumType(explicit_name, (), ())
-        self._structnode2type[type] = tp
         return tp

File cffi/model.py

         return global_cache(ffi, 'new_array_type', BPtrItem, self.length)
 
 
-class StructOrUnion(BaseType):
+class StructOrUnionOrEnum(BaseType):
     _attrs_ = ('name',)
     forcename = None
+
+    def _get_c_name(self, replace_with):
+        name = self.forcename or '%s %s' % (self.kind, self.name)
+        return name + replace_with
+
+
+class StructOrUnion(StructOrUnionOrEnum):
     fixedlayout = None
 
     def __init__(self, name, fldnames, fldtypes, fldbitsize):
         self.fldtypes = fldtypes
         self.fldbitsize = fldbitsize
 
-    def _get_c_name(self, replace_with):
-        name = self.forcename or '%s %s' % (self.kind, self.name)
-        return name + replace_with
-
     def finish_backend_type(self, ffi):
         BType = self.new_btype(ffi)
         ffi._cached_btypes[self] = BType
         return ffi._backend.new_union_type(self.name)
 
 
-class EnumType(BaseType):
-    _attrs_ = ('name',)
+class EnumType(StructOrUnionOrEnum):
+    kind = 'enum'
     partial = False
 
     def __init__(self, name, enumerators, enumvalues):
         self.enumerators = enumerators
         self.enumvalues = enumvalues
 
-    def _get_c_name(self, replace_with):
-        return 'enum %s%s' % (self.name, replace_with)
-
     def check_not_partial(self):
         if self.partial:
             from . import ffiplatform

File cffi/vengine_cpy.py

     _generate_cpy_anonymous_collecttype = _generate_nothing
 
     def _generate_cpy_anonymous_decl(self, tp, name):
-        self._generate_struct_or_union_decl(tp, '', name)
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_decl(tp, name, '')
+        else:
+            self._generate_struct_or_union_decl(tp, '', name)
 
     def _generate_cpy_anonymous_method(self, tp, name):
-        self._generate_struct_or_union_method(tp, '', name)
+        if not isinstance(tp, model.EnumType):
+            self._generate_struct_or_union_method(tp, '', name)
 
     def _loading_cpy_anonymous(self, tp, name, module):
-        self._loading_struct_or_union(tp, '', name, module)
+        if isinstance(tp, model.EnumType):
+            self._loading_cpy_enum(tp, name, module)
+        else:
+            self._loading_struct_or_union(tp, '', name, module)
 
     def _loaded_cpy_anonymous(self, tp, name, module, **kwds):
-        self._loaded_struct_or_union(tp)
+        if isinstance(tp, model.EnumType):
+            self._loaded_cpy_enum(tp, name, module, **kwds)
+        else:
+            self._loaded_struct_or_union(tp)
 
     # ----------
     # constants, likely declared with '#define'
     # ----------
     # enums
 
-    def _generate_cpy_enum_decl(self, tp, name):
+    def _generate_cpy_enum_decl(self, tp, name, prefix='enum'):
         if tp.partial:
             for enumerator in tp.enumerators:
                 self._generate_cpy_const(True, enumerator, delayed=False)
             return
         #
-        funcname = '_cffi_enum_%s' % name
+        funcname = '_cffi_e_%s_%s' % (prefix, name)
         prnt = self._prnt
         prnt('static int %s(PyObject *lib)' % funcname)
         prnt('{')
 
     _generate_cpy_enum_collecttype = _generate_nothing
     _generate_cpy_enum_method = _generate_nothing
-    _loading_cpy_enum = _loaded_noop
 
     def _loading_cpy_enum(self, tp, name, module):
         if tp.partial:

File cffi/vengine_gen.py

     # or unions; the 'name' is obtained by a typedef.
 
     def _generate_gen_anonymous_decl(self, tp, name):
-        self._generate_struct_or_union_decl(tp, '', name)
+        if isinstance(tp, model.EnumType):
+            self._generate_gen_enum_decl(tp, name, '')
+        else:
+            self._generate_struct_or_union_decl(tp, '', name)
 
     def _loading_gen_anonymous(self, tp, name, module):
-        self._loading_struct_or_union(tp, '', name, module)
+        if isinstance(tp, model.EnumType):
+            self._loading_gen_enum(tp, name, module, '')
+        else:
+            self._loading_struct_or_union(tp, '', name, module)
 
     def _loaded_gen_anonymous(self, tp, name, module, **kwds):
-        self._loaded_struct_or_union(tp)
+        if isinstance(tp, model.EnumType):
+            self._loaded_gen_enum(tp, name, module, **kwds)
+        else:
+            self._loaded_struct_or_union(tp)
 
     # ----------
     # constants, likely declared with '#define'
     # ----------
     # enums
 
-    def _generate_gen_enum_decl(self, tp, name):
+    def _generate_gen_enum_decl(self, tp, name, prefix='enum'):
         if tp.partial:
             for enumerator in tp.enumerators:
                 self._generate_gen_const(True, enumerator)
             return
         #
-        funcname = '_cffi_enum_%s' % name
+        funcname = '_cffi_e_%s_%s' % (prefix, name)
         self.export_symbols.append(funcname)
         prnt = self._prnt
         prnt('int %s(char *out_error)' % funcname)
 
     _loading_gen_enum = _loaded_noop
 
-    def _loading_gen_enum(self, tp, name, module):
+    def _loading_gen_enum(self, tp, name, module, prefix='enum'):
         if tp.partial:
             enumvalues = [self._load_constant(True, tp, enumerator, module)
                           for enumerator in tp.enumerators]
             tp.partial = False
         else:
             BFunc = self.ffi.typeof("int(*)(char*)")
-            funcname = '_cffi_enum_%s' % name
+            funcname = '_cffi_e_%s_%s' % (prefix, name)
             function = module.load_function(BFunc, funcname)
             p = self.ffi.new("char[]", 256)
             if function(p) < 0:

File testing/backend_tests.py

         ffi = FFI(backend=self.Backend())
         ffi.cdef("typedef enum { Value0 = 0 } e, *pe;\n"
                  "typedef enum { Value1 = 1 } e1;")
-        assert ffi.getctype("e*") == 'enum $1 *'
-        assert ffi.getctype("pe") == 'enum $1 *'
-        assert ffi.getctype("e1*") == 'enum $2 *'
+        assert ffi.getctype("e*") == 'enum $e *'
+        assert ffi.getctype("pe") == 'enum $e *'
+        assert ffi.getctype("e1*") == 'enum $e1 *'
 
     def test_new_ctype(self):
         ffi = FFI(backend=self.Backend())

File testing/test_parsing.py

     ffi = FFI()
     e = py.test.raises(CDefError, ffi.cdef, " x y z ")
     assert re.match(r'cannot parse " x y z "\n:\d+:', str(e.value))
+
+def test_cannot_declare_enum_later():
+    ffi = FFI()
+    e = py.test.raises(NotImplementedError, ffi.cdef,
+                       "typedef enum foo_e foo_t; enum foo_e { AA, BB };")
+    assert str(e.value) == (
+           "enum foo_e: the '{}' declaration should appear on the "
+           "first time the enum is mentioned, not later")

File testing/test_verify.py

     ffi = FFI()
     ffi.cdef("typedef enum { AA, BB } enum1_t;")
     lib = ffi.verify("typedef enum { AA, BB } enum1_t;")
-    assert ffi.string(ffi.cast("enum enum1_e", 1)) == 'BB'
+    assert ffi.string(ffi.cast("enum1_t", 1)) == 'BB'
     assert lib.AA == 0
     assert lib.BB == 1
 
     ffi = FFI()
     ffi.cdef("typedef enum { AA, BB, ... } enum1_t;")
     lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;")
-    assert ffi.string(ffi.cast("enum enum1_e", 1)) == '#1'
-    assert ffi.string(ffi.cast("enum enum1_e", 2)) == 'BB'
+    assert ffi.string(ffi.cast("enum1_t", 1)) == '#1'
+    assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB'
     assert lib.AA == 0
     assert lib.BB == 2