Commits

Armin Rigo  committed a5e3e36 Merge

hg merge auto-types

  • Participants
  • Parent commits 4aed2f4, c1ea20e

Comments (0)

Files changed (9)

File c/_cffi_backend.c

         return NULL;
 
     flag = CT_STRUCT;
-    if (strcmp(name, "_IO_FILE") == 0)
+    if (strcmp(name, "_IO_FILE") == 0 || strcmp(name, "$FILE") == 0)
         flag |= CT_IS_FILE;
     return _b_struct_or_union_type("struct", name, flag);
 }
     if sys.platform == "win32":
         py.test.skip("testing FILE not implemented")
     #
-    BFILE = new_struct_type("_IO_FILE")
+    BFILE = new_struct_type("$FILE")
     BFILEP = new_pointer_type(BFILE)
     BChar = new_primitive_type("char")
     BCharP = new_pointer_type(BChar)
             if name.startswith('RTLD_'):
                 setattr(self, name, getattr(backend, name))
         #
-        self._parser._declarations['typedef FILE'] = model.file_type
         BVoidP = self._get_cached_btype(model.voidp_type)
         if isinstance(backend, types.ModuleType):
             # _cffi_backend: attach these constants to the class

File cffi/commontypes.py

+import sys
+from . import api, model
+
+
+COMMON_TYPES = {
+    'FILE': model.unknown_type('FILE', '_IO_FILE'),
+    'bool': '_Bool',
+    }
+
+for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
+    if _type.endswith('_t'):
+        COMMON_TYPES[_type] = _type
+del _type
+
+_CACHE = {}
+
+def resolve_common_type(commontype):
+    try:
+        return _CACHE[commontype]
+    except KeyError:
+        result = COMMON_TYPES.get(commontype, commontype)
+        if not isinstance(result, str):
+            pass    # result is already a BaseType
+        elif result.endswith(' *'):
+            if result.startswith('const '):
+                result = model.ConstPointerType(
+                    resolve_common_type(result[6:-2]))
+            else:
+                result = model.PointerType(resolve_common_type(result[:-2]))
+        elif result in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
+            result = model.PrimitiveType(result)
+        else:
+            assert commontype != result
+            result = resolve_common_type(result)   # recursively
+        assert isinstance(result, model.BaseTypeByIdentity)
+        _CACHE[commontype] = result
+        return result
+
+
+# ____________________________________________________________
+# Windows common types
+
+
+def win_common_types(maxsize):
+    result = {}
+    if maxsize < (1<<32):
+        result.update({      # Windows 32-bits
+            'HALF_PTR': 'short',
+            'INT_PTR': 'int',
+            'LONG_PTR': 'long',
+            'UHALF_PTR': 'unsigned short',
+            'UINT_PTR': 'unsigned int',
+            'ULONG_PTR': 'unsigned long',
+            })
+    else:
+        result.update({      # Windows 64-bits
+            'HALF_PTR': 'int',
+            'INT_PTR': 'long long',
+            'LONG_PTR': 'long long',
+            'UHALF_PTR': 'unsigned int',
+            'UINT_PTR': 'unsigned long long',
+            'ULONG_PTR': 'unsigned long long',
+            })
+    result.update({
+        "BYTE": "unsigned char",
+        "BOOL": "int",
+        "CCHAR": "char",
+        "CHAR": "char",
+        "DWORD": "unsigned long",
+        "DWORD32": "unsigned int",
+        "DWORD64": "unsigned long long",
+        "FLOAT": "float",
+        "INT": "int",
+        "INT8": "signed char",
+        "INT16": "short",
+        "INT32": "int",
+        "INT64": "long long",
+        "LONG": "long",
+        "LONGLONG": "long long",
+        "LONG32": "int",
+        "LONG64": "long long",
+        "WORD": "unsigned short",
+        "PVOID": model.voidp_type,
+        "ULONGLONG": "unsigned long long",
+        "WCHAR": "wchar_t",
+        "SHORT": "short",
+        "TBYTE": "WCHAR",
+        "TCHAR": "WCHAR",
+        "UCHAR": "unsigned char",
+        "UINT": "unsigned int",
+        "UINT8": "unsigned char",
+        "UINT16": "unsigned short",
+        "UINT32": "unsigned int",
+        "UINT64": "unsigned long long",
+        "ULONG": "unsigned long",
+        "ULONG32": "unsigned int",
+        "ULONG64": "unsigned long long",
+        "USHORT": "unsigned short",
+
+        "SIZE_T": "ULONG_PTR",
+        "SSIZE_T": "LONG_PTR",
+        "ATOM": "WORD",
+        "BOOLEAN": "BYTE",
+        "COLORREF": "DWORD",
+
+        "HANDLE": "PVOID",
+        "DWORDLONG": "ULONGLONG",
+        "DWORD_PTR": "ULONG_PTR",
+        "HACCEL": "HANDLE",
+
+        "HBITMAP": "HANDLE",
+        "HBRUSH": "HANDLE",
+        "HCOLORSPACE": "HANDLE",
+        "HCONV": "HANDLE",
+        "HCONVLIST": "HANDLE",
+        "HDC": "HANDLE",
+        "HDDEDATA": "HANDLE",
+        "HDESK": "HANDLE",
+        "HDROP": "HANDLE",
+        "HDWP": "HANDLE",
+        "HENHMETAFILE": "HANDLE",
+        "HFILE": "int",
+        "HFONT": "HANDLE",
+        "HGDIOBJ": "HANDLE",
+        "HGLOBAL": "HANDLE",
+        "HHOOK": "HANDLE",
+        "HICON": "HANDLE",
+        "HCURSOR": "HICON",
+        "HINSTANCE": "HANDLE",
+        "HKEY": "HANDLE",
+        "HKL": "HANDLE",
+        "HLOCAL": "HANDLE",
+        "HMENU": "HANDLE",
+        "HMETAFILE": "HANDLE",
+        "HMODULE": "HINSTANCE",
+        "HMONITOR": "HANDLE",
+        "HPALETTE": "HANDLE",
+        "HPEN": "HANDLE",
+        "HRESULT": "LONG",
+        "HRGN": "HANDLE",
+        "HRSRC": "HANDLE",
+        "HSZ": "HANDLE",
+        "WINSTA": "HANDLE",
+        "HWND": "HANDLE",
+
+        "LANGID": "WORD",
+        "LCID": "DWORD",
+        "LCTYPE": "DWORD",
+        "LGRPID": "DWORD",
+        "LPARAM": "LONG_PTR",
+        "LPBOOL": "BOOL *",
+        "LPBYTE": "BYTE *",
+        "LPCOLORREF": "DWORD *",
+        "LPCSTR": "const char *",
+
+        "LPCVOID": model.const_voidp_type,
+        "LPCWSTR": "const WCHAR *",
+        "LPCTSTR": "LPCWSTR",
+        "LPDWORD": "DWORD *",
+        "LPHANDLE": "HANDLE *",
+        "LPINT": "int *",
+        "LPLONG": "long *",
+        "LPSTR": "CHAR *",
+        "LPWSTR": "WCHAR *",
+        "LPTSTR": "LPWSTR",
+        "LPVOID": model.voidp_type,
+        "LPWORD": "WORD *",
+        "LRESULT": "LONG_PTR",
+        "PBOOL": "BOOL *",
+        "PBOOLEAN": "BOOLEAN *",
+        "PBYTE": "BYTE *",
+        "PCHAR": "CHAR *",
+        "PCSTR": "const CHAR *",
+        "PCTSTR": "LPCWSTR",
+        "PCWSTR": "const WCHAR *",
+        "PDWORD": "DWORD *",
+        "PDWORDLONG": "DWORDLONG *",
+        "PDWORD_PTR": "DWORD_PTR *",
+        "PDWORD32": "DWORD32 *",
+        "PDWORD64": "DWORD64 *",
+        "PFLOAT": "FLOAT *",
+        "PHALF_PTR": "HALF_PTR *",
+        "PHANDLE": "HANDLE *",
+        "PHKEY": "HKEY *",
+        "PINT": "int *",
+        "PINT_PTR": "INT_PTR *",
+        "PINT8": "INT8 *",
+        "PINT16": "INT16 *",
+        "PINT32": "INT32 *",
+        "PINT64": "INT64 *",
+        "PLCID": "PDWORD",
+        "PLONG": "LONG *",
+        "PLONGLONG": "LONGLONG *",
+        "PLONG_PTR": "LONG_PTR *",
+        "PLONG32": "LONG32 *",
+        "PLONG64": "LONG64 *",
+        "PSHORT": "SHORT *",
+        "PSIZE_T": "SIZE_T *",
+        "PSSIZE_T": "SSIZE_T *",
+        "PSTR": "CHAR *",
+        "PTBYTE": "TBYTE *",
+        "PTCHAR": "TCHAR *",
+        "PTSTR": "LPWSTR",
+        "PUCHAR": "UCHAR *",
+        "PUHALF_PTR": "UHALF_PTR *",
+        "PUINT": "UINT *",
+        "PUINT_PTR": "UINT_PTR *",
+        "PUINT8": "UINT8 *",
+        "PUINT16": "UINT16 *",
+        "PUINT32": "UINT32 *",
+        "PUINT64": "UINT64 *",
+        "PULONG": "ULONG *",
+        "PULONGLONG": "ULONGLONG *",
+        "PULONG_PTR": "ULONG_PTR *",
+        "PULONG32": "ULONG32 *",
+        "PULONG64": "ULONG64 *",
+        "PUSHORT": "USHORT *",
+        "PWCHAR": "WCHAR *",
+        "PWORD": "WORD *",
+        "PWSTR": "WCHAR *",
+        "QWORD": "unsigned long long",
+        "SC_HANDLE": "HANDLE",
+        "SC_LOCK": "LPVOID",
+        "SERVICE_STATUS_HANDLE": "HANDLE",
+
+        "UNICODE_STRING": model.StructType(
+            "_UNICODE_STRING",
+            ["Length",
+             "MaximumLength",
+             "Buffer"],
+            [model.PrimitiveType("unsigned short"),
+             model.PrimitiveType("unsigned short"),
+             model.PointerType(model.PrimitiveType("wchar_t"))],
+            [-1, -1, -1]),
+        "PUNICODE_STRING": "UNICODE_STRING *",
+        "PCUNICODE_STRING": "const UNICODE_STRING *",
+
+        "USN": "LONGLONG",
+        "VOID": model.void_type,
+        "WPARAM": "UINT_PTR",
+        })
+    return result
+
+
+if sys.platform == 'win32':
+    COMMON_TYPES.update(win_common_types(sys.maxsize))

File cffi/cparser.py

 
 from . import api, model
+from .commontypes import COMMON_TYPES, resolve_common_type
 import pycparser.c_parser, weakref, re, sys
 
 try:
 _r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}")
 _r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$")
 _r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]")
+_r_words = re.compile(r"\w+|\S")
 _parser_cache = None
 
 def _get_parser():
     # which is declared with a typedef for the purpose of C parsing.
     return csource.replace('...', ' __dotdotdot__ '), macros
 
+def _common_type_names(csource):
+    # Look in the source for what looks like usages of types from the
+    # list of common types.  A "usage" is approximated here as the
+    # appearance of the word, minus a "definition" of the type, which
+    # is the last word in a "typedef" statement.  Approximative only
+    # but should be fine for all the common types.
+    look_for_words = set(COMMON_TYPES)
+    look_for_words.add(';')
+    look_for_words.add('typedef')
+    words_used = set()
+    is_typedef = False
+    previous_word = ''
+    for word in _r_words.findall(csource):
+        if word in look_for_words:
+            if word == ';':
+                if is_typedef:
+                    words_used.discard(previous_word)
+                    look_for_words.discard(previous_word)
+                    is_typedef = False
+            elif word == 'typedef':
+                is_typedef = True
+            else:   # word in COMMON_TYPES
+                words_used.add(word)
+        previous_word = word
+    return words_used
+
+
 class Parser(object):
-    TYPEDEFS = sorted(
-        [_name for _name in model.PrimitiveType.ALL_PRIMITIVE_TYPES
-               if _name.endswith('_t')])
 
     def __init__(self):
         self._declarations = {}
         self._override = False
 
     def _parse(self, csource):
+        csource, macros = _preprocess(csource)
         # XXX: for more efficiency we would need to poke into the
         # internals of CParser...  the following registers the
         # typedefs, because their presence or absence influences the
         # parsing itself (but what they are typedef'ed to plays no role)
-        typenames = self.TYPEDEFS[:]
+        ctn = _common_type_names(csource)
+        print `csource`, ctn
+        typenames = []
         for name in sorted(self._declarations):
             if name.startswith('typedef '):
-                typenames.append(name[8:])
+                name = name[8:]
+                typenames.append(name)
+                ctn.discard(name)
+        typenames += sorted(ctn)
+        #
         csourcelines = ['typedef int %s;' % typename for typename in typenames]
         csourcelines.append('typedef int __dotdotdot__;')
-        csource, macros = _preprocess(csource)
         csourcelines.append(csource)
         csource = '\n'.join(csourcelines)
         if lock is not None:
                     return model.void_type
                 if ident == '__dotdotdot__':
                     raise api.FFIError('bad usage of "..."')
-                return model.PrimitiveType(ident)
+                return resolve_common_type(ident)
             #
             if isinstance(type, pycparser.c_ast.Struct):
                 # 'struct foobar'

File cffi/model.py

         'float':              'f',
         'double':             'f',
         'long double':        'f',
-        'wchar_t':            'c',
         '_Bool':              'u',
         # the following types are not primitive in the C sense
+        'wchar_t':            'c',
         'int8_t':             'i',
         'uint8_t':            'u',
         'int16_t':            'i',
         BPtr = PointerType(self.totype).get_cached_btype(ffi, finishlist)
         return BPtr
 
+const_voidp_type = ConstPointerType(void_type)
+
 
 class NamedPointerType(PointerType):
     _attrs_ = ('totype', 'name')
     tp = StructType(structname, None, None, None)
     return NamedPointerType(tp, name)
 
-file_type = unknown_type('FILE', '_IO_FILE')
-
 def global_cache(srctype, ffi, funcname, *args, **kwds):
     key = kwds.pop('key', (funcname, args))
     assert not kwds

File doc/source/index.rst

 * wchar_t (if supported by the backend)
 
 * *New in version 0.4:* _Bool.  If not directly supported by the C compiler,
-  this is declared with the size of ``unsigned char``.  Note that the
-  effects of ``<stdbool.h>`` are not automatically included: you have
-  to say ``typedef _Bool bool;`` in your ``cdef()`` if you want to
-  use this ``_Bool`` with the more standard name ``bool``.  This is because
-  some headers declare a different type (e.g. an enum) and also call it
-  ``bool``.
+  this is declared with the size of ``unsigned char``.
+
+* *New in version 0.6:* bool.  In CFFI 0.4 or 0.5, you had to manually say
+  ``typedef _Bool bool;``.  Now such a line is optional.
 
 * *New in version 0.4:* FILE.  You can declare C functions taking a
   ``FILE *`` argument and call them with a Python file object.  If needed,
   you can also do ``c_f = ffi.cast("FILE *", fileobj)`` and then pass around
   ``c_f``.
 
-.. "versionadded:: 0.4": bool
+* *New in version 0.6:* all `common Windows types`_ are defined if you run
+  on Windows (``DWORD``, ``LPARAM``, etc.).
+
+.. _`common Windows types`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx
+
+.. "versionadded:: 0.4": _Bool
+.. "versionadded:: 0.6": bool
 .. "versionadded:: 0.4": FILE
+.. "versionadded:: 0.6": Wintypes
 
 As we will see on `the verification step`_ below, the declarations can
 also contain "``...``" at various places; these are placeholders that will
 be completed by a call to ``verify()``.
 
+.. versionadded:: 0.6
+   The standard type names listed above are now handled as *defaults*
+   only (apart from the ones that are keywords in the C language).
+   If your ``cdef`` contains an explicit typedef that redefines one of
+   the types above, then the default described above is ignored.  (This
+   is a bit hard to implement cleanly, so in some corner cases it might
+   fail, notably with the error ``Multiple type specifiers with a type
+   tag``.  Please report it as a bug if it does.)
+
 
 Loading libraries
 -----------------

File testing/test_parsing.py

     assert str(e.value).startswith('cannot parse "foobarbazunknown*"')
     e = py.test.raises(CDefError, ffi.cast, "int(*)(foobarbazunknown)", 0)
     assert str(e.value).startswith('cannot parse "int(*)(foobarbazunknown)"')
+
+def test_redefine_common_type():
+    ffi = FFI()
+    ffi.cdef("typedef char FILE;")
+    assert repr(ffi.cast("FILE", 123)) == "<cdata 'char' '{'>"
+    ffi.cdef("typedef char int32_t;")
+    assert repr(ffi.cast("int32_t", 123)) == "<cdata 'char' '{'>"
+
+def test_bool():
+    ffi = FFI()
+    ffi.cdef("void f(bool);")
+    #
+    ffi = FFI()
+    ffi.cdef("typedef _Bool bool; void f(bool);")
+
+def test_win_common_types():
+    from cffi.commontypes import COMMON_TYPES, _CACHE
+    from cffi.commontypes import win_common_types, resolve_common_type
+    #
+    def clear_all(extra={}, old_dict=COMMON_TYPES.copy()):
+        COMMON_TYPES.clear()
+        COMMON_TYPES.update(old_dict)
+        COMMON_TYPES.update(extra)
+        _CACHE.clear()
+    #
+    for maxsize in [2**32-1, 2**64-1]:
+        ct = win_common_types(maxsize)
+        clear_all(ct)
+        for key in sorted(ct):
+            resolve_common_type(key)
+    # assert did not crash
+    # now try to use e.g. WPARAM (-> UINT_PTR -> unsigned 32/64-bit)
+    for maxsize in [2**32-1, 2**64-1]:
+        ct = win_common_types(maxsize)
+        clear_all(ct)
+        ffi = FFI()
+        value = int(ffi.cast("WPARAM", -1))
+        assert value == maxsize
+    #
+    clear_all()
+
+def test_WPARAM_on_windows():
+    if sys.platform != 'win32':
+        py.test.skip("Only for Windows")
+    ffi = FFI()
+    ffi.cdef("void f(WPARAM);")

File testing/test_verify.py

         for sign in ['signed', 'unsigned']:
             type = '%s %s' % (sign, basetype)
             assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1
+            assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1
             assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0
 
 def test_addressof():