cffi-0.8.2/0000755000076400001440000000000012306266401012714 5ustar arigousers00000000000000cffi-0.8.2/MANIFEST.in0000644000076400001440000000034112244375414014456 0ustar arigousers00000000000000recursive-include cffi *.py recursive-include c *.c *.h *.asm *.py win64.obj recursive-include testing *.py recursive-include doc *.py *.rst Makefile *.bat recursive-include demo py.cleanup *.py include LICENSE setup_base.py cffi-0.8.2/setup.cfg0000644000076400001440000000007312306266401014535 0ustar arigousers00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 cffi-0.8.2/PKG-INFO0000644000076400001440000000163212306266401014013 0ustar arigousers00000000000000Metadata-Version: 1.1 Name: cffi Version: 0.8.2 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski Author-email: python-cffi@googlegroups.com License: MIT Description: CFFI ==== Foreign Function Interface for Python calling C code. Please see the `Documentation `_. Contact ------- `Mailing list `_ Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 cffi-0.8.2/setup_base.py0000644000076400001440000000160412067103443015421 0ustar arigousers00000000000000import sys, os from setup import include_dirs, sources, libraries, define_macros from setup import library_dirs, extra_compile_args, extra_link_args if __name__ == '__main__': from distutils.core import setup from distutils.extension import Extension standard = '__pypy__' not in sys.modules setup(packages=['cffi'], requires=['pycparser'], ext_modules=[Extension(name = '_cffi_backend', include_dirs=include_dirs, sources=sources, libraries=libraries, define_macros=define_macros, library_dirs=library_dirs, extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, )] * standard) cffi-0.8.2/cffi.egg-info/0000755000076400001440000000000012306266401015315 5ustar arigousers00000000000000cffi-0.8.2/cffi.egg-info/dependency_links.txt0000644000076400017500000000000112306266400021326 0ustar arigoarigo00000000000000 cffi-0.8.2/cffi.egg-info/top_level.txt0000644000076400017500000000002312306266400020005 0ustar arigoarigo00000000000000_cffi_backend cffi cffi-0.8.2/cffi.egg-info/requires.txt0000644000076400017500000000001112306266400017650 0ustar arigoarigo00000000000000pycparsercffi-0.8.2/cffi.egg-info/PKG-INFO0000644000076400017500000000163212306266400016357 0ustar arigoarigo00000000000000Metadata-Version: 1.1 Name: cffi Version: 0.8.2 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski Author-email: python-cffi@googlegroups.com License: MIT Description: CFFI ==== Foreign Function Interface for Python calling C code. Please see the `Documentation `_. Contact ------- `Mailing list `_ Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 cffi-0.8.2/cffi.egg-info/not-zip-safe0000644000076400001440000000000112026557304017547 0ustar arigousers00000000000000 cffi-0.8.2/cffi.egg-info/SOURCES.txt0000644000076400017500000000613112306266401017146 0ustar arigoarigo00000000000000LICENSE MANIFEST.in setup.py setup_base.py c/_cffi_backend.c c/check__thread.c c/file_emulator.h c/malloc_closure.h c/minibuffer.h c/misc_thread.h c/misc_win32.h c/test_c.py c/wchar_helper.h c/x.py c/libffi_msvc/ffi.c c/libffi_msvc/ffi.h c/libffi_msvc/ffi_common.h c/libffi_msvc/fficonfig.h c/libffi_msvc/ffitarget.h c/libffi_msvc/prep_cif.c c/libffi_msvc/types.c c/libffi_msvc/win32.c c/libffi_msvc/win64.asm c/libffi_msvc/win64.obj cffi/__init__.py cffi/api.py cffi/backend_ctypes.py cffi/commontypes.py cffi/cparser.py cffi/ffiplatform.py cffi/gc_weakref.py cffi/lock.py cffi/model.py cffi/vengine_cpy.py cffi/vengine_gen.py cffi/verifier.py cffi.egg-info/PKG-INFO cffi.egg-info/SOURCES.txt cffi.egg-info/dependency_links.txt cffi.egg-info/not-zip-safe cffi.egg-info/requires.txt cffi.egg-info/top_level.txt demo/_csvmodule.py demo/_curses.py demo/api.py demo/bsdopendirtype.py demo/btrfs-snap.py demo/cffi-cocoa.py demo/fastcsv.py demo/gmp.py demo/image.py demo/pwuid.py demo/py.cleanup demo/pyobj.py demo/readdir.py demo/readdir2.py demo/readdir_ctypes.py demo/sarvi.py demo/setup.py demo/syslog.py demo/ui.py demo/winclipboard.py demo/xclient.py demo/y.py doc/Makefile doc/design.rst doc/make.bat doc/source/conf.py doc/source/index.rst testing/__init__.py testing/backend_tests.py testing/callback_in_thread.py testing/support.py testing/test_cdata.py testing/test_ctypes.py testing/test_ffi_backend.py testing/test_function.py testing/test_model.py testing/test_ownlib.py testing/test_parsing.py testing/test_platform.py testing/test_unicode_literals.py testing/test_verify.py testing/test_verify2.py testing/test_version.py testing/test_vgen.py testing/test_vgen2.py testing/test_zdistutils.py testing/test_zintegration.py testing/udir.py testing/snippets/distutils_module/setup.py testing/snippets/distutils_module/snip_basic_verify.py testing/snippets/distutils_module/build/lib.linux-x86_64-3.2/snip_basic_verify.py testing/snippets/distutils_package_1/setup.py testing/snippets/distutils_package_1/build/lib.linux-x86_64-3.2/snip_basic_verify1/__init__.py testing/snippets/distutils_package_1/snip_basic_verify1/__init__.py testing/snippets/distutils_package_2/setup.py testing/snippets/distutils_package_2/build/lib.linux-x86_64-3.2/snip_basic_verify2/__init__.py testing/snippets/distutils_package_2/snip_basic_verify2/__init__.py testing/snippets/infrastructure/setup.py testing/snippets/infrastructure/build/lib/snip_infrastructure/__init__.py testing/snippets/infrastructure/snip_infrastructure/__init__.py testing/snippets/setuptools_module/setup.py testing/snippets/setuptools_module/snip_setuptools_verify.py testing/snippets/setuptools_module/build/lib.linux-x86_64-3.2/snip_setuptools_verify.py testing/snippets/setuptools_package_1/setup.py testing/snippets/setuptools_package_1/build/lib.linux-x86_64-3.2/snip_setuptools_verify1/__init__.py testing/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py testing/snippets/setuptools_package_2/setup.py testing/snippets/setuptools_package_2/build/lib.linux-x86_64-3.2/snip_setuptools_verify2/__init__.py testing/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.pycffi-0.8.2/setup.py0000644000076400001440000001172712306266214014440 0ustar arigousers00000000000000import sys, os import subprocess import errno sources = ['c/_cffi_backend.c'] libraries = ['ffi'] include_dirs = ['/usr/include/ffi', '/usr/include/libffi'] # may be changed by pkg-config define_macros = [] library_dirs = [] extra_compile_args = [] extra_link_args = [] def _ask_pkg_config(resultlist, option, result_prefix='', sysroot=False): pkg_config = os.environ.get('PKG_CONFIG','pkg-config') try: p = subprocess.Popen([pkg_config, option, 'libffi'], stdout=subprocess.PIPE) except OSError as e: if e.errno != errno.ENOENT: raise else: t = p.stdout.read().decode().strip() if p.wait() == 0: res = t.split() # '-I/usr/...' -> '/usr/...' for x in res: assert x.startswith(result_prefix) res = [x[len(result_prefix):] for x in res] #print 'PKG_CONFIG:', option, res # sysroot = sysroot and os.environ.get('PKG_CONFIG_SYSROOT_DIR', '') if sysroot: # old versions of pkg-config don't support this env var, # so here we emulate its effect if needed res = [path if path.startswith(sysroot) else sysroot + path for path in res] # resultlist[:] = res def ask_supports_thread(): if sys.platform == "darwin": sys.stderr.write("OS/X: confusion between 'cc' versus 'gcc'") sys.stderr.write(" (see issue 123)\n") sys.stderr.write("will not use '__thread' in the C code\n") return import distutils.errors from distutils.ccompiler import new_compiler compiler = new_compiler(force=1) try: compiler.compile(['c/check__thread.c']) except distutils.errors.CompileError: sys.stderr.write("the above error message can be safely ignored;\n") sys.stderr.write("will not use '__thread' in the C code\n") else: define_macros.append(('USE__THREAD', None)) try: os.unlink('c/check__thread.o') except OSError: pass def use_pkg_config(): _ask_pkg_config(include_dirs, '--cflags-only-I', '-I', sysroot=True) _ask_pkg_config(extra_compile_args, '--cflags-only-other') _ask_pkg_config(library_dirs, '--libs-only-L', '-L', sysroot=True) _ask_pkg_config(extra_link_args, '--libs-only-other') _ask_pkg_config(libraries, '--libs-only-l', '-l') if sys.platform == 'win32': COMPILE_LIBFFI = 'c/libffi_msvc' # from the CPython distribution else: COMPILE_LIBFFI = None if COMPILE_LIBFFI: assert os.path.isdir(COMPILE_LIBFFI), "directory not found!" include_dirs[:] = [COMPILE_LIBFFI] libraries[:] = [] _filenames = [filename.lower() for filename in os.listdir(COMPILE_LIBFFI)] _filenames = [filename for filename in _filenames if filename.endswith('.c')] if sys.maxsize > 2**32: # 64-bit: unlist win32.c, and add instead win64.obj. If the obj # happens to get outdated at some point in the future, you need to # rebuild it manually from win64.asm. _filenames.remove('win32.c') extra_link_args.append(os.path.join(COMPILE_LIBFFI, 'win64.obj')) sources.extend(os.path.join(COMPILE_LIBFFI, filename) for filename in _filenames) else: use_pkg_config() ask_supports_thread() if __name__ == '__main__': from setuptools import setup, Extension ext_modules = [] if '__pypy__' not in sys.modules: ext_modules.append(Extension( name='_cffi_backend', include_dirs=include_dirs, sources=sources, libraries=libraries, define_macros=define_macros, library_dirs=library_dirs, extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, )) setup( name='cffi', description='Foreign Function Interface for Python calling C code.', long_description=""" CFFI ==== Foreign Function Interface for Python calling C code. Please see the `Documentation `_. Contact ------- `Mailing list `_ """, version='0.8.2', packages=['cffi'], zip_safe=False, url='http://cffi.readthedocs.org', author='Armin Rigo, Maciej Fijalkowski', author_email='python-cffi@googlegroups.com', license='MIT', ext_modules=ext_modules, install_requires=[ 'pycparser', ], classifiers=[ 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', ], ) cffi-0.8.2/LICENSE0000644000076400001440000000241611767605344013741 0ustar arigousers00000000000000 Except when otherwise stated (look for LICENSE files in directories or information at the beginning of each file) all software and documentation is licensed as follows: The MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cffi-0.8.2/doc/0000755000076400001440000000000012306266401013461 5ustar arigousers00000000000000cffi-0.8.2/doc/source/0000755000076400001440000000000012306266401014761 5ustar arigousers00000000000000cffi-0.8.2/doc/source/conf.py0000644000076400001440000001426612306266214016273 0ustar arigousers00000000000000# -*- coding: utf-8 -*- # # CFFI documentation build configuration file, created by # sphinx-quickstart on Thu Jun 14 16:37:47 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'CFFI' copyright = u'2012, Armin Rigo, Maciej Fijalkowski' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.8' # The full version, including alpha/beta/rc tags. release = '0.8.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'CFFIdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'CFFI.tex', u'CFFI Documentation', u'Armin Rigo, Maciej Fijalkowski', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True cffi-0.8.2/doc/source/index.rst0000644000076400001440000020670012306266243016633 0ustar arigousers00000000000000CFFI documentation ================================ Foreign Function Interface for Python calling C code. The aim of this project is to provide a convenient and reliable way of calling C code from Python. The interface is based on `LuaJIT's FFI`_ and follows a few principles: * The goal is to call C code from Python. You should be able to do so without learning a 3rd language: every alternative requires you to learn their own language (Cython_, SWIG_) or API (ctypes_). So we tried to assume that you know Python and C and minimize the extra bits of API that you need to learn. * Keep all the Python-related logic in Python so that you don't need to write much C code (unlike `CPython native C extensions`_). * Work either at the level of the ABI (Application Binary Interface) or the API (Application Programming Interface). Usually, C libraries have a specified C API but often not an ABI (e.g. they may document a "struct" as having at least these fields, but maybe more). (ctypes_ works at the ABI level, whereas Cython_ and `native C extensions`_ work at the API level.) * We try to be complete. For now some C99 constructs are not supported, but all C89 should be, including macros (and including macro "abuses", which you can `manually wrap`_ in saner-looking C functions). * We attempt to support both PyPy and CPython, with a reasonable path for other Python implementations like IronPython and Jython. * Note that this project is **not** about embedding executable C code in Python, unlike `Weave`_. This is about calling existing C libraries from Python. .. _`LuaJIT's FFI`: http://luajit.org/ext_ffi.html .. _`Cython`: http://www.cython.org .. _`SWIG`: http://www.swig.org/ .. _`CPython native C extensions`: http://docs.python.org/extending/extending.html .. _`native C extensions`: http://docs.python.org/extending/extending.html .. _`ctypes`: http://docs.python.org/library/ctypes.html .. _`Weave`: http://www.scipy.org/Weave .. _`manually wrap`: `The verification step`_ Installation and Status ======================================================= Quick installation: * ``pip install cffi`` * or get the source code via the `Python Package Index`__. .. __: http://pypi.python.org/pypi/cffi In more details: This code has been developed on Linux but should work on any POSIX platform as well as on Win32. There are some Windows-specific issues left. It supports CPython 2.6; 2.7; 3.x (tested with 3.2 and 3.3); and PyPy 2.0 beta2 or later. Its speed is comparable to ctypes on CPython (a bit faster but a higher warm-up time). It is already faster on PyPy (1.5x-2x), but not yet *much* faster; stay tuned. Requirements: * CPython 2.6 or 2.7 or 3.x, or PyPy 2.0 beta2 * on CPython you need to build the C extension module, so you need ``python-dev`` and ``libffi-dev`` (for Windows, libffi is included with CFFI). * pycparser >= 2.06: https://github.com/eliben/pycparser * a C compiler is required to use CFFI during development, but not to run correctly-installed programs that use CFFI. * `py.test`_ is needed to run the tests of CFFI. .. _`py.test`: http://pypi.python.org/pypi/pytest Download and Installation: * http://pypi.python.org/packages/source/c/cffi/cffi-0.8.2.tar.gz - Or grab the most current version by following the instructions below. - MD5: ... - SHA: ... * Or get it from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` * ``python setup.py install`` or ``python setup_base.py install`` (should work out of the box on Linux or Windows; see below for `MacOS X`_ or `Windows 64`_.) * or you can directly import and use ``cffi``, but if you don't compile the ``_cffi_backend`` extension module, it will fall back to using internally ``ctypes`` (much slower; we recommend not to use it). * running the tests: ``py.test c/ testing/`` (if you didn't install cffi yet, you may need ``python setup_base.py build`` and ``PYTHONPATH=build/lib.xyz.../``) .. _`Bitbucket page`: https://bitbucket.org/cffi/cffi Demos: * The `demo`_ directory contains a number of small and large demos of using ``cffi``. * The documentation below is sketchy on the details; for now the ultimate reference is given by the tests, notably `testing/test_verify.py`_ and `testing/backend_tests.py`_. .. _`demo`: https://bitbucket.org/cffi/cffi/src/default/demo .. _`testing/backend_tests.py`: https://bitbucket.org/cffi/cffi/src/default/testing/backend_tests.py .. _`testing/test_verify.py`: https://bitbucket.org/cffi/cffi/src/default/testing/test_verify.py Platform-specific instructions ------------------------------ ``libffi`` is notoriously messy to install and use --- to the point that CPython includes its own copy to avoid relying on external packages. CFFI does the same for Windows, but not for other platforms (which should have their own working libffi's). Modern Linuxes work out of the box thanks to ``pkg-config``. Here are some (user-supplied) instructions for other platforms. MacOS X +++++++ **Homebrew** (Thanks David Griffin for this) 1) Install homebrew: http://brew.sh 2) Run the following commands in a terminal :: brew install pkg-config libffi export PKG_CONFIG_PATH=/usr/local/Cellar/libffi/3.0.13/lib/pkgconfig/ # May change with libffi version pip install cffi Aternatively, **on OS/X 10.6** (Thanks Juraj Sukop for this) For building libffi you can use the default install path, but then, in ``setup.py`` you need to change:: include_dirs = [] to:: include_dirs = ['/usr/local/lib/libffi-3.0.11/include'] Then running ``python setup.py build`` complains about "fatal error: error writing to -: Broken pipe", which can be fixed by running:: ARCHFLAGS="-arch i386 -arch x86_64" python setup.py build as described here_. .. _here: http://superuser.com/questions/259278/python-2-6-1-pycrypto-2-3-pypi-package-broken-pipe-during-build Windows 64 ++++++++++ Win32 works and is tested at least each official release. Status: Win64 received very basic testing and we applied a few essential fixes in cffi 0.7. Please report any other issue. Note as usual that this is only about running the 64-bit version of Python on the 64-bit OS. If you're running the 32-bit version (the common case apparently), then you're running Win32 as far as we're concerned. .. _`issue 9`: https://bitbucket.org/cffi/cffi/issue/9 .. _`Python issue 7546`: http://bugs.python.org/issue7546 ======================================================= Examples ======================================================= Simple example (ABI level) -------------------------- .. code-block:: python >>> from cffi import FFI >>> ffi = FFI() >>> ffi.cdef(""" ... int printf(const char *format, ...); // copy-pasted from the man page ... """) >>> C = ffi.dlopen(None) # loads the entire C namespace >>> arg = ffi.new("char[]", "world") # equivalent to C code: char arg[] = "world"; >>> C.printf("hi there, %s!\n", arg) # call printf hi there, world! Note that on Python 3 you need to pass byte strings to ``char *`` arguments. In the above example it would be ``b"world"`` and ``b"hi there, %s!\n"``. In general it is ``somestring.encode(myencoding)``. Real example (API level) ------------------------ .. code-block:: python from cffi import FFI ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[]) # or a list of libraries to link with p = C.getpwuid(0) assert ffi.string(p.pw_name) == 'root' # on Python 3: b'root' Note that the above example works independently of the exact layout of ``struct passwd``. It requires a C compiler the first time you run it, unless the module is distributed and installed according to the `Distributing modules using CFFI`_ intructions below. See also the note about `Cleaning up the __pycache__ directory`_. You will find a number of larger examples using ``verify()`` in the `demo`_ directory. Struct/Array Example -------------------- .. code-block:: python from cffi import FFI ffi = FFI() ffi.cdef(""" typedef struct { unsigned char r, g, b; } pixel_t; """) image = ffi.new("pixel_t[]", 800*600) f = open('data', 'rb') # binary mode -- important f.readinto(ffi.buffer(image)) f.close() image[100].r = 255 image[100].g = 192 image[100].b = 128 f = open('data', 'wb') f.write(ffi.buffer(image)) f.close() This can be used as a more flexible replacement of the struct_ and array_ modules. You could also call ``ffi.new("pixel_t[600][800]")`` and get a two-dimensional array. .. _struct: http://docs.python.org/library/struct.html .. _array: http://docs.python.org/library/array.html What actually happened? ----------------------- The CFFI interface operates on the same level as C - you declare types and functions using the same syntax as you would define them in C. This means that most of the documentation or examples can be copied straight from the man pages. The declarations can contain types, functions and global variables. The cdef in the above examples are just that - they declared "there is a function in the C level with this given signature", or "there is a struct type with this shape". The ``dlopen()`` line loads libraries. C has multiple namespaces - a global one and local ones per library. In this example we load the global one (``None`` as argument to ``dlopen()``) which always contains the standard C library. You get as a result a ```` object that has as attributes all symbols declared in the ``cdef()`` and coming from this library. The ``verify()`` line in the second example is an alternative: instead of doing a ``dlopen``, it generates and compiles a piece of C code. When using ``verify()`` you have the advantage that you can use "``...``" at various places in the ``cdef()``, and the missing information will be completed with the help of the C compiler. It also does checking, to verify that your declarations are correct. If the C compiler gives warnings or errors, they are reported here. Finally, the ``ffi.new()`` lines allocate C objects. They are filled with zeroes initially, unless the optional second argument is used. If specified, this argument gives an "initializer", like you can use with C code to initialize global variables. The actual function calls should be obvious. It's like C. ======================================================= Distributing modules using CFFI ======================================================= If you use CFFI and ``verify()`` in a project that you plan to distribute, other users will install it on machines that may not have a C compiler. Here is how to write a ``setup.py`` script using ``distutils`` in such a way that the extension modules are listed too. This lets normal ``setup.py`` commands compile and package the C extension modules too. Example:: from setuptools import setup --OR-- from distutils.core import setup # you must import at least the module(s) that define the ffi's # that you use in your application import yourmodule setup(... zip_safe=False, # with setuptools only ext_modules=[yourmodule.ffi.verifier.get_extension()]) Warning: with ``setuptools``, you have to say ``zip_safe=False``, otherwise it might or might not work, depending on which verifier engine is used! (I tried to find either workarounds or proper solutions but failed so far.) .. versionadded:: 0.4 If your ``setup.py`` installs a whole package, you can put the extension in it too: :: setup(... zip_safe=False, ext_package='yourpackage', # but see below! ext_modules=[yourmodule.ffi.verifier.get_extension()]) However in this case you must also give the same ``ext_package`` argument to the original call to ``ffi.verify()``:: ffi.verify("...", ext_package='yourpackage') Usually that's all you need, but see the `Reference: verifier`_ section for more details about the ``verifier`` object. Cleaning up the __pycache__ directory ------------------------------------- During development, every time you change the C sources that you pass to ``cdef()`` or ``verify()``, then the latter will create a new module file name, based on two CRC32 hashes computed from these strings. This creates more and more files in the ``__pycache__`` directory. It is recommended that you clean it up from time to time. A nice way to do that is to add, in your test suite, a call to ``cffi.verifier.cleanup_tmpdir()``. Alternatively, you can just completely remove the ``__pycache__`` directory. ======================================================= Reference ======================================================= As a guideline: you have already seen in the above examples all the major pieces except maybe ``ffi.cast()``. The rest of this documentation gives a more complete reference. Declaring types and functions ----------------------------- ``ffi.cdef(source)`` parses the given C source. This should be done first. It registers all the functions, types, and global variables in the C source. The types can be used immediately in ``ffi.new()`` and other functions. Before you can access the functions and global variables, you need to give ``ffi`` another piece of information: where they actually come from (which you do with either ``ffi.dlopen()`` or ``ffi.verify()``). The C source is parsed internally (using ``pycparser``). This code cannot contain ``#include``. It should typically be a self-contained piece of declarations extracted from a man page. The only things it can assume to exist are the standard types: * char, short, int, long, long long (both signed and unsigned) * float, double, long double * intN_t, uintN_t (for N=8,16,32,64), intptr_t, uintptr_t, ptrdiff_t, size_t, ssize_t * 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``. * *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``. * *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 ----------------- ``ffi.dlopen(libpath, [flags])``: this function opens a shared library and returns a module-like library object. You need to use *either* ``ffi.dlopen()`` *or* ``ffi.verify()``, documented below_. You can use the library object to call the functions previously declared by ``ffi.cdef()``, and to read or write global variables. Note that you can use a single ``cdef()`` to declare functions from multiple libraries, as long as you load each of them with ``dlopen()`` and access the functions from the correct one. The ``libpath`` is the file name of the shared library, which can contain a full path or not (in which case it is searched in standard locations, as described in ``man dlopen``), with extensions or not. Alternatively, if ``libpath`` is None, it returns the standard C library (which can be used to access the functions of glibc, on Linux). This gives ABI-level access to the library: you need to have all types declared manually exactly as they were while the library was made. No checking is done. For this reason, we recommend to use ``ffi.verify()`` instead when possible. Note that only functions and global variables are in library objects; types exist in the ``ffi`` instance independently of library objects. This is due to the C model: the types you declare in C are not tied to a particular library, as long as you ``#include`` their headers; but you cannot call functions from a library without linking it in your program, as ``dlopen()`` does dynamically in C. For the optional ``flags`` argument, see ``man dlopen`` (ignored on Windows). It defaults to ``ffi.RTLD_NOW``. This function returns a "library" object that gets closed when it goes out of scope. Make sure you keep the library object around as long as needed. .. _below: The verification step --------------------- ``ffi.verify(source, tmpdir=.., ext_package=.., modulename=.., **kwargs)``: verifies that the current ffi signatures compile on this machine, and return a dynamic library object. The dynamic library can be used to call functions and access global variables declared by a previous ``ffi.cdef()``. You don't need to use ``ffi.dlopen()`` in this case. The returned library is a custom one, compiled just-in-time by the C compiler: it gives you C-level API compatibility (including calling macros, as long as you declared them as functions in ``ffi.cdef()``). This differs from ``ffi.dlopen()``, which requires ABI-level compatibility and must be called several times to open several shared libraries. On top of CPython, the new library is actually a CPython C extension module. The arguments to ``ffi.verify()`` are: * ``source``: C code that is pasted verbatim in the generated code (it is *not* parsed internally). It should contain at least the necessary ``#include``. It can also contain the complete implementation of some functions declared in ``cdef()``; this is useful if you really need to write a piece of C code, e.g. to access some advanced macros (see the example of ``getyx()`` in `demo/_curses.py`_). * ``sources``, ``include_dirs``, ``define_macros``, ``undef_macros``, ``libraries``, ``library_dirs``, ``extra_objects``, ``extra_compile_args``, ``extra_link_args`` (keyword arguments): these are used when compiling the C code, and are passed directly to distutils_. You typically need at least ``libraries=['foo']`` in order to link with ``libfoo.so`` or ``libfoo.so.X.Y``, or ``foo.dll`` on Windows. The ``sources`` is a list of extra .c files compiled and linked together. See the distutils documentation for `more information about the other arguments`__. .. __: http://docs.python.org/distutils/setupscript.html#library-options .. _distutils: http://docs.python.org/distutils/setupscript.html#describing-extension-modules .. _`demo/_curses.py`: https://bitbucket.org/cffi/cffi/src/default/demo/_curses.py On the plus side, this solution gives more "C-like" flexibility: * functions taking or returning integer or float-point arguments can be misdeclared: if e.g. a function is declared by ``cdef()`` as taking a ``int``, but actually takes a ``long``, then the C compiler handles the difference. * other arguments are checked: you get a compilation warning or error if you pass a ``int *`` argument to a function expecting a ``long *``. Moreover, you can use "``...``" in the following places in the ``cdef()`` for leaving details unspecified, which are then completed by the C compiler during ``verify()``: * structure declarations: any ``struct`` that ends with "``...;``" is partial: it may be missing fields and/or have them declared out of order. This declaration will be corrected by the compiler. (But note that you can only access fields that you declared, not others.) Any ``struct`` declaration which doesn't use "``...``" is assumed to be exact, but this is checked: you get a ``VerificationError`` if it is not. * unknown types: the syntax "``typedef ... foo_t;``" declares the type ``foo_t`` as opaque. Useful mainly for when the API takes and returns ``foo_t *`` without you needing to look inside the ``foo_t``. Also works with "``typedef ... *foo_p;``" which declares the pointer type ``foo_p`` without giving a name to the opaque type itself. Note that such an opaque struct has no known size, which prevents some operations from working (mostly like in C). *You cannot use this syntax to declare a specific type, like an integer type! It declares opaque types only.* In some cases you need to say that ``foo_t`` is not opaque, but you just don't know any field in it; then you would use "``typedef struct { ...; } foo_t;``". * array lengths: when used as structure fields or in global variables, arrays can have an unspecified length, as in "``int n[...];``". The length is completed by the C compiler. (Only the outermost array may have an unknown length, in case of array-of-array.) You can also use the syntax "``int n[];``". .. versionchanged:: 0.8 "``int n[];``" asks for an array of unknown length whose length must *not* be completed by the C compiler. See `variable-length array`_ below. If the structure does not contain the syntax ``...`` anywhere, it will be not be considered to have a partial layout to complete by the compiler. * enums: if you don't know the exact order (or values) of the declared constants, then use this syntax: "``enum foo { A, B, C, ... };``" (with a trailing "``...``"). The C compiler will be used to figure out the exact values of the constants. An alternative syntax is "``enum foo { A=..., B, C };``" or even "``enum foo { A=..., B=..., C=... };``". Like with structs, an ``enum`` without "``...``" is assumed to be exact, and this is checked. * integer macros: you can write in the ``cdef`` the line "``#define FOO ...``", with any macro name FOO. Provided the macro is defined to be an integer value, this value will be available via an attribute of the library object returned by ``verify()``. The same effect can be achieved by writing a declaration ``static const int FOO;``. The latter is more general because it supports other types than integer types (note: the syntax is then to write the ``const`` together with the variable name, as in ``static char *const FOO;``). Currently, it is not supported to find automatically which of the various integer or float types you need at which place. In the case of function arguments or return type, when it is a simple integer/float type, it may be misdeclared (if you misdeclare a function ``void f(long)`` as ``void f(int)``, it still works, but you have to call it with arguments that fit an int). But it doesn't work any longer for more complex types (e.g. you cannot misdeclare a ``int *`` argument as ``long *``) or in other locations (e.g. a global array ``int a[5];`` must not be declared ``long a[5];``). CFFI considers all types listed above__ as primitive (so ``long long a[5];`` and ``int64_t a[5]`` are different declarations). .. __: `Declaring types and functions`_ Note the following hack to find explicitly the size of any type, in bytes:: ffi.cdef("const int mysize;") lib = ffi.verify("const int mysize = sizeof(THE_TYPE);") print lib.mysize Note that ``verify()`` is meant to call C libraries that are *not* using ``#include ``. The C functions are called without the GIL, and afterwards we don't check if they set a Python exception, for example. You may work around it, but mixing CFFI with ``Python.h`` is not recommended. .. versionadded:: 0.4 Unions used to crash ``verify()``. Fixed. .. versionadded:: 0.4 The ``tmpdir`` argument to ``verify()`` controls where the C files are created and compiled. By default it is ``directory_containing_the_py_file/__pycache__``, using the directory name of the .py file that contains the actual call to ``ffi.verify()``. (This is a bit of a hack but is generally consistent with the location of the .pyc files for your library. The name ``__pycache__`` itself comes from Python 3.) The ``ext_package`` argument controls in which package the compiled extension module should be looked from. This is only useful after `distributing modules using CFFI`_. The ``tag`` argument gives an extra string inserted in the middle of the extension module's name: ``_cffi__``. Useful to give a bit more context, e.g. when debugging. .. _`warning about modulename`: .. versionadded:: 0.5 The ``modulename`` argument can be used to force a specific module name, overriding the name ``_cffi__``. Use with care, e.g. if you are passing variable information to ``verify()`` but still want the module name to be always the same (e.g. absolute paths to local files). In this case, no hash is computed and if the module name already exists it will be reused without further check. Be sure to have other means of clearing the ``tmpdir`` whenever you change your sources. This function returns a "library" object that gets closed when it goes out of scope. Make sure you keep the library object around as long as needed. Working with pointers, structures and arrays -------------------------------------------- The C code's integers and floating-point values are mapped to Python's regular ``int``, ``long`` and ``float``. Moreover, the C type ``char`` corresponds to single-character strings in Python. (If you want it to map to small integers, use either ``signed char`` or ``unsigned char``.) Similarly, the C type ``wchar_t`` corresponds to single-character unicode strings, if supported by the backend. Note that in some situations (a narrow Python build with an underlying 4-bytes wchar_t type), a single wchar_t character may correspond to a pair of surrogates, which is represented as a unicode string of length 2. If you need to convert such a 2-chars unicode string to an integer, ``ord(x)`` does not work; use instead ``int(ffi.cast('wchar_t', x))``. Pointers, structures and arrays are more complex: they don't have an obvious Python equivalent. Thus, they correspond to objects of type ``cdata``, which are printed for example as ````. ``ffi.new(ctype, [initializer])``: this function builds and returns a new cdata object of the given ``ctype``. The ctype is usually some constant string describing the C type. It must be a pointer or array type. If it is a pointer, e.g. ``"int *"`` or ``struct foo *``, then it allocates the memory for one ``int`` or ``struct foo``. If it is an array, e.g. ``int[10]``, then it allocates the memory for ten ``int``. In both cases the returned cdata is of type ``ctype``. The memory is initially filled with zeros. An initializer can be given too, as described later. Example:: >>> ffi.new("char *") >>> ffi.new("int *") >>> ffi.new("int[10]") .. versionchanged:: 0.2 Note that this changed from CFFI version 0.1: what used to be ``ffi.new("int")`` is now ``ffi.new("int *")``. Unlike C, the returned pointer object has *ownership* on the allocated memory: when this exact object is garbage-collected, then the memory is freed. If, at the level of C, you store a pointer to the memory somewhere else, then make sure you also keep the object alive for as long as needed. (This also applies if you immediately cast the returned pointer to a pointer of a different type: only the original object has ownership, so you must keep it alive. As soon as you forget it, then the casted pointer will point to garbage! In other words, the ownership rules are attached to the *wrapper* cdata objects: they are not, and cannot, be attached to the underlying raw memory.) Example:: global_weakkeydict = weakref.WeakKeyDictionary() s1 = ffi.new("struct foo *") fld1 = ffi.new("struct bar *") fld2 = ffi.new("struct bar *") s1.thefield1 = fld1 s1.thefield2 = fld2 # here the 'fld1' and 'fld2' object must not go away, # otherwise 's1.thefield1/2' will point to garbage! global_weakkeydict[s1] = (fld1, fld2) # now 's1' keeps alive 'fld1' and 'fld2'. When 's1' goes # away, then the weak dictionary entry will be removed. The cdata objects support mostly the same operations as in C: you can read or write from pointers, arrays and structures. Dereferencing a pointer is done usually in C with the syntax ``*p``, which is not valid Python, so instead you have to use the alternative syntax ``p[0]`` (which is also valid C). Additionally, the ``p.x`` and ``p->x`` syntaxes in C both become ``p.x`` in Python. .. versionchanged:: 0.2 You will find ``ffi.NULL`` to use in the same places as the C ``NULL``. Like the latter, it is actually defined to be ``ffi.cast("void *", 0)``. In version 0.1, reading a NULL pointer used to return None; now it returns a regular ````, which you can check for e.g. by comparing it with ``ffi.NULL``. There is no general equivalent to the ``&`` operator in C (because it would not fit nicely in the model, and it does not seem to be needed here). But see ``ffi.addressof()`` below__. __ `Misc methods on ffi`_ Any operation that would in C return a pointer or array or struct type gives you a fresh cdata object. Unlike the "original" one, these fresh cdata objects don't have ownership: they are merely references to existing memory. As an exception to the above rule, dereferencing a pointer that owns a *struct* or *union* object returns a cdata struct or union object that "co-owns" the same memory. Thus in this case there are two objects that can keep the same memory alive. This is done for cases where you really want to have a struct object but don't have any convenient place to keep alive the original pointer object (returned by ``ffi.new()``). Example:: ffi.cdef("void somefunction(int *);") lib = ffi.verify("#include ") x = ffi.new("int *") # allocate one int, and return a pointer to it x[0] = 42 # fill it lib.somefunction(x) # call the C function print x[0] # read the possibly-changed value The equivalent of C casts are provided with ``ffi.cast("type", value)``. They should work in the same cases as they do in C. Additionally, this is the only way to get cdata objects of integer or floating-point type:: >>> x = ffi.cast("int", 42) >>> x >>> int(x) 42 To cast a pointer to an int, cast it to ``intptr_t`` or ``uintptr_t``, which are defined by C to be large enough integer types (example on 32 bits):: >>> int(ffi.cast("intptr_t", pointer_cdata)) # signed -1340782304 >>> int(ffi.cast("uintptr_t", pointer_cdata)) # unsigned 2954184992L The initializer given as the optional second argument to ``ffi.new()`` can be mostly anything that you would use as an initializer for C code, with lists or tuples instead of using the C syntax ``{ .., .., .. }``. Example:: typedef struct { int x, y; } foo_t; foo_t v = { 1, 2 }; // C syntax v = ffi.new("foo_t *", [1, 2]) # CFFI equivalent foo_t v = { .y=1, .x=2 }; // C99 syntax v = ffi.new("foo_t *", {'y': 1, 'x': 2}) # CFFI equivalent Like C, arrays of chars can also be initialized from a string, in which case a terminating null character is appended implicitly:: >>> x = ffi.new("char[]", "hello") >>> x >>> len(x) # the actual size of the array 6 >>> x[5] # the last item in the array '\x00' >>> x[0] = 'H' # change the first item >>> ffi.string(x) # interpret 'x' as a regular null-terminated string 'Hello' Similarly, arrays of wchar_t can be initialized from a unicode string, and calling ``ffi.string()`` on the cdata object returns the current unicode string stored in the wchar_t array (encoding and decoding surrogates as needed if necessary). Note that unlike Python lists or tuples, but like C, you *cannot* index in a C array from the end using negative numbers. More generally, the C array types can have their length unspecified in C types, as long as their length can be derived from the initializer, like in C:: int array[] = { 1, 2, 3, 4 }; // C syntax array = ffi.new("int[]", [1, 2, 3, 4]) # CFFI equivalent As an extension, the initializer can also be just a number, giving the length (in case you just want zero-initialization):: int array[1000]; // C syntax array = ffi.new("int[1000]") # CFFI 1st equivalent array = ffi.new("int[]", 1000) # CFFI 2nd equivalent This is useful if the length is not actually a constant, to avoid things like ``ffi.new("int[%d]" % x)``. Indeed, this is not recommended: ``ffi`` normally caches the string ``"int[]"`` to not need to re-parse it all the time. .. versionadded:: 0.9 The ``ffi.cdef()`` call takes an optional argument ``packed``: if True, then all structs declared within this cdef are "packed". This has a meaning similar to ``__attribute__((packed))`` in GCC. It specifies that all structure fields should have an alignment of one byte. (Note that the packed attribute has no effect on bit fields so far, which mean that they may be packed differently than on GCC.) Python 3 support ---------------- Python 3 is supported, but the main point to note is that the ``char`` C type corresponds to the ``bytes`` Python type, and not ``str``. It is your responsibility to encode/decode all Python strings to bytes when passing them to or receiving them from CFFI. This only concerns the ``char`` type and derivative types; other parts of the API that accept strings in Python 2 continue to accept strings in Python 3. An example of calling a main-like thing --------------------------------------- Imagine we have something like this: .. code-block:: python from cffi import FFI ffi = FFI() ffi.cdef(""" int main_like(int argv, char *argv[]); """) lib = ffi.dlopen("some_library.so") Now, everything is simple, except, how do we create the ``char**`` argument here? The first idea: .. code-block:: python lib.main_like(2, ["arg0", "arg1"]) does not work, because the initializer receives two Python ``str`` objects where it was expecting ```` objects. You need to use ``ffi.new()`` explicitly to make these objects: .. code-block:: python lib.main_like(2, [ffi.new("char[]", "arg0"), ffi.new("char[]", "arg1")]) Note that the two ```` objects are kept alive for the duration of the call: they are only freed when the list itself is freed, and the list is only freed when the call returns. If you want instead to build an "argv" variable that you want to reuse, then more care is needed: .. code-block:: python # DOES NOT WORK! argv = ffi.new("char *[]", [ffi.new("char[]", "arg0"), ffi.new("char[]", "arg1")]) In the above example, the inner "arg0" string is deallocated as soon as "argv" is built. You have to make sure that you keep a reference to the inner "char[]" objects, either directly or by keeping the list alive like this: .. code-block:: python argv_keepalive = [ffi.new("char[]", "arg0"), ffi.new("char[]", "arg1")] argv = ffi.new("char *[]", argv_keepalive) .. versionchanged:: 0.3 In older versions, passing a list as the ``char *[]`` argument did not work; you needed to make an ``argv_keepalive`` and an ``argv`` in all cases. Function calls -------------- When calling C functions, passing arguments follows mostly the same rules as assigning to structure fields, and the return value follows the same rules as reading a structure field. For example:: ffi.cdef(""" int foo(short a, int b); """) lib = ffi.verify("#include ") n = lib.foo(2, 3) # returns a normal integer lib.foo(40000, 3) # raises OverflowError As an extension, you can pass to ``char *`` arguments a normal Python string (but don't pass a normal Python string to functions that take a ``char *`` argument and may mutate it!):: ffi.cdef(""" size_t strlen(const char *); """) C = ffi.dlopen(None) assert C.strlen("hello") == 5 You can also pass unicode strings as ``wchar_t *`` arguments. Note that in general, there is no difference between C argument declarations that use ``type *`` or ``type[]``. For example, ``int *`` is fully equivalent to ``int[]`` or ``int[5]``. So you can pass an ``int *`` as a list of integers:: ffi.cdef(""" void do_something_with_array(int *array); """) lib.do_something_with_array([1, 2, 3, 4, 5]) CFFI supports passing and returning structs to functions and callbacks. Example (sketch):: >>> ffi.cdef(""" ... struct foo_s { int a, b; }; ... struct foo_s function_returning_a_struct(void); ... """) >>> lib = ffi.verify("#include ") >>> lib.function_returning_a_struct() There are a few (obscure) limitations to the argument types and return type. You cannot pass directly as argument a union (but a **pointer** to a union is fine), nor a struct which uses bitfields (but a **pointer** to such a struct is fine). If you pass a struct (not a **pointer** to a struct), the struct type cannot have been declared with "``...;``" and completed with ``verify()``; you need to declare it completely in ``cdef()``. You can work around these limitations by writing a C function with a simpler signature in the code passed to ``ffi.verify()``, which calls the real C function. Aside from these limitations, functions and callbacks can return structs. CPython only: for performance, ``ffi.verify()`` returns functions as objects of type ````. They are not ````, so you cannot e.g. pass them to some other C function expecting a function pointer argument. Only ``ffi.typeof()`` works on them. If you really need a pointer to the function, use the following workaround:: ffi.cdef(""" int (*foo)(int a, int b); """) i.e. declare them as pointer-to-function in the cdef (even if they are regular functions in the C code). Variadic function calls ----------------------- Variadic functions in C (which end with "``...``" as their last argument) can be declared and called normally, with the exception that all the arguments passed in the variable part *must* be cdata objects. This is because it would not be possible to guess, if you wrote this:: C.printf("hello, %d\n", 42) that you really meant the 42 to be passed as a C ``int``, and not a ``long`` or ``long long``. The same issue occurs with ``float`` versus ``double``. So you have to force cdata objects of the C type you want, if necessary with ``ffi.cast()``:: C.printf("hello, %d\n", ffi.cast("int", 42)) C.printf("hello, %ld\n", ffi.cast("long", 42)) C.printf("hello, %f\n", ffi.cast("double", 42)) C.printf("hello, %s\n", ffi.new("char[]", "world")) Callbacks --------- C functions can also be viewed as ``cdata`` objects, and so can be passed as callbacks. To make new C callback objects that will invoke a Python function, you need to use:: >>> def myfunc(x, y): ... return x + y ... >>> ffi.callback("int(int, int)", myfunc) > .. versionadded:: 0.4 Or equivalently as a decorator: >>> @ffi.callback("int(int, int)") ... def myfunc(x, y): ... return x + y Note that you can also use a C *function pointer* type like ``"int(*)(int, int)"`` (as opposed to a C *function* type like ``"int(int, int)"``). It is equivalent here. Warning: like ffi.new(), ffi.callback() returns a cdata that has ownership of its C data. (In this case, the necessary C data contains the libffi data structures to do a callback.) This means that the callback can only be invoked as long as this cdata object is alive. If you store the function pointer into C code, then make sure you also keep this object alive for as long as the callback may be invoked. (If you want the callback to remain valid forever, store the object in a fresh global variable somewhere.) Note that callbacks of a variadic function type are not supported. A workaround is to add custom C code. In the following example, a callback gets a first argument that counts how many extra ``int`` arguments are passed:: ffi.cdef(""" int (*python_callback)(int how_many, int *values); void *const c_callback; /* pass this ptr to C routines */ """) lib = ffi.verify(""" #include #include static int (*python_callback)(int how_many, int *values); static int c_callback(int how_many, ...) { va_list ap; /* collect the "..." arguments into the values[] array */ int i, *values = alloca(how_many * sizeof(int)); va_start(ap, how_many); for (i=0; i>> ffi.callback("int(int, int)", myfunc, error=42) In all cases the exception is printed to stderr, so this should be used only as a last-resort solution. Misc methods on ffi ------------------- ``ffi.include(other_ffi)``: includes the typedefs, structs, unions and enum types defined in another FFI instance. Usage is similar to a ``#include`` in C, where a part of the program might include types defined in another part for its own usage. Note that the include() method has no effect on functions, constants and global variables, which must anyway be accessed directly from the ``lib`` object returned by the original FFI instance. *Note that you should only use one ffi object per library; the intended usage of ffi.include() is if you want to interface with several inter-dependent libraries.* For only one library, make one ``ffi`` object. (If the source becomes too large, split it up e.g. by collecting the cdef/verify strings from multiple Python modules, as long as you call ``ffi.verify()`` only once.) *New in version 0.5.* .. "versionadded:: 0.5" --- inlined in the previous paragraph ``ffi.errno``: the value of ``errno`` received from the most recent C call in this thread, and passed to the following C call, is available via reads and writes of the property ``ffi.errno``. ``ffi.getwinerror(code=-1)``: on Windows, in addition to ``errno`` we also save and restore the ``GetLastError()`` value across function calls. This function returns this error code as a tuple ``(code, message)``, adding a readable message like Python does when raising WindowsError. If the argument ``code`` is given, format that code into a message instead of using ``GetLastError()``. *New in version 0.8.* (Note that it is also possible to declare and call the ``GetLastError()`` function as usual.) .. "versionadded:: 0.8" --- inlined in the previous paragraph ``ffi.string(cdata, [maxlen])``: return a Python string (or unicode string) from the 'cdata'. *New in version 0.3.* .. "versionadded:: 0.3" --- inlined in the previous paragraph - If 'cdata' is a pointer or array of characters or bytes, returns the null-terminated string. The returned string extends until the first null character, or at most 'maxlen' characters. If 'cdata' is an array then 'maxlen' defaults to its length. See ``ffi.buffer()`` below for a way to continue past the first null character. *Python 3:* this returns a ``bytes``, not a ``str``. - If 'cdata' is a pointer or array of wchar_t, returns a unicode string following the same rules. - If 'cdata' is a single character or byte or a wchar_t, returns it as a byte string or unicode string. (Note that in some situation a single wchar_t may require a Python unicode string of length 2.) - If 'cdata' is an enum, returns the value of the enumerator as a string. If the value is out of range, it is simply returned as the stringified integer. ``ffi.buffer(cdata, [size])``: return a buffer object that references the raw C data pointed to by the given 'cdata', of 'size' bytes. The 'cdata' must be a pointer or an array. If unspecified, the size of the buffer is either the size of what ``cdata`` points to, or the whole size of the array. Getting a buffer is useful because you can read from it without an extra copy, or write into it to change the original value; you can use for example ``file.write()`` and ``file.readinto()`` with such a buffer (for files opened in binary mode). (Remember that like in C, you use ``array + index`` to get the pointer to the index'th item of an array.) .. versionchanged:: 0.4 The returned object is not a built-in buffer nor memoryview object, because these objects' API changes too much across Python versions. Instead it has the following Python API (a subset of ``buffer``): - ``buf[:]`` or ``bytes(buf)``: fetch a copy as a regular byte string (or ``buf[start:end]`` for a part) - ``buf[:] = newstr``: change the original content (or ``buf[start:end] = newstr``) - ``len(buf), buf[index], buf[index] = newchar``: access as a sequence of characters. .. versionchanged:: 0.5 The buffer object returned by ``ffi.buffer(cdata)`` keeps alive the ``cdata`` object: if it was originally an owning cdata, then its owned memory will not be freed as long as the buffer is alive. Moreover buffer objects now support weakrefs to them. .. versionchanged:: 0.9 Before version 0.9, ``bytes(buf)`` was supported in Python 3 to get the content of the buffer, but on Python 2 it would return the repr ``<_cffi_backend.buffer object>``. This has been fixed. But you should avoid using ``str(buf)``: it now gives inconsistent results between Python 2 and Python 3 (this is similar to how ``str()`` gives inconsistent results on regular byte strings). ``ffi.typeof("C type" or cdata object)``: return an object of type ```` corresponding to the parsed string, or to the C type of the cdata instance. Usually you don't need to call this function or to explicitly manipulate ```` objects in your code: any place that accepts a C type can receive either a string or a pre-parsed ``ctype`` object (and because of caching of the string, there is no real performance difference). It can still be useful in writing typechecks, e.g.:: def myfunction(ptr): assert ffi.typeof(ptr) is ffi.typeof("foo_t*") ... .. versionadded:: 0.4 ``ffi.CData, ffi.CType``: the Python type of the objects referred to as ```` and ```` in the rest of this document. Note that some cdata objects may be actually of a subclass of ``ffi.CData``, and similarly with ctype, so you should check with ``if isinstance(x, ffi.CData)``. Also, ```` objects have a number of attributes for introspection: ``kind`` and ``cname`` are always present, and depending on the kind they may also have ``item``, ``length``, ``fields``, ``args``, ``result``, ``ellipsis``, ``abi``, ``elements`` and ``relements``. ``ffi.sizeof("C type" or cdata object)``: return the size of the argument in bytes. The argument can be either a C type, or a cdata object, like in the equivalent ``sizeof`` operator in C. ``ffi.alignof("C type")``: return the alignment of the C type. Corresponds to the ``__alignof__`` operator in GCC. ``ffi.offsetof("C struct type", "fieldname")``: return the offset within the struct of the given field. Corresponds to ``offsetof()`` in C. ``ffi.getctype("C type" or , extra="")``: return the string representation of the given C type. If non-empty, the "extra" string is appended (or inserted at the right place in more complicated cases); it can be the name of a variable to declare, or an extra part of the type like ``"*"`` or ``"[5]"``. For example ``ffi.getctype(ffi.typeof(x), "*")`` returns the string representation of the C type "pointer to the same type than x"; and ``ffi.getctype("char[80]", "a") == "char a[80]"``. ``ffi.gc(cdata, destructor)``: return a new cdata object that points to the same data. Later, when this new cdata object is garbage-collected, ``destructor(old_cdata_object)`` will be called. Example of usage: ``ptr = ffi.gc(lib.malloc(42), lib.free)``. Note that like objects returned by ``ffi.new()``, the returned pointer objects have *ownership*, which means the destructor is called as soon as *this* exact returned object is garbage-collected. *New in version 0.3* (together with the fact that any cdata object can be weakly referenced). Note that this should be avoided for large memory allocations or for limited resources. This is particularly true on PyPy: its GC does not know how much memory or how many resources the returned ``ptr`` holds. It will only run its GC when enough memory it knows about has been allocated (and thus run the destructor possibly later than you would expect). Moreover, the destructor is called in whatever thread PyPy is at that moment, which might be a problem for some C libraries. In these cases, consider writing a wrapper class with custom ``__enter__()`` and ``__exit__()`` methods that allocate and free the C data at known points in time, and using it in a ``with`` statement. .. "versionadded:: 0.3" --- inlined in the previous paragraph ``ffi.new_handle(python_object)``: return a non-NULL cdata of type ``void *`` that contains an opaque reference to ``python_object``. You can pass it around to C functions or store it into C structures. Later, you can use ``ffi.from_handle(p)`` to retrive the original ``python_object`` from a value with the same ``void *`` pointer. *Calling ffi.from_handle(p) is invalid and will likely crash if the cdata object returned by new_handle() is not kept alive!* *New in version 0.7.* Note that ``from_handle()`` conceptually works like this: it searches in the list of cdata objects made by ``new_handle()`` the one which has got the same ``void *`` value; and then it fetches in that cdata object the corresponding Python object. The cdata object keeps the Python object alive, similar to how ``ffi.new()`` returns a cdata object that keeps a piece of memory alive. If the cdata object *itself* is not alive any more, then the association ``void * -> python_object`` is dead and ``from_handle()`` will crash. .. "versionadded:: 0.7" --- inlined in the previous paragraph ``ffi.addressof(cdata, field=None)``: from a cdata whose type is ``struct foo_s``, return its "address", as a cdata whose type is ``struct foo_s *``. Also works on unions, but not on any other type. (It would be difficult because only structs and unions are internally stored as an indirect pointer to the data. If you need a C int whose address can be taken, use ``ffi.new("int[1]")`` in the first place; similarly, if it's a C pointer, use ``ffi.new("foo_t *[1]")``.) If ``field`` is given, returns the address of that field in the structure. The returned pointer is only valid as long as the original ``cdata`` object is; be sure to keep it alive if it was obtained directly from ``ffi.new()``. *New in version 0.4.* .. "versionadded:: 0.4" --- inlined in the previous paragraph Unimplemented features ---------------------- All of the ANSI C declarations should be supported, and some of C99. Known missing features that are GCC or MSVC extensions: * Any ``__attribute__`` or ``#pragma pack(n)`` * Additional types: complex numbers, special-size floating and fixed point types, vector types, and so on. You might be able to access an array of complex numbers by declaring it as an array of ``struct my_complex { double real, imag; }``, but in general you should declare them as ``struct { ...; }`` and cannot access them directly. This means that you cannot call any function which has an argument or return value of this type (this would need added support in libffi). You need to write wrapper functions in C, e.g. ``void foo_wrapper(struct my_complex c) { foo(c.real + c.imag*1j); }``, and call ``foo_wrapper`` rather than ``foo`` directly. * Thread-local variables (access them via getter/setter functions) .. versionadded:: 0.4 Now supported: the common GCC extension of anonymous nested structs/unions inside structs/unions. .. versionadded:: 0.6 Enum types follow the GCC rules: they are defined as the first of ``unsigned int``, ``int``, ``unsigned long`` or ``long`` that fits all numeric values. Note that the first choice is unsigned. In CFFI 0.5 and before, enums were always ``int``. *Unimplemented: if the enum has very large values in C not declared in CFFI, the enum will incorrectly be considered as an int even though it is really a long! Work around this by naming the largest value. A similar but less important problem involves negative values.* .. _`variable-length array`: .. versionadded:: 0.8 Now supported: variable-length structures, i.e. whose last field is a variable-length array. Note that since version 0.8, declarations like ``int field[];`` in structures are interpreted as variable-length structures. When used for structures that are not, in fact, variable-length, it works too; in this case, the difference with using ``int field[...];`` is that, as CFFI believes it cannot ask the C compiler for the length of the array, you get reduced safety checks: for example, you risk overwriting the following fields by passing too many array items in the constructor. Debugging dlopen'ed C libraries ------------------------------- A few C libraries are actually hard to use correctly in a ``dlopen()`` setting. This is because most C libraries are intented for, and tested with, a situation where they are *linked* with another program, using either static linking or dynamic linking --- but from a program written in C, at start-up, using the linker's capabilities instead of ``dlopen()``. This can occasionally create issues. You would have the same issues in another setting than CFFI, like with ``ctypes`` or even plain C code that calls ``dlopen()``. This section contains a few generally useful environment variables (on Linux) that can help when debugging these issues. **export LD_TRACE_LOADED_OBJECTS=all** provides a lot of information, sometimes too much depending on the setting. Output verbose debugging information about the dynamic linker. If set to ``all`` prints all debugging information it has, if set to ``help`` prints a help message about which categories can be specified in this environment variable **export LD_VERBOSE=1** (glibc since 2.1) If set to a nonempty string, output symbol versioning information about the program if querying information about the program (i.e., either ``LD_TRACE_LOADED_OBJECTS`` has been set, or ``--list`` or ``--verify`` options have been given to the dynamic linker). **export LD_WARN=1** (ELF only)(glibc since 2.1.3) If set to a nonempty string, warn about unresolved symbols. Reference: conversions ---------------------- This section documents all the conversions that are allowed when *writing into* a C data structure (or passing arguments to a function call), and *reading from* a C data structure (or getting the result of a function call). The last column gives the type-specific operations allowed. +---------------+------------------------+------------------+----------------+ | C type | writing into | reading from |other operations| +===============+========================+==================+================+ | integers | an integer or anything | a Python int or | int() | | and enums | on which int() works | long, depending | | | `(*****)` | (but not a float!). | on the type | | | | Must be within range. | | | +---------------+------------------------+------------------+----------------+ | ``char`` | a string of length 1 | a string of | int() | | | or another | length 1 | | +---------------+------------------------+------------------+----------------+ | ``wchar_t`` | a unicode of length 1 | a unicode of | | | | (or maybe 2 if | length 1 | int() | | | surrogates) or | (or maybe 2 if | | | | another | surrogates) | | +---------------+------------------------+------------------+----------------+ | ``float``, | a float or anything on | a Python float | float(), int() | | ``double`` | which float() works | | | +---------------+------------------------+------------------+----------------+ |``long double``| another with | a , to | float(), int() | | | a ``long double``, or | avoid loosing | | | | anything on which | precision `(***)`| | | | float() works | | | +---------------+------------------------+------------------+----------------+ | pointers | another with | a |``[]`` `(****)`,| | | a compatible type (i.e.| |``+``, ``-``, | | | same type or ``char*`` | |bool() | | | or ``void*``, or as an | | | | | array instead) `(*)` | | | +---------------+------------------------+ | | | ``void *``, | another with | | | | ``char *`` | any pointer or array | | | | | type | | | +---------------+------------------------+ +----------------+ | pointers to | same as pointers | | ``[]``, ``+``, | | structure or | | | ``-``, bool(), | | union | | | and read/write | | | | | struct fields | +---------------+------------------------+ +----------------+ | function | same as pointers | | bool(), | | pointers | | | call `(**)` | +---------------+------------------------+------------------+----------------+ | arrays | a list or tuple of | a |len(), iter(), | | | items | |``[]`` `(****)`,| | | | |``+``, ``-`` | +---------------+------------------------+ +----------------+ | ``char[]`` | same as arrays, or a | | len(), iter(), | | | Python string | | ``[]``, ``+``, | | | | | ``-`` | +---------------+------------------------+ +----------------+ | ``wchar_t[]`` | same as arrays, or a | | len(), iter(), | | | Python unicode | | ``[]``, | | | | | ``+``, ``-`` | | | | | | +---------------+------------------------+------------------+----------------+ | structure | a list or tuple or | a | read/write | | | dict of the field | | fields | | | values, or a same-type | | | | | | | | +---------------+------------------------+ +----------------+ | union | same as struct, but | | read/write | | | with at most one field | | fields | +---------------+------------------------+------------------+----------------+ .. versionchanged:: 0.3 `(*)` Note that when calling a function, as per C, a ``item *`` argument is identical to a ``item[]`` argument. So you can pass an argument that is accepted by either C type, like for example passing a Python string to a ``char *`` argument (because it works for ``char[]`` arguments) or a list of integers to a ``int *`` argument (it works for ``int[]`` arguments). Note that even if you want to pass a single ``item``, you need to specify it in a list of length 1; for example, a ``struct foo *`` argument might be passed as ``[[field1, field2...]]``. As an optimization, the CPython version of CFFI assumes that a function with a ``char *`` argument to which you pass a Python string will not actually modify the array of characters passed in, and so passes directly a pointer inside the Python string object. .. versionchanged:: 0.3 `(**)` C function calls are now done with the GIL released. .. versionadded:: 0.3 `(***)` ``long double`` support. Such a number is passed around in a cdata object to avoid loosing precision, because a normal Python floating-point number only contains enough precision for a ``double``. To convert it to a regular float, call ``float()``. If you want to operate on such numbers without any precision loss, you need to define and use a family of C functions like ``long double add(long double a, long double b);``. .. versionadded:: 0.6 `(****)` Supports simple slices as well: ``x[start:stop]`` gives another cdata object that is a "view" of all items from ``start`` to ``stop``. It is a cdata of type "array" (so e.g. passing it as an argument to a C function would just convert it to a pointer to the ``start`` item). This makes cdata's of type "array" behave more like a Python list, but ``start`` and ``stop`` are not optional and a ``step`` is not supported. As with indexing, negative bounds mean really negative indices, like in C. As for slice assignment, it accepts any iterable, including a list of items or another array-like cdata object, but the length must match. (Note that this behavior differs from initialization: e.g. if you pass a string when assigning to a slice of a ``char`` array, it must be of the correct length; no implicit null character is added.) .. versionchanged:: 0.6 `(*****)` Enums are now handled like ints (unsigned or signed, int or long, like GCC; note that the first choice is unsigned). In previous versions, you would get the enum's value as a string. Now we follow the C convention and treat them as really equivalent to integers. To compare their value symbolically, use code like ``if x.field == lib.FOO``. If you really want to get their value as a string, use ``ffi.string(ffi.cast("the_enum_type", x.field))``. Reference: verifier ------------------- For advanced use cases, the ``Verifier`` class from ``cffi.verifier`` can be instantiated directly. It is normally instantiated for you by ``ffi.verify()``, and the instance is attached as ``ffi.verifier``. - ``Verifier(ffi, preamble, tmpdir=.., ext_package='', modulename=None, tag='', **kwds)``: instantiate the class with an FFI object and a preamble, which is C text that will be pasted into the generated C source. The value of ``tmpdir`` defaults to the directory ``directory_of_the_caller/__pycache__``. The value of ``ext_package`` is used when looking up an already-compiled, already- installed version of the extension module. The module name is ``_cffi__``, unless overridden with ``modulename`` (see the `warning about modulename`_ above). The other keyword arguments are passed directly to `distutils when building the Extension object.`__ .. __: http://docs.python.org/distutils/setupscript.html#describing-extension-module ``Verifier`` objects have the following public attributes and methods: - ``sourcefilename``: name of a C file. Defaults to ``tmpdir/_cffi_CRCHASH.c``, with the ``CRCHASH`` part computed from the strings you passed to cdef() and verify() as well as the version numbers of Python and CFFI. Can be changed before calling ``write_source()`` if you want to write the source somewhere else. - ``modulefilename``: name of the ``.so`` file (or ``.pyd`` on Windows). Defaults to ``tmpdir/_cffi_CRCHASH.so``. Can be changed before calling ``compile_module()``. - ``get_module_name()``: extract the module name from ``modulefilename``. - ``write_source(file=None)``: produces the C source of the extension module. If ``file`` is specified, write it in that file (or file-like) object rather than to ``sourcefilename``. - ``compile_module()``: writes the C source code (if not done already) and compiles it. This produces a dynamic link library whose file is given by ``modulefilename``. - ``load_library()``: loads the C module (if necessary, making it first; it looks for the existing module based on the checksum of the strings passed to ``ffi.cdef()`` and ``preamble``, either in the directory ``tmpdir`` or in the directory of the package ``ext_package``). Returns an instance of a FFILibrary class that behaves like the objects returned by ffi.dlopen(), but that delegates all operations to the C module. This is what is returned by ``ffi.verify()``. - ``get_extension()``: returns a distutils-compatible ``Extension`` instance. The following are global functions in the ``cffi.verifier`` module: - ``set_tmpdir(dirname)``: sets the temporary directory to use instead of ``directory_containing_the_py_file/__pycache__``. This is a global, so avoid it in production code. - ``cleanup_tmpdir(tmpdir=...)``: cleans up the temporary directory by removing all files in it called ``_cffi_*.{c,so}`` as well as all files in the ``build`` subdirectory. By default it will clear ``directory_containing_the_py_file/__pycache__``. This is the .py file containing the actual call to ``cleanup_tmpdir()``. ================= Comments and bugs ================= The best way to contact us is on the IRC ``#pypy`` channel of ``irc.freenode.net``. Feel free to discuss matters either there or in the `mailing list`_. Please report to the `issue tracker`_ any bugs. As a general rule, when there is a design issue to resolve, we pick the solution that is the "most C-like". We hope that this module has got everything you need to access C code and nothing more. --- the authors, Armin Rigo and Maciej Fijalkowski .. _`issue tracker`: https://bitbucket.org/cffi/cffi/issues .. _`mailing list`: https://groups.google.com/forum/#!forum/python-cffi Indices and tables ================== * :ref:`genindex` * :ref:`search` cffi-0.8.2/doc/make.bat0000644000076400001440000000577711766377660015131 0ustar arigousers00000000000000@ECHO OFF REM Command file for Sphinx documentation set SPHINXBUILD=sphinx-build set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\CFFI.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\CFFI.ghc goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end cffi-0.8.2/doc/Makefile0000644000076400001440000000606611766377660015154 0ustar arigousers00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CFFI.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CFFI.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." cffi-0.8.2/doc/design.rst0000644000076400001440000000533311764325060015474 0ustar arigousers00000000000000================ Design decisions ================ * Generally follow LuaJIT's ffi: http://luajit.org/ext_ffi.html * Be explicit: almost no automatic conversions. Here is the set of automatic conversions: the various C integer types are automatically wrapped and unwrapped to regular applevel integers. The type ``char`` might correspond to single-character strings instead; for integer correspondance you would use ``signed char`` or ``unsigned char``. We might also decide that ``const char *`` automatically maps to strings; for cases where you don't want that, use ``char *``. * Integers are not automatically converted when passed as vararg arguments. You have to use explicitly ``ffi.new("int", 42)`` or ``ffi.new("long", 42)`` to resolve the ambiguity. Floats would be fine (varargs in C can only accept ``double``, not ``float``), but there is again ambiguity between characters and strings. Even with floats the result is a bit strange because passing a float works but passing an integer not. I would fix this once and for all by saying that varargs must *always* be a cdata (from ``ffi.new()``). The possibly acceptable exception would be None (for ``NULL``). * The internal class ``blob`` is used for raw-malloced data. You only get a class that has internally a ``blob`` instance (or maybe is a subclass of ``blob``) by calling ``ffi.new(struct-or-array-type)``. The other cases, namely the cases where the type is a pointer or a primitive, don't need a blob because it's not possible to take their raw address. * It would be possible to add a debug mode: when we cast ``struct foo`` to ``struct foo *`` or store it in some other struct, then we would additionally record a weakref to the original ``struct foo`` blob. If later we try to access the ``struct foo *`` but the weakref shows that the blob was freed, we complain. This is a difference with ctypes, which in these cases would store a strong reference and keep the blob alive. "Explicit is better than implicit", so we ask the user to keep a reference to the original blob alive as long as it may be used (instead of doing the right things in 90% of the cases but still crashing in the remaining 10%). * LuaJIT uses ``struct foo &`` for a number of things, like for ``p[0]`` if ``p`` is a ``struct foo *``. I suppose it's not a bad idea at least to have internally such types, even if you can't specify them through pycparser. Basically ``struct foo &`` is a type that doesn't own a blob, whereas ``struct foo`` is the type that does. * LuaJIT uses ``int[?]`` which pycparser doesn't accept. I propose instead to use ``int[]`` for the same purpose (its use is anyway quite close to the C standard's use of ``int[]``). cffi-0.8.2/c/0000755000076400001440000000000012306266401013136 5ustar arigousers00000000000000cffi-0.8.2/c/check__thread.c0000644000076400001440000000005312242413161016037 0ustar arigousers00000000000000__thread int some_threadlocal_variable_42; cffi-0.8.2/c/misc_win32.h0000644000076400001440000001303712240432017015263 0ustar arigousers00000000000000 /************************************************************/ /* errno and GetLastError support */ struct cffi_errno_s { int saved_errno; int saved_lasterror; }; static DWORD cffi_tls_index; static void init_errno(void) { cffi_tls_index = TlsAlloc(); if (cffi_tls_index == TLS_OUT_OF_INDEXES) PyErr_SetString(PyExc_WindowsError, "TlsAlloc() failed"); } static struct cffi_errno_s *_geterrno_object(void) { LPVOID p = TlsGetValue(cffi_tls_index); if (p == NULL) { /* XXX this malloc() leaks */ p = malloc(sizeof(struct cffi_errno_s)); if (p == NULL) return NULL; memset(p, 0, sizeof(struct cffi_errno_s)); TlsSetValue(cffi_tls_index, p); } return (struct cffi_errno_s *)p; } static void save_errno(void) { int current_err = errno; int current_lasterr = GetLastError(); struct cffi_errno_s *p; p = _geterrno_object(); if (p != NULL) { p->saved_errno = current_err; p->saved_lasterror = current_lasterr; } /* else: cannot report the error */ } static void save_errno_only(void) { int current_err = errno; struct cffi_errno_s *p; p = _geterrno_object(); if (p != NULL) { p->saved_errno = current_err; } /* else: cannot report the error */ } static void restore_errno(void) { struct cffi_errno_s *p; p = _geterrno_object(); if (p != NULL) { SetLastError(p->saved_lasterror); errno = p->saved_errno; } /* else: cannot report the error */ } static void restore_errno_only(void) { struct cffi_errno_s *p; p = _geterrno_object(); if (p != NULL) { errno = p->saved_errno; } /* else: cannot report the error */ } #if PY_MAJOR_VERSION >= 3 static PyObject *b_getwinerror(PyObject *self, PyObject *args) { int err = -1; int len; WCHAR *s_buf = NULL; /* Free via LocalFree */ PyObject *v, *message; if (!PyArg_ParseTuple(args, "|i", &err)) return NULL; if (err == -1) { struct cffi_errno_s *p; p = _geterrno_object(); if (p == NULL) return PyErr_NoMemory(); err = p->saved_lasterror; } len = FormatMessageW( /* Error API error */ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, /* no message source */ err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPWSTR) &s_buf, 0, /* size not used */ NULL); /* no args */ if (len==0) { /* Only seen this in out of mem situations */ message = PyUnicode_FromFormat("Windows Error 0x%X", err); } else { /* remove trailing cr/lf and dots */ while (len > 0 && (s_buf[len-1] <= L' ' || s_buf[len-1] == L'.')) s_buf[--len] = L'\0'; message = PyUnicode_FromWideChar(s_buf, len); } if (message != NULL) v = Py_BuildValue("(iO)", err, message); else v = NULL; LocalFree(s_buf); return v; } #else static PyObject *b_getwinerror(PyObject *self, PyObject *args) { int err = -1; int len; char *s; char *s_buf = NULL; /* Free via LocalFree */ char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */ PyObject *v; if (!PyArg_ParseTuple(args, "|i", &err)) return NULL; if (err == -1) { struct cffi_errno_s *p; p = _geterrno_object(); if (p == NULL) return PyErr_NoMemory(); err = p->saved_lasterror; } len = FormatMessage( /* Error API error */ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, /* no message source */ err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPTSTR) &s_buf, 0, /* size not used */ NULL); /* no args */ if (len==0) { /* Only seen this in out of mem situations */ sprintf(s_small_buf, "Windows Error 0x%X", err); s = s_small_buf; s_buf = NULL; } else { s = s_buf; /* remove trailing cr/lf and dots */ while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.')) s[--len] = '\0'; } v = Py_BuildValue("(is)", err, s); LocalFree(s_buf); return v; } #endif /************************************************************/ /* Emulate dlopen()&co. from the Windows API */ #define RTLD_LAZY 0 #define RTLD_NOW 0 #define RTLD_GLOBAL 0 #define RTLD_LOCAL 0 static void *dlopen(const char *filename, int flag) { return (void *)LoadLibrary(filename); } static void *dlsym(void *handle, const char *symbol) { return GetProcAddress((HMODULE)handle, symbol); } static void dlclose(void *handle) { FreeLibrary((HMODULE)handle); } static const char *dlerror(void) { static char buf[32]; DWORD dw = GetLastError(); if (dw == 0) return NULL; sprintf(buf, "error 0x%x", (unsigned int)dw); return buf; } /************************************************************/ /* types */ typedef __int8 int8_t; typedef __int16 int16_t; typedef __int32 int32_t; typedef __int64 int64_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; typedef unsigned char _Bool; /************************************************************/ /* obscure */ #define ffi_prep_closure(a,b,c,d) ffi_prep_closure_loc(a,b,c,d,a) cffi-0.8.2/c/file_emulator.h0000644000076400001440000000400412176750421016141 0ustar arigousers00000000000000 /* Emulation of PyFile_Check() and PyFile_AsFile() for Python 3. */ static PyObject *PyIOBase_TypeObj; static int init_file_emulator(void) { PyObject *io = PyImport_ImportModule("_io"); if (io == NULL) return -1; PyIOBase_TypeObj = PyObject_GetAttrString(io, "_IOBase"); if (PyIOBase_TypeObj == NULL) return -1; return 0; } #define PyFile_Check(p) PyObject_IsInstance(p, PyIOBase_TypeObj) static void _close_file_capsule(PyObject *ob_capsule) { FILE *f = (FILE *)PyCapsule_GetPointer(ob_capsule, "FILE"); if (f != NULL) fclose(f); } static FILE *PyFile_AsFile(PyObject *ob_file) { PyObject *ob, *ob_capsule = NULL, *ob_mode = NULL; FILE *f = NULL; int fd; char *mode; ob = PyObject_CallMethod(ob_file, "flush", NULL); if (ob == NULL) goto fail; Py_DECREF(ob); ob_capsule = PyObject_GetAttrString(ob_file, "__cffi_FILE"); if (ob_capsule == NULL) { PyErr_Clear(); fd = PyObject_AsFileDescriptor(ob_file); if (fd < 0) goto fail; ob_mode = PyObject_GetAttrString(ob_file, "mode"); if (ob_mode == NULL) goto fail; mode = PyText_AsUTF8(ob_mode); if (mode == NULL) goto fail; fd = dup(fd); if (fd < 0) { PyErr_SetFromErrno(PyExc_OSError); goto fail; } f = fdopen(fd, mode); if (f == NULL) { close(fd); PyErr_SetFromErrno(PyExc_OSError); goto fail; } setbuf(f, NULL); /* non-buffered */ Py_DECREF(ob_mode); ob_mode = NULL; ob_capsule = PyCapsule_New(f, "FILE", _close_file_capsule); if (ob_capsule == NULL) { fclose(f); goto fail; } if (PyObject_SetAttrString(ob_file, "__cffi_FILE", ob_capsule) < 0) goto fail; } return PyCapsule_GetPointer(ob_capsule, "FILE"); fail: Py_XDECREF(ob_mode); Py_XDECREF(ob_capsule); return NULL; } cffi-0.8.2/c/test_c.py0000644000076400001440000033753712306266214015015 0ustar arigousers00000000000000import py def _setup_path(): import os, sys if '__pypy__' in sys.builtin_module_names: py.test.skip("_cffi_backend.c: not tested on top of pypy, " "use pypy/module/_cffi_backend/test/ instead.") sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) _setup_path() from _cffi_backend import * from _cffi_backend import _testfunc, _get_types, __version__ # ____________________________________________________________ import sys if sys.version_info < (3,): type_or_class = "type" mandatory_b_prefix = '' mandatory_u_prefix = 'u' bytechr = chr bitem2bchr = lambda x: x class U(object): def __add__(self, other): return eval('u'+repr(other).replace(r'\\u', r'\u') .replace(r'\\U', r'\U')) u = U() str2bytes = str else: type_or_class = "class" long = int unicode = str unichr = chr mandatory_b_prefix = 'b' mandatory_u_prefix = '' bytechr = lambda n: bytes([n]) bitem2bchr = bytechr u = "" str2bytes = lambda s: bytes(s, "ascii") def size_of_int(): BInt = new_primitive_type("int") return sizeof(BInt) def size_of_long(): BLong = new_primitive_type("long") return sizeof(BLong) def size_of_ptr(): BInt = new_primitive_type("int") BPtr = new_pointer_type(BInt) return sizeof(BPtr) def find_and_load_library(name, flags=RTLD_NOW): import ctypes.util if name is None: path = None else: path = ctypes.util.find_library(name) return load_library(path, flags) def test_load_library(): x = find_and_load_library('c') assert repr(x).startswith("" def check_dir(p, expected): got = set(name for name in dir(p) if not name.startswith('_')) assert got == set(expected) def test_inspect_primitive_type(): p = new_primitive_type("signed char") assert p.kind == "primitive" assert p.cname == "signed char" check_dir(p, ['cname', 'kind']) def test_cast_to_signed_char(): p = new_primitive_type("signed char") x = cast(p, -65 + 17*256) assert repr(x) == "" assert repr(type(x)) == "<%s '_cffi_backend.CData'>" % type_or_class assert int(x) == -65 x = cast(p, -66 + (1<<199)*256) assert repr(x) == "" assert int(x) == -66 assert (x == cast(p, -66)) is False assert (x != cast(p, -66)) is True q = new_primitive_type("short") assert (x == cast(q, -66)) is False assert (x != cast(q, -66)) is True def test_sizeof_type(): py.test.raises(TypeError, sizeof, 42.5) p = new_primitive_type("short") assert sizeof(p) == 2 def test_integer_types(): for name in ['signed char', 'short', 'int', 'long', 'long long']: p = new_primitive_type(name) size = sizeof(p) min = -(1 << (8*size-1)) max = (1 << (8*size-1)) - 1 assert int(cast(p, min)) == min assert int(cast(p, max)) == max assert int(cast(p, min - 1)) == max assert int(cast(p, max + 1)) == min py.test.raises(TypeError, cast, p, None) assert long(cast(p, min - 1)) == max assert int(cast(p, b'\x08')) == 8 assert int(cast(p, u+'\x08')) == 8 for name in ['char', 'short', 'int', 'long', 'long long']: p = new_primitive_type('unsigned ' + name) size = sizeof(p) max = (1 << (8*size)) - 1 assert int(cast(p, 0)) == 0 assert int(cast(p, max)) == max assert int(cast(p, -1)) == max assert int(cast(p, max + 1)) == 0 assert long(cast(p, -1)) == max assert int(cast(p, b'\xFE')) == 254 assert int(cast(p, u+'\xFE')) == 254 def test_no_float_on_int_types(): p = new_primitive_type('long') py.test.raises(TypeError, float, cast(p, 42)) py.test.raises(TypeError, complex, cast(p, 42)) def test_float_types(): INF = 1E200 * 1E200 for name in ["float", "double"]: p = new_primitive_type(name) assert bool(cast(p, 0)) assert bool(cast(p, INF)) assert bool(cast(p, -INF)) assert int(cast(p, -150)) == -150 assert int(cast(p, 61.91)) == 61 assert long(cast(p, 61.91)) == 61 assert type(int(cast(p, 61.91))) is int assert type(int(cast(p, 1E22))) is long assert type(long(cast(p, 61.91))) is long assert type(long(cast(p, 1E22))) is long py.test.raises(OverflowError, int, cast(p, INF)) py.test.raises(OverflowError, int, cast(p, -INF)) assert float(cast(p, 1.25)) == 1.25 assert float(cast(p, INF)) == INF assert float(cast(p, -INF)) == -INF if name == "float": assert float(cast(p, 1.1)) != 1.1 # rounding error assert float(cast(p, 1E200)) == INF # limited range assert cast(p, -1.1) != cast(p, -1.1) assert repr(float(cast(p, -0.0))) == '-0.0' assert float(cast(p, b'\x09')) == 9.0 assert float(cast(p, u+'\x09')) == 9.0 assert float(cast(p, True)) == 1.0 py.test.raises(TypeError, cast, p, None) def test_complex_types(): py.test.skip("later") INF = 1E200 * 1E200 for name in ["float", "double"]: p = new_primitive_type("_Complex " + name) assert bool(cast(p, 0)) assert bool(cast(p, INF)) assert bool(cast(p, -INF)) assert bool(cast(p, 0j)) assert bool(cast(p, INF*1j)) assert bool(cast(p, -INF*1j)) py.test.raises(TypeError, int, cast(p, -150)) py.test.raises(TypeError, long, cast(p, -150)) py.test.raises(TypeError, float, cast(p, -150)) assert complex(cast(p, 1.25)) == 1.25 assert complex(cast(p, 1.25j)) == 1.25j assert float(cast(p, INF*1j)) == INF*1j assert float(cast(p, -INF)) == -INF if name == "float": assert complex(cast(p, 1.1j)) != 1.1j # rounding error assert complex(cast(p, 1E200+3j)) == INF+3j # limited range assert complex(cast(p, 3+1E200j)) == 3+INF*1j # limited range assert cast(p, -1.1j) != cast(p, -1.1j) assert repr(complex(cast(p, -0.0)).real) == '-0.0' assert repr(complex(cast(p, -0j))) == '-0j' assert complex(cast(p, '\x09')) == 9.0 assert complex(cast(p, True)) == 1.0 py.test.raises(TypeError, cast, p, None) # py.test.raises(cast, new_primitive_type(name), 1+2j) py.test.raises(cast, new_primitive_type("int"), 1+2j) def test_character_type(): p = new_primitive_type("char") assert bool(cast(p, '\x00')) assert cast(p, '\x00') != cast(p, -17*256) assert int(cast(p, 'A')) == 65 assert long(cast(p, 'A')) == 65 assert type(int(cast(p, 'A'))) is int assert type(long(cast(p, 'A'))) is long assert str(cast(p, 'A')) == repr(cast(p, 'A')) assert repr(cast(p, 'A')) == "" % mandatory_b_prefix assert repr(cast(p, 255)) == r"" % mandatory_b_prefix assert repr(cast(p, 0)) == r"" % mandatory_b_prefix def test_pointer_type(): p = new_primitive_type("int") assert repr(p) == "" p = new_pointer_type(p) assert repr(p) == "" p = new_pointer_type(p) assert repr(p) == "" p = new_pointer_type(p) assert repr(p) == "" def test_inspect_pointer_type(): p1 = new_primitive_type("int") p2 = new_pointer_type(p1) assert p2.kind == "pointer" assert p2.cname == "int *" assert p2.item is p1 check_dir(p2, ['cname', 'kind', 'item']) p3 = new_pointer_type(p2) assert p3.item is p2 def test_pointer_to_int(): BInt = new_primitive_type("int") py.test.raises(TypeError, newp, BInt) py.test.raises(TypeError, newp, BInt, None) BPtr = new_pointer_type(BInt) p = newp(BPtr) assert repr(p) == "" % size_of_int() p = newp(BPtr, None) assert repr(p) == "" % size_of_int() p = newp(BPtr, 5000) assert repr(p) == "" % size_of_int() q = cast(BPtr, p) assert repr(q).startswith("" % size_of_ptr() def test_reading_pointer_to_int(): BInt = new_primitive_type("int") BPtr = new_pointer_type(BInt) p = newp(BPtr, None) assert p[0] == 0 p = newp(BPtr, 5000) assert p[0] == 5000 py.test.raises(IndexError, "p[1]") py.test.raises(IndexError, "p[-1]") def test_reading_pointer_to_float(): BFloat = new_primitive_type("float") py.test.raises(TypeError, newp, BFloat, None) BPtr = new_pointer_type(BFloat) p = newp(BPtr, None) assert p[0] == 0.0 and type(p[0]) is float p = newp(BPtr, 1.25) assert p[0] == 1.25 and type(p[0]) is float p = newp(BPtr, 1.1) assert p[0] != 1.1 and abs(p[0] - 1.1) < 1E-5 # rounding errors def test_cast_float_to_int(): for type in ["int", "unsigned int", "long", "unsigned long", "long long", "unsigned long long"]: p = new_primitive_type(type) assert int(cast(p, 4.2)) == 4 py.test.raises(TypeError, newp, new_pointer_type(p), 4.2) def test_newp_integer_types(): for name in ['signed char', 'short', 'int', 'long', 'long long']: p = new_primitive_type(name) pp = new_pointer_type(p) size = sizeof(p) min = -(1 << (8*size-1)) max = (1 << (8*size-1)) - 1 assert newp(pp, min)[0] == min assert newp(pp, max)[0] == max py.test.raises(OverflowError, newp, pp, min - 1) py.test.raises(OverflowError, newp, pp, max + 1) for name in ['char', 'short', 'int', 'long', 'long long']: p = new_primitive_type('unsigned ' + name) pp = new_pointer_type(p) size = sizeof(p) max = (1 << (8*size)) - 1 assert newp(pp, 0)[0] == 0 assert newp(pp, max)[0] == max py.test.raises(OverflowError, newp, pp, -1) py.test.raises(OverflowError, newp, pp, max + 1) def test_reading_pointer_to_char(): BChar = new_primitive_type("char") py.test.raises(TypeError, newp, BChar, None) BPtr = new_pointer_type(BChar) p = newp(BPtr, None) assert p[0] == b'\x00' p = newp(BPtr, b'A') assert p[0] == b'A' py.test.raises(TypeError, newp, BPtr, 65) py.test.raises(TypeError, newp, BPtr, b"foo") py.test.raises(TypeError, newp, BPtr, u+"foo") c = cast(BChar, b'A') assert str(c) == repr(c) assert int(c) == ord(b'A') py.test.raises(TypeError, cast, BChar, b'foo') py.test.raises(TypeError, cast, BChar, u+'foo') def test_reading_pointer_to_pointer(): BVoidP = new_pointer_type(new_void_type()) BCharP = new_pointer_type(new_primitive_type("char")) BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) BIntPtrPtr = new_pointer_type(BIntPtr) q = newp(BIntPtr, 42) assert q[0] == 42 p = newp(BIntPtrPtr, None) assert p[0] is not None assert p[0] == cast(BVoidP, 0) assert p[0] == cast(BCharP, 0) assert p[0] != None assert repr(p[0]) == "" p[0] = q assert p[0] != cast(BVoidP, 0) assert p[0] != cast(BCharP, 0) assert p[0][0] == 42 q[0] += 1 assert p[0][0] == 43 p = newp(BIntPtrPtr, q) assert p[0][0] == 43 def test_load_standard_library(): if sys.platform == "win32": py.test.raises(OSError, find_and_load_library, None) return x = find_and_load_library(None) BVoidP = new_pointer_type(new_void_type()) assert x.load_function(BVoidP, 'strcpy') py.test.raises(KeyError, x.load_function, BVoidP, 'xxx_this_function_does_not_exist') # the next one is from 'libm', not 'libc', but we assume # that it is already loaded too, so it should work assert x.load_function(BVoidP, 'sqrt') def test_hash_differences(): BChar = new_primitive_type("char") BInt = new_primitive_type("int") BFloat = new_primitive_type("float") for i in range(1, 20): x1 = cast(BChar, chr(i)) x2 = cast(BInt, i) if hash(x1) != hash(x2): break else: raise AssertionError("hashes are equal") for i in range(1, 20): if hash(cast(BFloat, i)) != hash(float(i)): break else: raise AssertionError("hashes are equal") def test_no_len_on_nonarray(): p = new_primitive_type("int") py.test.raises(TypeError, len, cast(p, 42)) def test_cmp_none(): p = new_primitive_type("int") x = cast(p, 42) assert (x == None) is False assert (x != None) is True assert (x == ["hello"]) is False assert (x != ["hello"]) is True y = cast(p, 0) assert (y == None) is False def test_invalid_indexing(): p = new_primitive_type("int") x = cast(p, 42) py.test.raises(TypeError, "p[0]") def test_default_str(): BChar = new_primitive_type("char") x = cast(BChar, 42) assert str(x) == repr(x) BInt = new_primitive_type("int") x = cast(BInt, 42) assert str(x) == repr(x) BArray = new_array_type(new_pointer_type(BInt), 10) x = newp(BArray, None) assert str(x) == repr(x) def test_default_unicode(): BInt = new_primitive_type("int") x = cast(BInt, 42) assert unicode(x) == unicode(repr(x)) BArray = new_array_type(new_pointer_type(BInt), 10) x = newp(BArray, None) assert unicode(x) == unicode(repr(x)) def test_cast_from_cdataint(): BInt = new_primitive_type("int") x = cast(BInt, 0) y = cast(new_pointer_type(BInt), x) assert bool(y) is False # x = cast(BInt, 42) y = cast(BInt, x) assert int(y) == 42 y = cast(new_primitive_type("char"), x) assert int(y) == 42 y = cast(new_primitive_type("float"), x) assert float(y) == 42.0 # z = cast(BInt, 42.5) assert int(z) == 42 z = cast(BInt, y) assert int(z) == 42 def test_void_type(): p = new_void_type() assert p.kind == "void" assert p.cname == "void" check_dir(p, ['kind', 'cname']) def test_array_type(): p = new_primitive_type("int") assert repr(p) == "" # py.test.raises(TypeError, new_array_type, new_pointer_type(p), "foo") py.test.raises(ValueError, new_array_type, new_pointer_type(p), -42) # p1 = new_array_type(new_pointer_type(p), None) assert repr(p1) == "" py.test.raises(ValueError, new_array_type, new_pointer_type(p1), 42) # p1 = new_array_type(new_pointer_type(p), 42) p2 = new_array_type(new_pointer_type(p1), 25) assert repr(p2) == "" p2 = new_array_type(new_pointer_type(p1), None) assert repr(p2) == "" # py.test.raises(OverflowError, new_array_type, new_pointer_type(p), sys.maxsize+1) py.test.raises(OverflowError, new_array_type, new_pointer_type(p), sys.maxsize // 3) def test_inspect_array_type(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), None) assert p1.kind == "array" assert p1.cname == "int[]" assert p1.item is p assert p1.length is None check_dir(p1, ['cname', 'kind', 'item', 'length']) p1 = new_array_type(new_pointer_type(p), 42) assert p1.kind == "array" assert p1.cname == "int[42]" assert p1.item is p assert p1.length == 42 check_dir(p1, ['cname', 'kind', 'item', 'length']) def test_array_instance(): LENGTH = 1423 p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), LENGTH) a = newp(p1, None) assert repr(a) == "" % ( LENGTH, LENGTH * size_of_int()) assert len(a) == LENGTH for i in range(LENGTH): assert a[i] == 0 py.test.raises(IndexError, "a[LENGTH]") py.test.raises(IndexError, "a[-1]") for i in range(LENGTH): a[i] = i * i + 1 for i in range(LENGTH): assert a[i] == i * i + 1 e = py.test.raises(IndexError, "a[LENGTH+100] = 500") assert ('(expected %d < %d)' % (LENGTH+100, LENGTH)) in str(e.value) py.test.raises(TypeError, int, a) def test_array_of_unknown_length_instance(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), None) py.test.raises(TypeError, newp, p1, None) py.test.raises(ValueError, newp, p1, -42) a = newp(p1, 42) assert len(a) == 42 for i in range(42): a[i] -= i for i in range(42): assert a[i] == -i py.test.raises(IndexError, "a[42]") py.test.raises(IndexError, "a[-1]") py.test.raises(IndexError, "a[42] = 123") py.test.raises(IndexError, "a[-1] = 456") def test_array_of_unknown_length_instance_with_initializer(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), None) a = newp(p1, list(range(42))) assert len(a) == 42 a = newp(p1, tuple(range(142))) assert len(a) == 142 def test_array_initializer(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), None) a = newp(p1, list(range(100, 142))) for i in range(42): assert a[i] == 100 + i # p2 = new_array_type(new_pointer_type(p), 43) a = newp(p2, tuple(range(100, 142))) for i in range(42): assert a[i] == 100 + i assert a[42] == 0 # extra uninitialized item def test_array_add(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), 5) # int[5] p2 = new_array_type(new_pointer_type(p1), 3) # int[3][5] a = newp(p2, [list(range(n, n+5)) for n in [100, 200, 300]]) assert repr(a) == "" % ( 3*5*size_of_int(),) assert repr(a + 0).startswith("" BStruct = new_struct_type("struct foo") assert repr(BStruct) == "" BPtr = new_pointer_type(BStruct) assert repr(BPtr) == "" py.test.raises(ValueError, sizeof, BStruct) py.test.raises(ValueError, alignof, BStruct) def test_new_union_type(): BUnion = new_union_type("union foo") assert repr(BUnion) == "" BPtr = new_pointer_type(BUnion) assert repr(BPtr) == "" def test_complete_struct(): BLong = new_primitive_type("long") BChar = new_primitive_type("char") BShort = new_primitive_type("short") BStruct = new_struct_type("struct foo") assert BStruct.kind == "struct" assert BStruct.cname == "struct foo" assert BStruct.fields is None check_dir(BStruct, ['cname', 'kind', 'fields']) # complete_struct_or_union(BStruct, [('a1', BLong, -1), ('a2', BChar, -1), ('a3', BShort, -1)]) d = BStruct.fields assert len(d) == 3 assert d[0][0] == 'a1' assert d[0][1].type is BLong assert d[0][1].offset == 0 assert d[0][1].bitshift == -1 assert d[0][1].bitsize == -1 assert d[1][0] == 'a2' assert d[1][1].type is BChar assert d[1][1].offset == sizeof(BLong) assert d[1][1].bitshift == -1 assert d[1][1].bitsize == -1 assert d[2][0] == 'a3' assert d[2][1].type is BShort assert d[2][1].offset == sizeof(BLong) + sizeof(BShort) assert d[2][1].bitshift == -1 assert d[2][1].bitsize == -1 assert sizeof(BStruct) == 2 * sizeof(BLong) assert alignof(BStruct) == alignof(BLong) def test_complete_union(): BLong = new_primitive_type("long") BChar = new_primitive_type("char") BUnion = new_union_type("union foo") assert BUnion.kind == "union" assert BUnion.cname == "union foo" assert BUnion.fields is None complete_struct_or_union(BUnion, [('a1', BLong, -1), ('a2', BChar, -1)]) d = BUnion.fields assert len(d) == 2 assert d[0][0] == 'a1' assert d[0][1].type is BLong assert d[0][1].offset == 0 assert d[1][0] == 'a2' assert d[1][1].type is BChar assert d[1][1].offset == 0 assert sizeof(BUnion) == sizeof(BLong) assert alignof(BUnion) == alignof(BLong) def test_struct_instance(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) p = cast(BStructPtr, 0) py.test.raises(AttributeError, "p.a1") # opaque complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1)]) p = newp(BStructPtr, None) s = p[0] assert s.a1 == 0 s.a2 = 123 assert s.a1 == 0 assert s.a2 == 123 py.test.raises(OverflowError, "s.a1 = sys.maxsize+1") assert s.a1 == 0 py.test.raises(AttributeError, "p.foobar") py.test.raises(AttributeError, "s.foobar") def test_union_instance(): BInt = new_primitive_type("int") BUInt = new_primitive_type("unsigned int") BUnion = new_union_type("union bar") complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)]) p = newp(new_pointer_type(BUnion), [-42]) bigval = -42 + (1 << (8*size_of_int())) assert p.a1 == -42 assert p.a2 == bigval p = newp(new_pointer_type(BUnion), {'a2': bigval}) assert p.a1 == -42 assert p.a2 == bigval py.test.raises(OverflowError, newp, new_pointer_type(BUnion), {'a1': bigval}) p = newp(new_pointer_type(BUnion), []) assert p.a1 == p.a2 == 0 def test_struct_pointer(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1)]) p = newp(BStructPtr, None) assert p.a1 == 0 # read/write via the pointer (C equivalent: '->') p.a2 = 123 assert p.a1 == 0 assert p.a2 == 123 def test_struct_init_list(): BVoidP = new_pointer_type(new_void_type()) BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1), ('a3', BInt, -1), ('p4', BIntPtr, -1)]) s = newp(BStructPtr, [123, 456]) assert s.a1 == 123 assert s.a2 == 456 assert s.a3 == 0 assert s.p4 == cast(BVoidP, 0) assert s.p4 != 0 # s = newp(BStructPtr, {'a2': 41122, 'a3': -123}) assert s.a1 == 0 assert s.a2 == 41122 assert s.a3 == -123 assert s.p4 == cast(BVoidP, 0) # py.test.raises(KeyError, newp, BStructPtr, {'foobar': 0}) # p = newp(BIntPtr, 14141) s = newp(BStructPtr, [12, 34, 56, p]) assert s.p4 == p assert s.p4 # s = newp(BStructPtr, [12, 34, 56, cast(BVoidP, 0)]) assert s.p4 == cast(BVoidP, 0) assert not s.p4 # py.test.raises(TypeError, newp, BStructPtr, [12, 34, 56, None]) def test_array_in_struct(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BArrayInt5 = new_array_type(new_pointer_type(BInt), 5) complete_struct_or_union(BStruct, [('a1', BArrayInt5, -1)]) s = newp(new_pointer_type(BStruct), [[20, 24, 27, 29, 30]]) assert s.a1[2] == 27 assert repr(s.a1).startswith("" BFunc2 = new_function_type((), BFunc, False) assert repr(BFunc2) == "" def test_inspect_function_type(): BInt = new_primitive_type("int") BFunc = new_function_type((BInt, BInt), BInt, False) assert BFunc.kind == "function" assert BFunc.cname == "int(*)(int, int)" assert BFunc.args == (BInt, BInt) assert BFunc.result is BInt assert BFunc.ellipsis is False assert BFunc.abi == FFI_DEFAULT_ABI def test_function_type_taking_struct(): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BChar, -1), ('a2', BShort, -1)]) BFunc = new_function_type((BStruct,), BShort, False) assert repr(BFunc) == "" def test_function_void_result(): BVoid = new_void_type() BInt = new_primitive_type("int") BFunc = new_function_type((BInt, BInt), BVoid, False) assert repr(BFunc) == "" def test_function_void_arg(): BVoid = new_void_type() BInt = new_primitive_type("int") py.test.raises(TypeError, new_function_type, (BVoid,), BInt, False) def test_call_function_0(): BSignedChar = new_primitive_type("signed char") BFunc0 = new_function_type((BSignedChar, BSignedChar), BSignedChar, False) f = cast(BFunc0, _testfunc(0)) assert f(40, 2) == 42 assert f(-100, -100) == -200 + 256 py.test.raises(OverflowError, f, 128, 0) py.test.raises(OverflowError, f, 0, 128) def test_call_function_1(): BInt = new_primitive_type("int") BLong = new_primitive_type("long") BFunc1 = new_function_type((BInt, BLong), BLong, False) f = cast(BFunc1, _testfunc(1)) assert f(40, 2) == 42 assert f(-100, -100) == -200 int_max = (1 << (8*size_of_int()-1)) - 1 long_max = (1 << (8*size_of_long()-1)) - 1 if int_max == long_max: assert f(int_max, 1) == - int_max - 1 else: assert f(int_max, 1) == int_max + 1 def test_call_function_2(): BLongLong = new_primitive_type("long long") BFunc2 = new_function_type((BLongLong, BLongLong), BLongLong, False) f = cast(BFunc2, _testfunc(2)) longlong_max = (1 << (8*sizeof(BLongLong)-1)) - 1 assert f(longlong_max - 42, 42) == longlong_max assert f(43, longlong_max - 42) == - longlong_max - 1 def test_call_function_3(): BFloat = new_primitive_type("float") BDouble = new_primitive_type("double") BFunc3 = new_function_type((BFloat, BDouble), BDouble, False) f = cast(BFunc3, _testfunc(3)) assert f(1.25, 5.1) == 1.25 + 5.1 # exact res = f(1.3, 5.1) assert res != 6.4 and abs(res - 6.4) < 1E-5 # inexact def test_call_function_4(): BFloat = new_primitive_type("float") BDouble = new_primitive_type("double") BFunc4 = new_function_type((BFloat, BDouble), BFloat, False) f = cast(BFunc4, _testfunc(4)) res = f(1.25, 5.1) assert res != 6.35 and abs(res - 6.35) < 1E-5 # inexact def test_call_function_5(): BVoid = new_void_type() BFunc5 = new_function_type((), BVoid, False) f = cast(BFunc5, _testfunc(5)) f() # did not crash def test_call_function_6(): BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) BFunc6 = new_function_type((BIntPtr,), BIntPtr, False) f = cast(BFunc6, _testfunc(6)) x = newp(BIntPtr, 42) res = f(x) assert typeof(res) is BIntPtr assert res[0] == 42 - 1000 # BIntArray = new_array_type(BIntPtr, None) BFunc6bis = new_function_type((BIntArray,), BIntPtr, False) f = cast(BFunc6bis, _testfunc(6)) # res = f([142]) assert typeof(res) is BIntPtr assert res[0] == 142 - 1000 # res = f((143,)) assert typeof(res) is BIntPtr assert res[0] == 143 - 1000 # x = newp(BIntArray, [242]) res = f(x) assert typeof(res) is BIntPtr assert res[0] == 242 - 1000 # py.test.raises(TypeError, f, 123456) py.test.raises(TypeError, f, "foo") py.test.raises(TypeError, f, u+"bar") def test_call_function_7(): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BChar, -1), ('a2', BShort, -1)]) BFunc7 = new_function_type((BStruct,), BShort, False) f = cast(BFunc7, _testfunc(7)) res = f({'a1': b'A', 'a2': -4042}) assert res == -4042 + ord(b'A') # x = newp(BStructPtr, {'a1': b'A', 'a2': -4042}) res = f(x[0]) assert res == -4042 + ord(b'A') def test_call_function_20(): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BChar, -1), ('a2', BShort, -1)]) BFunc20 = new_function_type((BStructPtr,), BShort, False) f = cast(BFunc20, _testfunc(20)) x = newp(BStructPtr, {'a1': b'A', 'a2': -4042}) # can't pass a 'struct foo' py.test.raises(TypeError, f, x[0]) def test_call_function_21(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a', BInt, -1), ('b', BInt, -1), ('c', BInt, -1), ('d', BInt, -1), ('e', BInt, -1), ('f', BInt, -1), ('g', BInt, -1), ('h', BInt, -1), ('i', BInt, -1), ('j', BInt, -1)]) BFunc21 = new_function_type((BStruct,), BInt, False) f = cast(BFunc21, _testfunc(21)) res = f(list(range(13, 3, -1))) lst = [(n << i) for (i, n) in enumerate(range(13, 3, -1))] assert res == sum(lst) def test_call_function_22(): BInt = new_primitive_type("int") BArray10 = new_array_type(new_pointer_type(BInt), 10) BStruct = new_struct_type("struct foo") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BArray10, -1)]) BFunc22 = new_function_type((BStruct, BStruct), BStruct, False) f = cast(BFunc22, _testfunc(22)) p1 = newp(BStructP, {'a': list(range(100, 110))}) p2 = newp(BStructP, {'a': list(range(1000, 1100, 10))}) res = f(p1[0], p2[0]) for i in range(10): assert res.a[i] == p1.a[i] - p2.a[i] def test_call_function_23(): BVoid = new_void_type() # declaring the function as int(void*) BVoidP = new_pointer_type(BVoid) BInt = new_primitive_type("int") BFunc23 = new_function_type((BVoidP,), BInt, False) f = cast(BFunc23, _testfunc(23)) res = f(b"foo") assert res == 1000 * ord(b'f') res = f(cast(BVoidP, 0)) # NULL assert res == -42 py.test.raises(TypeError, f, None) py.test.raises(TypeError, f, 0) py.test.raises(TypeError, f, 0.0) def test_call_function_23_bis(): # declaring the function as int(unsigned char*) BUChar = new_primitive_type("unsigned char") BUCharP = new_pointer_type(BUChar) BInt = new_primitive_type("int") BFunc23 = new_function_type((BUCharP,), BInt, False) f = cast(BFunc23, _testfunc(23)) res = f(b"foo") assert res == 1000 * ord(b'f') def test_cannot_pass_struct_with_array_of_length_0(): BInt = new_primitive_type("int") BArray0 = new_array_type(new_pointer_type(BInt), 0) BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a', BArray0)]) py.test.raises(NotImplementedError, new_function_type, (BStruct,), BInt, False) py.test.raises(NotImplementedError, new_function_type, (BInt,), BStruct, False) def test_call_function_9(): BInt = new_primitive_type("int") BFunc9 = new_function_type((BInt,), BInt, True) # vararg f = cast(BFunc9, _testfunc(9)) assert f(0) == 0 assert f(1, cast(BInt, 42)) == 42 assert f(2, cast(BInt, 40), cast(BInt, 2)) == 42 py.test.raises(TypeError, f, 1, 42) py.test.raises(TypeError, f, 2, None) # promotion of chars and shorts to ints BSChar = new_primitive_type("signed char") BUChar = new_primitive_type("unsigned char") BSShort = new_primitive_type("short") assert f(3, cast(BSChar, -3), cast(BUChar, 200), cast(BSShort, -5)) == 192 def test_cannot_call_with_a_autocompleted_struct(): BSChar = new_primitive_type("signed char") BDouble = new_primitive_type("double") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('c', BDouble, -1, 8), ('a', BSChar, -1, 2), ('b', BSChar, -1, 0)]) e = py.test.raises(TypeError, new_function_type, (BStruct,), BDouble) msg ='cannot pass as an argument a struct that was completed with verify()' assert msg in str(e.value) def test_new_charp(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) x = newp(BCharA, 42) assert len(x) == 42 x = newp(BCharA, b"foobar") assert len(x) == 7 def test_load_and_call_function(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BLong = new_primitive_type("long") BFunc = new_function_type((BCharP,), BLong, False) ll = find_and_load_library('c') strlen = ll.load_function(BFunc, "strlen") input = newp(new_array_type(BCharP, None), b"foobar") assert strlen(input) == 6 # assert strlen(b"foobarbaz") == 9 # BVoidP = new_pointer_type(new_void_type()) strlenaddr = ll.load_function(BVoidP, "strlen") assert strlenaddr == cast(BVoidP, strlen) def test_read_variable(): ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard ## https://bugs.pypy.org/issue1643 if sys.platform == 'win32' or sys.platform == 'darwin' or sys.platform.startswith('freebsd'): py.test.skip("untested") BVoidP = new_pointer_type(new_void_type()) ll = find_and_load_library('c') stderr = ll.read_variable(BVoidP, "stderr") assert stderr == cast(BVoidP, _testfunc(8)) def test_read_variable_as_unknown_length_array(): ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard ## https://bugs.pypy.org/issue1643 if sys.platform == 'win32' or sys.platform == 'darwin' or sys.platform.startswith('freebsd'): py.test.skip("untested") BCharP = new_pointer_type(new_primitive_type("char")) BArray = new_array_type(BCharP, None) ll = find_and_load_library('c') stderr = ll.read_variable(BArray, "stderr") assert repr(stderr).startswith(": Traceback (most recent call last): File "$", line $, in Zcb1 $ File "$", line $, in check_value $ ValueError: 42 """) sys.stderr = cStringIO.StringIO() bigvalue = 20000 assert f(bigvalue) == -42 assert matches(sys.stderr.getvalue(), """\ From callback : Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' """) finally: sys.stderr = orig_stderr linecache.getline = orig_getline def test_callback_return_type(): for rettype in ["signed char", "short", "int", "long", "long long", "unsigned char", "unsigned short", "unsigned int", "unsigned long", "unsigned long long"]: BRet = new_primitive_type(rettype) def cb(n): return n + 1 BFunc = new_function_type((BRet,), BRet) f = callback(BFunc, cb, 42) assert f(41) == 42 if rettype.startswith("unsigned "): min = 0 max = (1 << (8*sizeof(BRet))) - 1 else: min = -(1 << (8*sizeof(BRet)-1)) max = (1 << (8*sizeof(BRet)-1)) - 1 assert f(min) == min + 1 assert f(max - 1) == max assert f(max) == 42 def test_a_lot_of_callbacks(): BIGNUM = 10000 if 'PY_DOT_PY' in globals(): BIGNUM = 100 # tests on py.py # BInt = new_primitive_type("int") BFunc = new_function_type((BInt,), BInt, False) def make_callback(m): def cb(n): return n + m return callback(BFunc, cb, 42) # 'cb' and 'BFunc' go out of scope # flist = [make_callback(i) for i in range(BIGNUM)] for i, f in enumerate(flist): assert f(-142) == -142 + i def test_callback_receiving_tiny_struct(): BSChar = new_primitive_type("signed char") BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BSChar, -1), ('b', BSChar, -1)]) def cb(s): return s.a + 10 * s.b BFunc = new_function_type((BStruct,), BInt) f = callback(BFunc, cb) p = newp(BStructPtr, [-2, -4]) n = f(p[0]) assert n == -42 def test_callback_returning_tiny_struct(): BSChar = new_primitive_type("signed char") BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BSChar, -1), ('b', BSChar, -1)]) def cb(n): return newp(BStructPtr, [-n, -3*n])[0] BFunc = new_function_type((BInt,), BStruct) f = callback(BFunc, cb) s = f(10) assert typeof(s) is BStruct assert repr(s) == "" assert s.a == -10 assert s.b == -30 def test_callback_receiving_struct(): BSChar = new_primitive_type("signed char") BInt = new_primitive_type("int") BDouble = new_primitive_type("double") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BSChar, -1), ('b', BDouble, -1)]) def cb(s): return s.a + int(s.b) BFunc = new_function_type((BStruct,), BInt) f = callback(BFunc, cb) p = newp(BStructPtr, [-2, 44.444]) n = f(p[0]) assert n == 42 def test_callback_returning_struct(): BSChar = new_primitive_type("signed char") BInt = new_primitive_type("int") BDouble = new_primitive_type("double") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BSChar, -1), ('b', BDouble, -1)]) def cb(n): return newp(BStructPtr, [-n, 1E-42])[0] BFunc = new_function_type((BInt,), BStruct) f = callback(BFunc, cb) s = f(10) assert typeof(s) is BStruct assert repr(s) in ["", ""] assert s.a == -10 assert s.b == 1E-42 def test_callback_receiving_big_struct(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BInt, -1), ('b', BInt, -1), ('c', BInt, -1), ('d', BInt, -1), ('e', BInt, -1), ('f', BInt, -1), ('g', BInt, -1), ('h', BInt, -1), ('i', BInt, -1), ('j', BInt, -1)]) def cb(s): for i, name in enumerate("abcdefghij"): assert getattr(s, name) == 13 - i return 42 BFunc = new_function_type((BStruct,), BInt) f = callback(BFunc, cb) p = newp(BStructPtr, list(range(13, 3, -1))) n = f(p[0]) assert n == 42 def test_callback_returning_big_struct(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BInt, -1), ('b', BInt, -1), ('c', BInt, -1), ('d', BInt, -1), ('e', BInt, -1), ('f', BInt, -1), ('g', BInt, -1), ('h', BInt, -1), ('i', BInt, -1), ('j', BInt, -1)]) def cb(): return newp(BStructPtr, list(range(13, 3, -1)))[0] BFunc = new_function_type((), BStruct) f = callback(BFunc, cb) s = f() assert typeof(s) is BStruct assert repr(s) in ["", ""] for i, name in enumerate("abcdefghij"): assert getattr(s, name) == 13 - i def test_callback_returning_void(): BVoid = new_void_type() BFunc = new_function_type((), BVoid, False) def cb(): seen.append(42) f = callback(BFunc, cb) seen = [] f() assert seen == [42] py.test.raises(TypeError, callback, BFunc, cb, -42) def test_enum_type(): BUInt = new_primitive_type("unsigned int") BEnum = new_enum_type("foo", (), (), BUInt) assert repr(BEnum) == "" assert BEnum.kind == "enum" assert BEnum.cname == "foo" assert BEnum.elements == {} # BInt = new_primitive_type("int") BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) assert BEnum.kind == "enum" assert BEnum.cname == "enum foo" assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'} # 'elements' is not the real dict, but merely a copy BEnum.elements[2] = '??' assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'} # BEnum = new_enum_type("enum bar", ('ab', 'cd'), (5, 5), BUInt) assert BEnum.elements == {5: 'ab'} assert BEnum.relements == {'ab': 5, 'cd': 5} def test_cast_to_enum(): BInt = new_primitive_type("int") BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) assert sizeof(BEnum) == sizeof(BInt) e = cast(BEnum, 0) assert repr(e) == "" assert repr(cast(BEnum, -42)) == "" assert repr(cast(BEnum, -20)) == "" assert string(e) == 'def' assert string(cast(BEnum, -20)) == 'ab' assert int(cast(BEnum, 1)) == 1 assert int(cast(BEnum, 0)) == 0 assert int(cast(BEnum, -242 + 2**128)) == -242 assert string(cast(BEnum, -242 + 2**128)) == '-242' # BUInt = new_primitive_type("unsigned int") BEnum = new_enum_type("enum bar", ('def', 'c', 'ab'), (0, 1, 20), BUInt) e = cast(BEnum, -1) assert repr(e) == "" # unsigned int # BLong = new_primitive_type("long") BEnum = new_enum_type("enum baz", (), (), BLong) assert sizeof(BEnum) == sizeof(BLong) e = cast(BEnum, -1) assert repr(e) == "" def test_enum_with_non_injective_mapping(): BInt = new_primitive_type("int") BEnum = new_enum_type("enum foo", ('ab', 'cd'), (7, 7), BInt) e = cast(BEnum, 7) assert repr(e) == "" assert string(e) == 'ab' def test_enum_in_struct(): BInt = new_primitive_type("int") BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) BStruct = new_struct_type("struct bar") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BEnum, -1)]) p = newp(BStructPtr, [-20]) assert p.a1 == -20 p = newp(BStructPtr, [12]) assert p.a1 == 12 e = py.test.raises(TypeError, newp, BStructPtr, [None]) msg = str(e.value) assert ("an integer is required" in msg or # CPython "unsupported operand type for int(): 'NoneType'" in msg or # old PyPys "expected integer, got NoneType object" in msg) # newer PyPys py.test.raises(TypeError, 'p.a1 = "def"') if sys.version_info < (3,): BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,), BInt) assert string(cast(BEnum2, 5)) == 'abc' assert type(string(cast(BEnum2, 5))) is str def test_enum_overflow(): max_uint = 2 ** (size_of_int()*8) - 1 max_int = max_uint // 2 max_ulong = 2 ** (size_of_long()*8) - 1 max_long = max_ulong // 2 for BPrimitive in [new_primitive_type("int"), new_primitive_type("unsigned int"), new_primitive_type("long"), new_primitive_type("unsigned long")]: for x in [max_uint, max_int, max_ulong, max_long]: for testcase in [x, x+1, -x-1, -x-2]: if int(cast(BPrimitive, testcase)) == testcase: # fits BEnum = new_enum_type("foo", ("AA",), (testcase,), BPrimitive) assert int(cast(BEnum, testcase)) == testcase else: # overflows py.test.raises(OverflowError, new_enum_type, "foo", ("AA",), (testcase,), BPrimitive) def test_callback_returning_enum(): BInt = new_primitive_type("int") BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) def cb(n): if n & 1: return cast(BEnum, n) else: return n BFunc = new_function_type((BInt,), BEnum) f = callback(BFunc, cb) assert f(0) == 0 assert f(1) == 1 assert f(-20) == -20 assert f(20) == 20 assert f(21) == 21 def test_callback_returning_enum_unsigned(): BInt = new_primitive_type("int") BUInt = new_primitive_type("unsigned int") BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, 20), BUInt) def cb(n): if n & 1: return cast(BEnum, n) else: return n BFunc = new_function_type((BInt,), BEnum) f = callback(BFunc, cb) assert f(0) == 0 assert f(1) == 1 assert f(-21) == 2**32 - 21 assert f(20) == 20 assert f(21) == 21 def test_callback_returning_char(): BInt = new_primitive_type("int") BChar = new_primitive_type("char") def cb(n): return bytechr(n) BFunc = new_function_type((BInt,), BChar) f = callback(BFunc, cb) assert f(0) == b'\x00' assert f(255) == b'\xFF' def _hacked_pypy_uni4(): pyuni4 = {1: True, 2: False}[len(u+'\U00012345')] return 'PY_DOT_PY' in globals() and not pyuni4 def test_callback_returning_wchar_t(): BInt = new_primitive_type("int") BWChar = new_primitive_type("wchar_t") def cb(n): if n == -1: return u+'\U00012345' if n == -2: raise ValueError return unichr(n) BFunc = new_function_type((BInt,), BWChar) f = callback(BFunc, cb) assert f(0) == unichr(0) assert f(255) == unichr(255) assert f(0x1234) == u+'\u1234' if sizeof(BWChar) == 4 and not _hacked_pypy_uni4(): assert f(-1) == u+'\U00012345' assert f(-2) == u+'\x00' # and an exception printed to stderr def test_struct_with_bitfields(): BLong = new_primitive_type("long") BStruct = new_struct_type("struct foo") LONGBITS = 8 * sizeof(BLong) complete_struct_or_union(BStruct, [('a1', BLong, 1), ('a2', BLong, 2), ('a3', BLong, 3), ('a4', BLong, LONGBITS - 5)]) d = BStruct.fields assert d[0][1].offset == d[1][1].offset == d[2][1].offset == 0 assert d[3][1].offset == sizeof(BLong) def f(m, r): if sys.byteorder == 'little': return r else: return LONGBITS - m - r assert d[0][1].bitshift == f(1, 0) assert d[0][1].bitsize == 1 assert d[1][1].bitshift == f(2, 1) assert d[1][1].bitsize == 2 assert d[2][1].bitshift == f(3, 3) assert d[2][1].bitsize == 3 assert d[3][1].bitshift == f(LONGBITS - 5, 0) assert d[3][1].bitsize == LONGBITS - 5 assert sizeof(BStruct) == 2 * sizeof(BLong) assert alignof(BStruct) == alignof(BLong) def test_bitfield_instance(): BInt = new_primitive_type("int") BUnsignedInt = new_primitive_type("unsigned int") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BInt, 1), ('a2', BUnsignedInt, 2), ('a3', BInt, 3)]) p = newp(new_pointer_type(BStruct), None) p.a1 = -1 assert p.a1 == -1 p.a1 = 0 py.test.raises(OverflowError, "p.a1 = 2") assert p.a1 == 0 # p.a1 = -1 p.a2 = 3 p.a3 = -4 py.test.raises(OverflowError, "p.a3 = 4") e = py.test.raises(OverflowError, "p.a3 = -5") assert str(e.value) == ("value -5 outside the range allowed by the " "bit field width: -4 <= x <= 3") assert p.a1 == -1 and p.a2 == 3 and p.a3 == -4 # # special case for convenience: "int x:1", while normally signed, # allows also setting the value "1" (it still gets read back as -1) p.a1 = 1 assert p.a1 == -1 e = py.test.raises(OverflowError, "p.a1 = -2") assert str(e.value) == ("value -2 outside the range allowed by the " "bit field width: -1 <= x <= 1") def test_bitfield_instance_init(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BInt, 1)]) p = newp(new_pointer_type(BStruct), [-1]) assert p.a1 == -1 p = newp(new_pointer_type(BStruct), {'a1': -1}) assert p.a1 == -1 # BUnion = new_union_type("union bar") complete_struct_or_union(BUnion, [('a1', BInt, 1)]) p = newp(new_pointer_type(BUnion), [-1]) assert p.a1 == -1 def test_weakref(): import _weakref BInt = new_primitive_type("int") BPtr = new_pointer_type(BInt) rlist = [_weakref.ref(BInt), _weakref.ref(newp(BPtr, 42)), _weakref.ref(cast(BPtr, 42)), _weakref.ref(cast(BInt, 42)), _weakref.ref(buffer(newp(BPtr, 42))), ] for i in range(5): import gc; gc.collect() if [r() for r in rlist] == [None for r in rlist]: break def test_no_inheritance(): BInt = new_primitive_type("int") try: class foo(type(BInt)): pass except TypeError: pass else: raise AssertionError x = cast(BInt, 42) try: class foo(type(x)): pass except TypeError: pass else: raise AssertionError def test_assign_string(): BChar = new_primitive_type("char") BArray1 = new_array_type(new_pointer_type(BChar), 5) BArray2 = new_array_type(new_pointer_type(BArray1), 5) a = newp(BArray2, [b"abc", b"de", b"ghij"]) assert string(a[1]) == b"de" assert string(a[2]) == b"ghij" a[2] = b"." assert string(a[2]) == b"." a[2] = b"12345" assert string(a[2]) == b"12345" e = py.test.raises(IndexError, 'a[2] = b"123456"') assert 'char[5]' in str(e.value) assert 'got 6 characters' in str(e.value) def test_add_error(): x = cast(new_primitive_type("int"), 42) py.test.raises(TypeError, "x + 1") py.test.raises(TypeError, "x - 1") def test_void_errors(): py.test.raises(ValueError, alignof, new_void_type()) py.test.raises(TypeError, newp, new_pointer_type(new_void_type()), None) def test_too_many_items(): BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), 5) py.test.raises(IndexError, newp, BArray, tuple(b'123456')) py.test.raises(IndexError, newp, BArray, list(b'123456')) py.test.raises(IndexError, newp, BArray, b'123456') BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, []) py.test.raises(TypeError, newp, new_pointer_type(BStruct), b'') py.test.raises(ValueError, newp, new_pointer_type(BStruct), [b'1']) def test_more_type_errors(): BInt = new_primitive_type("int") BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), 5) py.test.raises(TypeError, newp, BArray, 12.34) BArray = new_array_type(new_pointer_type(BInt), 5) py.test.raises(TypeError, newp, BArray, 12.34) BFloat = new_primitive_type("float") py.test.raises(TypeError, cast, BFloat, newp(BArray, None)) def test_more_overflow_errors(): BUInt = new_primitive_type("unsigned int") py.test.raises(OverflowError, newp, new_pointer_type(BUInt), -1) py.test.raises(OverflowError, newp, new_pointer_type(BUInt), 2**32) def test_newp_copying(): """Test that we can do newp(, ) for most types, with the exception of arrays, like in C. """ BInt = new_primitive_type("int") p = newp(new_pointer_type(BInt), cast(BInt, 42)) assert p[0] == 42 # BUInt = new_primitive_type("unsigned int") p = newp(new_pointer_type(BUInt), cast(BUInt, 42)) assert p[0] == 42 # BChar = new_primitive_type("char") p = newp(new_pointer_type(BChar), cast(BChar, '!')) assert p[0] == b'!' # BFloat = new_primitive_type("float") p = newp(new_pointer_type(BFloat), cast(BFloat, 12.25)) assert p[0] == 12.25 # BStruct = new_struct_type("struct foo_s") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BInt, -1)]) s1 = newp(BStructPtr, [42]) p1 = newp(new_pointer_type(BStructPtr), s1) assert p1[0] == s1 # BArray = new_array_type(new_pointer_type(BInt), None) a1 = newp(BArray, [1, 2, 3, 4]) py.test.raises(TypeError, newp, BArray, a1) BArray6 = new_array_type(new_pointer_type(BInt), 6) a1 = newp(BArray6, None) py.test.raises(TypeError, newp, BArray6, a1) # s1 = newp(BStructPtr, [42]) s2 = newp(BStructPtr, s1[0]) assert s2.a1 == 42 # BUnion = new_union_type("union foo_u") BUnionPtr = new_pointer_type(BUnion) complete_struct_or_union(BUnion, [('a1', BInt, -1)]) u1 = newp(BUnionPtr, [42]) u2 = newp(BUnionPtr, u1[0]) assert u2.a1 == 42 # BFunc = new_function_type((BInt,), BUInt) p1 = cast(BFunc, 42) p2 = newp(new_pointer_type(BFunc), p1) assert p2[0] == p1 def test_string(): BChar = new_primitive_type("char") assert string(cast(BChar, 42)) == b'*' assert string(cast(BChar, 0)) == b'\x00' BCharP = new_pointer_type(BChar) BArray = new_array_type(BCharP, 10) a = newp(BArray, b"hello") assert len(a) == 10 assert string(a) == b"hello" p = a + 2 assert string(p) == b"llo" assert string(newp(new_array_type(BCharP, 4), b"abcd")) == b"abcd" py.test.raises(RuntimeError, string, cast(BCharP, 0)) assert string(a, 4) == b"hell" assert string(a, 5) == b"hello" assert string(a, 6) == b"hello" def test_string_byte(): BByte = new_primitive_type("signed char") assert string(cast(BByte, 42)) == b'*' assert string(cast(BByte, 0)) == b'\x00' BArray = new_array_type(new_pointer_type(BByte), None) a = newp(BArray, [65, 66, 67]) assert type(string(a)) is bytes and string(a) == b'ABC' # BByte = new_primitive_type("unsigned char") assert string(cast(BByte, 42)) == b'*' assert string(cast(BByte, 0)) == b'\x00' BArray = new_array_type(new_pointer_type(BByte), None) a = newp(BArray, [65, 66, 67]) assert type(string(a)) is bytes and string(a) == b'ABC' if 'PY_DOT_PY' not in globals() and sys.version_info < (3,): assert string(a, 8).startswith(b'ABC') # may contain additional garbage def test_string_wchar(): BWChar = new_primitive_type("wchar_t") assert string(cast(BWChar, 42)) == u+'*' assert string(cast(BWChar, 0x4253)) == u+'\u4253' assert string(cast(BWChar, 0)) == u+'\x00' BArray = new_array_type(new_pointer_type(BWChar), None) a = newp(BArray, [u+'A', u+'B', u+'C']) assert type(string(a)) is unicode and string(a) == u+'ABC' if 'PY_DOT_PY' not in globals() and sys.version_info < (3,): try: # may contain additional garbage assert string(a, 8).startswith(u+'ABC') except ValueError: # garbage contains values > 0x10FFFF assert sizeof(BWChar) == 4 def test_string_typeerror(): BShort = new_primitive_type("short") BArray = new_array_type(new_pointer_type(BShort), None) a = newp(BArray, [65, 66, 67]) py.test.raises(TypeError, string, a) def test_bug_convert_to_ptr(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BDouble = new_primitive_type("double") x = cast(BDouble, 42) py.test.raises(TypeError, newp, new_pointer_type(BCharP), x) def test_set_struct_fields(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharArray10 = new_array_type(BCharP, 10) BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)]) p = newp(BStructPtr, None) assert string(p.a1) == b'' p.a1 = b'foo' assert string(p.a1) == b'foo' assert list(p.a1) == [b'f', b'o', b'o'] + [b'\x00'] * 7 p.a1 = [b'x', b'y'] assert string(p.a1) == b'xyo' def test_invalid_function_result_types(): BFunc = new_function_type((), new_void_type()) BArray = new_array_type(new_pointer_type(BFunc), 5) # works new_function_type((), BFunc) # works new_function_type((), new_primitive_type("int")) new_function_type((), new_pointer_type(BFunc)) BUnion = new_union_type("union foo_u") complete_struct_or_union(BUnion, []) py.test.raises(NotImplementedError, new_function_type, (), BUnion) py.test.raises(TypeError, new_function_type, (), BArray) def test_struct_return_in_func(): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BFloat = new_primitive_type("float") BDouble = new_primitive_type("double") BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo_s") complete_struct_or_union(BStruct, [('a1', BChar, -1), ('a2', BShort, -1)]) BFunc10 = new_function_type((BInt,), BStruct) f = cast(BFunc10, _testfunc(10)) s = f(40) assert repr(s) == "" assert s.a1 == bytechr(40) assert s.a2 == 40 * 40 # BStruct11 = new_struct_type("struct test11") complete_struct_or_union(BStruct11, [('a1', BInt, -1), ('a2', BInt, -1)]) BFunc11 = new_function_type((BInt,), BStruct11) f = cast(BFunc11, _testfunc(11)) s = f(40) assert repr(s) == "" assert s.a1 == 40 assert s.a2 == 40 * 40 # BStruct12 = new_struct_type("struct test12") complete_struct_or_union(BStruct12, [('a1', BDouble, -1), ]) BFunc12 = new_function_type((BInt,), BStruct12) f = cast(BFunc12, _testfunc(12)) s = f(40) assert repr(s) == "" assert s.a1 == 40.0 # BStruct13 = new_struct_type("struct test13") complete_struct_or_union(BStruct13, [('a1', BInt, -1), ('a2', BInt, -1), ('a3', BInt, -1)]) BFunc13 = new_function_type((BInt,), BStruct13) f = cast(BFunc13, _testfunc(13)) s = f(40) assert repr(s) == "" assert s.a1 == 40 assert s.a2 == 40 * 40 assert s.a3 == 40 * 40 * 40 # BStruct14 = new_struct_type("struct test14") complete_struct_or_union(BStruct14, [('a1', BFloat, -1), ]) BFunc14 = new_function_type((BInt,), BStruct14) f = cast(BFunc14, _testfunc(14)) s = f(40) assert repr(s) == "" assert s.a1 == 40.0 # BStruct15 = new_struct_type("struct test15") complete_struct_or_union(BStruct15, [('a1', BFloat, -1), ('a2', BInt, -1)]) BFunc15 = new_function_type((BInt,), BStruct15) f = cast(BFunc15, _testfunc(15)) s = f(40) assert repr(s) == "" assert s.a1 == 40.0 assert s.a2 == 40 * 40 # BStruct16 = new_struct_type("struct test16") complete_struct_or_union(BStruct16, [('a1', BFloat, -1), ('a2', BFloat, -1)]) BFunc16 = new_function_type((BInt,), BStruct16) f = cast(BFunc16, _testfunc(16)) s = f(40) assert repr(s) == "" assert s.a1 == 40.0 assert s.a2 == -40.0 # BStruct17 = new_struct_type("struct test17") complete_struct_or_union(BStruct17, [('a1', BInt, -1), ('a2', BFloat, -1)]) BFunc17 = new_function_type((BInt,), BStruct17) f = cast(BFunc17, _testfunc(17)) s = f(40) assert repr(s) == "" assert s.a1 == 40 assert s.a2 == 40.0 * 40.0 # BStruct17Ptr = new_pointer_type(BStruct17) BFunc18 = new_function_type((BStruct17Ptr,), BInt) f = cast(BFunc18, _testfunc(18)) x = f([[40, 2.5]]) assert x == 42 x = f([{'a2': 43.1}]) assert x == 43 def test_cast_with_functionptr(): BFunc = new_function_type((), new_void_type()) BFunc2 = new_function_type((), new_primitive_type("short")) BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BFunc, -1)]) newp(BStructPtr, [cast(BFunc, 0)]) newp(BStructPtr, [cast(BCharP, 0)]) py.test.raises(TypeError, newp, BStructPtr, [cast(BIntP, 0)]) py.test.raises(TypeError, newp, BStructPtr, [cast(BFunc2, 0)]) def test_wchar(): BWChar = new_primitive_type("wchar_t") BInt = new_primitive_type("int") pyuni4 = {1: True, 2: False}[len(u+'\U00012345')] wchar4 = {2: False, 4: True}[sizeof(BWChar)] assert str(cast(BWChar, 0x45)) == "" % ( mandatory_u_prefix,) assert str(cast(BWChar, 0x1234)) == "" % ( mandatory_u_prefix,) if wchar4: if not _hacked_pypy_uni4(): x = cast(BWChar, 0x12345) assert str(x) == "" % ( mandatory_u_prefix,) assert int(x) == 0x12345 else: assert not pyuni4 # BWCharP = new_pointer_type(BWChar) BStruct = new_struct_type("struct foo_s") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BWChar, -1), ('a2', BWCharP, -1)]) s = newp(BStructPtr) s.a1 = u+'\x00' assert s.a1 == u+'\x00' py.test.raises(TypeError, "s.a1 = b'a'") py.test.raises(TypeError, "s.a1 = bytechr(0xFF)") s.a1 = u+'\u1234' assert s.a1 == u+'\u1234' if pyuni4: assert wchar4 s.a1 = u+'\U00012345' assert s.a1 == u+'\U00012345' elif wchar4: if not _hacked_pypy_uni4(): s.a1 = cast(BWChar, 0x12345) assert s.a1 == u+'\ud808\udf45' s.a1 = u+'\ud807\udf44' assert s.a1 == u+'\U00011f44' else: py.test.raises(TypeError, "s.a1 = u+'\U00012345'") # BWCharArray = new_array_type(BWCharP, None) a = newp(BWCharArray, u+'hello \u1234 world') assert len(a) == 14 # including the final null assert string(a) == u+'hello \u1234 world' a[13] = u+'!' assert string(a) == u+'hello \u1234 world!' assert str(a) == repr(a) assert a[6] == u+'\u1234' a[6] = u+'-' assert string(a) == u+'hello - world!' assert str(a) == repr(a) # if wchar4 and not _hacked_pypy_uni4(): u1 = u+'\U00012345\U00012346\U00012347' a = newp(BWCharArray, u1) assert len(a) == 4 assert string(a) == u1 assert len(list(a)) == 4 expected = [u+'\U00012345', u+'\U00012346', u+'\U00012347', unichr(0)] assert list(a) == expected got = [a[i] for i in range(4)] assert got == expected py.test.raises(IndexError, 'a[4]') # w = cast(BWChar, 'a') assert repr(w) == "" % mandatory_u_prefix assert str(w) == repr(w) assert string(w) == u+'a' assert int(w) == ord('a') w = cast(BWChar, 0x1234) assert repr(w) == "" % mandatory_u_prefix assert str(w) == repr(w) assert string(w) == u+'\u1234' assert int(w) == 0x1234 w = cast(BWChar, u+'\u8234') assert repr(w) == "" % mandatory_u_prefix assert str(w) == repr(w) assert string(w) == u+'\u8234' assert int(w) == 0x8234 w = cast(BInt, u+'\u1234') assert repr(w) == "" if wchar4 and not _hacked_pypy_uni4(): w = cast(BWChar, u+'\U00012345') assert repr(w) == "" % ( mandatory_u_prefix,) assert str(w) == repr(w) assert string(w) == u+'\U00012345' assert int(w) == 0x12345 w = cast(BInt, u+'\U00012345') assert repr(w) == "" py.test.raises(TypeError, cast, BInt, u+'') py.test.raises(TypeError, cast, BInt, u+'XX') assert int(cast(BInt, u+'a')) == ord('a') # a = newp(BWCharArray, u+'hello - world') p = cast(BWCharP, a) assert string(p) == u+'hello - world' p[6] = u+'\u2345' assert string(p) == u+'hello \u2345 world' # s = newp(BStructPtr, [u+'\u1234', p]) assert s.a1 == u+'\u1234' assert s.a2 == p assert str(s.a2) == repr(s.a2) assert string(s.a2) == u+'hello \u2345 world' # q = cast(BWCharP, 0) assert str(q) == repr(q) py.test.raises(RuntimeError, string, q) # def cb(p): assert repr(p).startswith("" q = p[0] assert repr(q) == "" q.a1 = 123456 assert p.a1 == 123456 r = cast(BStructPtr, p) assert repr(r[0]).startswith("" assert q.a1 == 123456 def test_nokeepalive_struct(): BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) BStructPtrPtr = new_pointer_type(BStructPtr) complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)]) p = newp(BStructPtr) pp = newp(BStructPtrPtr) pp[0] = p s = pp[0][0] assert repr(s).startswith("" assert sizeof(p) == 28 # BArray = new_array_type(new_pointer_type(BInt), 7) # int[7] p = newp(BArray, None) assert repr(p) == "" assert sizeof(p) == 28 def test_cannot_dereference_void(): BVoidP = new_pointer_type(new_void_type()) p = cast(BVoidP, 123456) py.test.raises(TypeError, "p[0]") p = cast(BVoidP, 0) if 'PY_DOT_PY' in globals(): py.test.skip("NULL crashes early on py.py") py.test.raises(TypeError, "p[0]") def test_iter(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BArray = new_array_type(BIntP, None) # int[] p = newp(BArray, 7) assert list(p) == list(iter(p)) == [0] * 7 # py.test.raises(TypeError, iter, cast(BInt, 5)) py.test.raises(TypeError, iter, cast(BIntP, 123456)) def test_cmp(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BVoidP = new_pointer_type(new_void_type()) p = newp(BIntP, 123) q = cast(BInt, 124) py.test.raises(TypeError, "p < q") py.test.raises(TypeError, "p <= q") assert (p == q) is False assert (p != q) is True py.test.raises(TypeError, "p > q") py.test.raises(TypeError, "p >= q") r = cast(BVoidP, p) assert (p < r) is False assert (p <= r) is True assert (p == r) is True assert (p != r) is False assert (p > r) is False assert (p >= r) is True s = newp(BIntP, 125) assert (p == s) is False assert (p != s) is True assert (p < s) is (p <= s) is (s > p) is (s >= p) assert (p > s) is (p >= s) is (s < p) is (s <= p) assert (p < s) ^ (p > s) def test_buffer(): try: import __builtin__ except ImportError: import builtins as __builtin__ BShort = new_primitive_type("short") s = newp(new_pointer_type(BShort), 100) assert sizeof(s) == size_of_ptr() assert sizeof(BShort) == 2 assert len(buffer(s)) == 2 # BChar = new_primitive_type("char") BCharArray = new_array_type(new_pointer_type(BChar), None) c = newp(BCharArray, b"hi there") # buf = buffer(c) assert repr(buf).startswith('<_cffi_backend.buffer object at 0x') assert bytes(buf) == b"hi there\x00" if sys.version_info < (3,): assert str(buf) == "hi there\x00" assert unicode(buf) == u+"hi there\x00" else: assert str(buf) == repr(buf) # --mb_length-- assert len(buf) == len(b"hi there\x00") # --mb_item-- for i in range(-12, 12): try: expected = b"hi there\x00"[i] except IndexError: py.test.raises(IndexError, "buf[i]") else: assert buf[i] == bitem2bchr(expected) # --mb_slice-- assert buf[:] == b"hi there\x00" for i in range(-12, 12): assert buf[i:] == b"hi there\x00"[i:] assert buf[:i] == b"hi there\x00"[:i] for j in range(-12, 12): assert buf[i:j] == b"hi there\x00"[i:j] # --misc-- assert list(buf) == list(map(bitem2bchr, b"hi there\x00")) # --mb_as_buffer-- if hasattr(__builtin__, 'buffer'): # Python <= 2.7 py.test.raises(TypeError, __builtin__.buffer, c) bf1 = __builtin__.buffer(buf) assert len(bf1) == len(buf) and bf1[3] == "t" if hasattr(__builtin__, 'memoryview'): # Python >= 2.7 py.test.raises(TypeError, memoryview, c) mv1 = memoryview(buf) assert len(mv1) == len(buf) and mv1[3] in (b"t", ord(b"t")) # --mb_ass_item-- expected = list(map(bitem2bchr, b"hi there\x00")) for i in range(-12, 12): try: expected[i] = bytechr(i & 0xff) except IndexError: py.test.raises(IndexError, "buf[i] = bytechr(i & 0xff)") else: buf[i] = bytechr(i & 0xff) assert list(buf) == expected # --mb_ass_slice-- buf[:] = b"hi there\x00" assert list(buf) == list(c) == list(map(bitem2bchr, b"hi there\x00")) py.test.raises(ValueError, 'buf[:] = b"shorter"') py.test.raises(ValueError, 'buf[:] = b"this is much too long!"') buf[4:2] = b"" # no effect, but should work assert buf[:] == b"hi there\x00" buf[:2] = b"HI" assert buf[:] == b"HI there\x00" buf[:2] = b"hi" expected = list(map(bitem2bchr, b"hi there\x00")) x = 0 for i in range(-12, 12): for j in range(-12, 12): start = i if i >= 0 else i + len(buf) stop = j if j >= 0 else j + len(buf) start = max(0, min(len(buf), start)) stop = max(0, min(len(buf), stop)) sample = bytechr(x & 0xff) * (stop - start) x += 1 buf[i:j] = sample expected[i:j] = map(bitem2bchr, sample) assert list(buf) == expected def test_getcname(): BUChar = new_primitive_type("unsigned char") BArray = new_array_type(new_pointer_type(BUChar), 123) assert getcname(BArray, "<-->") == "unsigned char<-->[123]" def test_errno(): BVoid = new_void_type() BFunc5 = new_function_type((), BVoid) f = cast(BFunc5, _testfunc(5)) set_errno(50) f() assert get_errno() == 65 f(); f() assert get_errno() == 95 def test_errno_callback(): if globals().get('PY_DOT_PY') == '2.5': py.test.skip("cannot run this test on py.py with Python 2.5") set_errno(95) def cb(): e = get_errno() set_errno(e - 6) BVoid = new_void_type() BFunc5 = new_function_type((), BVoid) f = callback(BFunc5, cb) f() assert get_errno() == 89 f(); f() assert get_errno() == 77 def test_abi(): assert isinstance(FFI_DEFAULT_ABI, int) def test_cast_to_array(): # not valid in C! extension to get a non-owning BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BArray = new_array_type(BIntP, 3) x = cast(BArray, 0) assert repr(x) == "" def test_cast_invalid(): BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, []) p = cast(new_pointer_type(BStruct), 123456) s = p[0] py.test.raises(TypeError, cast, BStruct, s) def test_bug_float_convertion(): BDouble = new_primitive_type("double") BDoubleP = new_pointer_type(BDouble) py.test.raises(TypeError, newp, BDoubleP, "foobar") def test_bug_delitem(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) x = newp(BCharP) py.test.raises(TypeError, "del x[0]") def test_bug_delattr(): BLong = new_primitive_type("long") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BLong, -1)]) x = newp(new_pointer_type(BStruct)) py.test.raises(AttributeError, "del x.a1") def test_variable_length_struct(): py.test.skip("later") BLong = new_primitive_type("long") BArray = new_array_type(new_pointer_type(BLong), None) BStruct = new_struct_type("struct foo") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BLong, -1), ('a2', BArray, -1)]) assert sizeof(BStruct) == size_of_long() assert alignof(BStruct) == alignof(BLong) # py.test.raises(TypeError, newp, BStructP, None) x = newp(BStructP, 5) assert sizeof(x) == 6 * size_of_long() x[4] = 123 assert x[4] == 123 py.test.raises(IndexError, "x[5]") assert len(x.a2) == 5 # py.test.raises(TypeError, newp, BStructP, [123]) x = newp(BStructP, [123, 5]) assert x.a1 == 123 assert len(x.a2) == 5 assert list(x.a2) == [0] * 5 # x = newp(BStructP, {'a2': 5}) assert x.a1 == 0 assert len(x.a2) == 5 assert list(x.a2) == [0] * 5 # x = newp(BStructP, [123, (4, 5)]) assert x.a1 == 123 assert len(x.a2) == 2 assert list(x.a2) == [4, 5] # x = newp(BStructP, {'a2': (4, 5)}) assert x.a1 == 0 assert len(x.a2) == 2 assert list(x.a2) == [4, 5] def test_autocast_int(): BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) BLongLong = new_primitive_type("long long") BULongLong = new_primitive_type("unsigned long long") BULongLongPtr = new_pointer_type(BULongLong) x = newp(BIntPtr, cast(BInt, 42)) assert x[0] == 42 x = newp(BIntPtr, cast(BLongLong, 42)) assert x[0] == 42 x = newp(BIntPtr, cast(BULongLong, 42)) assert x[0] == 42 x = newp(BULongLongPtr, cast(BInt, 42)) assert x[0] == 42 py.test.raises(OverflowError, newp, BULongLongPtr, cast(BInt, -42)) x = cast(BInt, cast(BInt, 42)) assert int(x) == 42 x = cast(BInt, cast(BLongLong, 42)) assert int(x) == 42 x = cast(BInt, cast(BULongLong, 42)) assert int(x) == 42 x = cast(BULongLong, cast(BInt, 42)) assert int(x) == 42 x = cast(BULongLong, cast(BInt, -42)) assert int(x) == 2 ** 64 - 42 x = cast(BIntPtr, cast(BInt, 42)) assert int(cast(BInt, x)) == 42 def test_autocast_float(): BFloat = new_primitive_type("float") BDouble = new_primitive_type("float") BFloatPtr = new_pointer_type(BFloat) x = newp(BFloatPtr, cast(BDouble, 12.5)) assert x[0] == 12.5 x = cast(BFloat, cast(BDouble, 12.5)) assert float(x) == 12.5 def test_longdouble(): py_py = 'PY_DOT_PY' in globals() BInt = new_primitive_type("int") BLongDouble = new_primitive_type("long double") BLongDoublePtr = new_pointer_type(BLongDouble) BLongDoubleArray = new_array_type(BLongDoublePtr, None) a = newp(BLongDoubleArray, 1) x = a[0] if not py_py: assert repr(x).startswith(" sizeof(new_primitive_type("double")): assert float(lstart) != start assert repr(lstart).startswith("" s = p[0] assert repr(s) == "" a = rawaddressof(BStructPtr, s) assert repr(a).startswith("= (3,): try: import posix, io posix.fdopen = io.open except ImportError: pass # win32 def test_FILE(): if sys.platform == "win32": py.test.skip("testing FILE not implemented") # BFILE = new_struct_type("struct _IO_FILE") BFILEP = new_pointer_type(BFILE) BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BInt = new_primitive_type("int") BFunc = new_function_type((BCharP, BFILEP), BInt, False) BFunc2 = new_function_type((BFILEP, BCharP), BInt, True) ll = find_and_load_library('c') fputs = ll.load_function(BFunc, "fputs") fscanf = ll.load_function(BFunc2, "fscanf") # import posix fdr, fdw = posix.pipe() fr1 = posix.fdopen(fdr, 'rb', 256) fw1 = posix.fdopen(fdw, 'wb', 256) # fw1.write(b"X") res = fputs(b"hello world\n", fw1) assert res >= 0 fw1.flush() # should not be needed # p = newp(new_array_type(BCharP, 100), None) res = fscanf(fr1, b"%s\n", p) assert res == 1 assert string(p) == b"Xhello" fr1.close() fw1.close() def test_FILE_only_for_FILE_arg(): if sys.platform == "win32": py.test.skip("testing FILE not implemented") # B_NOT_FILE = new_struct_type("struct NOT_FILE") B_NOT_FILEP = new_pointer_type(B_NOT_FILE) BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BInt = new_primitive_type("int") BFunc = new_function_type((BCharP, B_NOT_FILEP), BInt, False) ll = find_and_load_library('c') fputs = ll.load_function(BFunc, "fputs") # import posix fdr, fdw = posix.pipe() fr1 = posix.fdopen(fdr, 'r') fw1 = posix.fdopen(fdw, 'w') # e = py.test.raises(TypeError, fputs, b"hello world\n", fw1) assert str(e.value).startswith( "initializer for ctype 'struct NOT_FILE *' must " "be a cdata pointer, not ") def test_FILE_object(): if sys.platform == "win32": py.test.skip("testing FILE not implemented") # BFILE = new_struct_type("FILE") BFILEP = new_pointer_type(BFILE) BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BInt = new_primitive_type("int") BFunc = new_function_type((BCharP, BFILEP), BInt, False) BFunc2 = new_function_type((BFILEP,), BInt, False) ll = find_and_load_library('c') fputs = ll.load_function(BFunc, "fputs") fileno = ll.load_function(BFunc2, "fileno") # import posix fdr, fdw = posix.pipe() fw1 = posix.fdopen(fdw, 'wb', 256) # fw1p = cast(BFILEP, fw1) fw1.write(b"X") fw1.flush() res = fputs(b"hello\n", fw1p) assert res >= 0 res = fileno(fw1p) assert (res == fdw) == (sys.version_info < (3,)) fw1.close() # data = posix.read(fdr, 256) assert data == b"Xhello\n" posix.close(fdr) def test_GetLastError(): if sys.platform != "win32": py.test.skip("GetLastError(): only for Windows") # lib = find_and_load_library('KERNEL32.DLL') BInt = new_primitive_type("int") BVoid = new_void_type() BFunc1 = new_function_type((BInt,), BVoid, False) BFunc2 = new_function_type((), BInt, False) SetLastError = lib.load_function(BFunc1, "SetLastError") GetLastError = lib.load_function(BFunc2, "GetLastError") # SetLastError(42) # a random function that will reset the real GetLastError() to 0 import nt; nt.stat('.') # res = GetLastError() assert res == 42 # SetLastError(2) code, message = getwinerror() assert code == 2 assert message == "The system cannot find the file specified" # code, message = getwinerror(1155) assert code == 1155 assert message == ("No application is associated with the " "specified file for this operation") def test_nonstandard_integer_types(): for typename in ['int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t', 'uint32_t', 'int64_t', 'uint64_t', 'intptr_t', 'uintptr_t', 'ptrdiff_t', 'size_t', 'ssize_t']: new_primitive_type(typename) # works def test_cannot_convert_unicode_to_charp(): BCharP = new_pointer_type(new_primitive_type("char")) BCharArray = new_array_type(BCharP, None) py.test.raises(TypeError, newp, BCharArray, u+'foobar') def test_buffer_keepalive(): BCharP = new_pointer_type(new_primitive_type("char")) BCharArray = new_array_type(BCharP, None) buflist = [] for i in range(20): c = newp(BCharArray, str2bytes("hi there %d" % i)) buflist.append(buffer(c)) import gc; gc.collect() for i in range(20): buf = buflist[i] assert buf[:] == str2bytes("hi there %d\x00" % i) def test_slice(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) assert len(c) == 5 assert repr(c) == "" d = c[1:4] assert len(d) == 3 assert repr(d) == "" d[0] = 123 d[2] = 456 assert c[1] == 123 assert c[3] == 456 assert d[2] == 456 py.test.raises(IndexError, "d[3]") py.test.raises(IndexError, "d[-1]") def test_slice_ptr(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) d = (c+1)[0:2] assert len(d) == 2 assert repr(d) == "" d[1] += 50 assert c[2] == 50 def test_slice_array_checkbounds(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) c[0:5] assert len(c[5:5]) == 0 py.test.raises(IndexError, "c[-1:1]") cp = c + 0 cp[-1:1] def test_nonstandard_slice(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) e = py.test.raises(IndexError, "c[:5]") assert str(e.value) == "slice start must be specified" e = py.test.raises(IndexError, "c[4:]") assert str(e.value) == "slice stop must be specified" e = py.test.raises(IndexError, "c[1:2:3]") assert str(e.value) == "slice with step not supported" e = py.test.raises(IndexError, "c[1:2:1]") assert str(e.value) == "slice with step not supported" e = py.test.raises(IndexError, "c[4:2]") assert str(e.value) == "slice start > stop" e = py.test.raises(IndexError, "c[6:6]") assert str(e.value) == "index too large (expected 6 <= 5)" def test_setslice(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) c[1:3] = [100, 200] assert list(c) == [0, 100, 200, 0, 0] cp = c + 3 cp[-1:1] = [300, 400] assert list(c) == [0, 100, 300, 400, 0] cp[-1:1] = iter([500, 600]) assert list(c) == [0, 100, 500, 600, 0] py.test.raises(ValueError, "cp[-1:1] = [1000]") assert list(c) == [0, 100, 1000, 600, 0] py.test.raises(ValueError, "cp[-1:1] = (700, 800, 900)") assert list(c) == [0, 100, 700, 800, 0] def test_setslice_array(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) d = newp(BIntArray, [10, 20, 30]) c[1:4] = d assert list(c) == [0, 10, 20, 30, 0] # BShortP = new_pointer_type(new_primitive_type("short")) BShortArray = new_array_type(BShortP, None) d = newp(BShortArray, [40, 50]) c[1:3] = d assert list(c) == [0, 40, 50, 30, 0] def test_cdata_name_module_doc(): p = new_primitive_type("signed char") x = cast(p, 17) assert x.__module__ == '_cffi_backend' assert x.__name__ == '' assert hasattr(x, '__doc__') def test_different_types_of_ptr_equality(): BVoidP = new_pointer_type(new_void_type()) BIntP = new_pointer_type(new_primitive_type("int")) x = cast(BVoidP, 12345) assert x == cast(BIntP, 12345) assert x != cast(BIntP, 12344) assert hash(x) == hash(cast(BIntP, 12345)) def test_new_handle(): import _weakref BVoidP = new_pointer_type(new_void_type()) BCharP = new_pointer_type(new_primitive_type("char")) class mylist(list): pass o = mylist([2, 3, 4]) x = newp_handle(BVoidP, o) assert repr(x) == "" assert x assert from_handle(x) is o assert from_handle(cast(BCharP, x)) is o wr = _weakref.ref(o) del o import gc; gc.collect() assert wr() is not None assert from_handle(x) == list((2, 3, 4)) assert from_handle(cast(BCharP, x)) == list((2, 3, 4)) del x for i in range(3): if wr() is not None: import gc; gc.collect() assert wr() is None py.test.raises(RuntimeError, from_handle, cast(BCharP, 0)) def test_new_handle_cycle(): import _weakref BVoidP = new_pointer_type(new_void_type()) class A(object): pass o = A() o.cycle = newp_handle(BVoidP, o) wr = _weakref.ref(o) del o for i in range(3): if wr() is not None: import gc; gc.collect() assert wr() is None def _test_bitfield_details(flag): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BInt = new_primitive_type("int") BUInt = new_primitive_type("unsigned int") BStruct = new_struct_type("struct foo1") complete_struct_or_union(BStruct, [('a', BChar, -1), ('b1', BInt, 9), ('b2', BUInt, 7), ('c', BChar, -1)], -1, -1, -1, flag) if not (flag & SF_MSVC_BITFIELDS): # gcc, any variant assert typeoffsetof(BStruct, 'c') == (BChar, 3) assert sizeof(BStruct) == 4 else: # msvc assert typeoffsetof(BStruct, 'c') == (BChar, 8) assert sizeof(BStruct) == 12 assert alignof(BStruct) == 4 # p = newp(new_pointer_type(BStruct), None) p.a = b'A' p.b1 = -201 p.b2 = 99 p.c = b'\x9D' raw = buffer(p)[:] if sys.byteorder == 'little': if flag & SF_MSVC_BITFIELDS: assert raw == b'A\x00\x00\x007\xC7\x00\x00\x9D\x00\x00\x00' elif flag & SF_GCC_LITTLE_ENDIAN: assert raw == b'A7\xC7\x9D' elif flag & SF_GCC_BIG_ENDIAN: assert raw == b'A\xE3\x9B\x9D' else: raise AssertionError("bad flag") else: if flag & SF_MSVC_BITFIELDS: assert raw == b'A\x00\x00\x00\x00\x00\xC77\x9D\x00\x00\x00' elif flag & SF_GCC_LITTLE_ENDIAN: assert raw == b'A\xC77\x9D' elif flag & SF_GCC_BIG_ENDIAN: assert raw == b'A\x9B\xE3\x9D' else: raise AssertionError("bad flag") # BStruct = new_struct_type("struct foo2") complete_struct_or_union(BStruct, [('a', BChar, -1), ('', BShort, 9), ('c', BChar, -1)], -1, -1, -1, flag) assert typeoffsetof(BStruct, 'c') == (BChar, 4) if flag & SF_MSVC_BITFIELDS: assert sizeof(BStruct) == 6 assert alignof(BStruct) == 2 elif flag & SF_GCC_X86_BITFIELDS: assert sizeof(BStruct) == 5 assert alignof(BStruct) == 1 elif flag & SF_GCC_ARM_BITFIELDS: assert sizeof(BStruct) == 6 assert alignof(BStruct) == 2 else: raise AssertionError("bad flag") # BStruct = new_struct_type("struct foo2") complete_struct_or_union(BStruct, [('a', BChar, -1), ('', BInt, 0), ('', BInt, 0), ('c', BChar, -1)], -1, -1, -1, flag) if flag & SF_MSVC_BITFIELDS: assert typeoffsetof(BStruct, 'c') == (BChar, 1) assert sizeof(BStruct) == 2 assert alignof(BStruct) == 1 elif flag & SF_GCC_X86_BITFIELDS: assert typeoffsetof(BStruct, 'c') == (BChar, 4) assert sizeof(BStruct) == 5 assert alignof(BStruct) == 1 elif flag & SF_GCC_ARM_BITFIELDS: assert typeoffsetof(BStruct, 'c') == (BChar, 4) assert sizeof(BStruct) == 8 assert alignof(BStruct) == 4 else: raise AssertionError("bad flag") SF_MSVC_BITFIELDS = 0x01 SF_GCC_ARM_BITFIELDS = 0x02 SF_GCC_X86_BITFIELDS = 0x10 SF_GCC_BIG_ENDIAN = 0x04 SF_GCC_LITTLE_ENDIAN = 0x40 SF_PACKED = 0x08 def test_bitfield_as_x86_gcc(): _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_LITTLE_ENDIAN) def test_bitfield_as_msvc(): _test_bitfield_details(flag=SF_MSVC_BITFIELDS|SF_GCC_LITTLE_ENDIAN) def test_bitfield_as_arm_gcc(): _test_bitfield_details(flag=SF_GCC_ARM_BITFIELDS|SF_GCC_LITTLE_ENDIAN) def test_bitfield_as_ppc_gcc(): # PowerPC uses the same format as X86, but is big-endian _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN) def test_struct_array_no_length(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BArray = new_array_type(BIntP, None) BStruct = new_struct_type("foo") py.test.raises(TypeError, complete_struct_or_union, BStruct, [('x', BArray), ('y', BInt)]) # BStruct = new_struct_type("foo") complete_struct_or_union(BStruct, [('x', BInt), ('y', BArray)]) assert sizeof(BStruct) == size_of_int() d = BStruct.fields assert len(d) == 2 assert d[0][0] == 'x' assert d[0][1].type is BInt assert d[0][1].offset == 0 assert d[0][1].bitshift == -1 assert d[0][1].bitsize == -1 assert d[1][0] == 'y' assert d[1][1].type is BArray assert d[1][1].offset == size_of_int() assert d[1][1].bitshift == -1 assert d[1][1].bitsize == -1 # p = newp(new_pointer_type(BStruct)) p.x = 42 assert p.x == 42 assert typeof(p.y) is BIntP assert p.y == cast(BIntP, p) + 1 # p = newp(new_pointer_type(BStruct), [100]) assert p.x == 100 # # Tests for # ffi.new("struct_with_var_array *", [field.., [the_array_items..]]) # ffi.new("struct_with_var_array *", [field.., array_size]) plist = [] for i in range(20): if i % 2 == 0: p = newp(new_pointer_type(BStruct), [100, [200, i, 400]]) else: p = newp(new_pointer_type(BStruct), [100, 3]) p.y[1] = i p.y[0] = 200 assert p.y[2] == 0 p.y[2] = 400 plist.append(p) for i in range(20): p = plist[i] assert p.x == 100 assert p.y[0] == 200 assert p.y[1] == i assert p.y[2] == 400 assert list(p.y[0:3]) == [200, i, 400] # # the following assignment works, as it normally would, for any array field p.y = [500, 600] assert list(p.y[0:3]) == [500, 600, 400] # # error cases py.test.raises(TypeError, "p.y = cast(BIntP, 0)") py.test.raises(TypeError, "p.y = 15") py.test.raises(TypeError, "p.y = None") # # accepting this may be specified by the C99 standard, # or a GCC strangeness... BStruct2 = new_struct_type("bar") complete_struct_or_union(BStruct2, [('f', BStruct), ('n', BInt)]) p = newp(new_pointer_type(BStruct2), {'n': 42}) assert p.n == 42 # # more error cases py.test.raises(TypeError, newp, new_pointer_type(BStruct), [100, None]) BArray4 = new_array_type(BIntP, 4) BStruct4 = new_struct_type("test4") complete_struct_or_union(BStruct4, [('a', BArray4)]) # not varsized py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [None]) py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [4]) p = newp(new_pointer_type(BStruct4), [[10, 20, 30]]) assert p.a[0] == 10 assert p.a[1] == 20 assert p.a[2] == 30 assert p.a[3] == 0 def test_struct_array_no_length_explicit_position(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BArray = new_array_type(BIntP, None) BStruct = new_struct_type("foo") complete_struct_or_union(BStruct, [('x', BArray, -1, 0), # actually 3 items ('y', BInt, -1, 12)]) p = newp(new_pointer_type(BStruct), [[10, 20], 30]) assert p.x[0] == 10 assert p.x[1] == 20 assert p.x[2] == 0 assert p.y == 30 p = newp(new_pointer_type(BStruct), {'x': [40], 'y': 50}) assert p.x[0] == 40 assert p.x[1] == 0 assert p.x[2] == 0 assert p.y == 50 p = newp(new_pointer_type(BStruct), {'y': 60}) assert p.x[0] == 0 assert p.x[1] == 0 assert p.x[2] == 0 assert p.y == 60 # # This "should" work too, allocating a larger structure # (a bit strange in this case, but useful in general) plist = [] for i in range(20): p = newp(new_pointer_type(BStruct), [[10, 20, 30, 40, 50, 60, 70]]) plist.append(p) for i in range(20): p = plist[i] assert p.x[0] == 10 assert p.x[1] == 20 assert p.x[2] == 30 assert p.x[3] == 40 == p.y assert p.x[4] == 50 assert p.x[5] == 60 assert p.x[6] == 70 def test_ass_slice(): BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), None) p = newp(BArray, b"foobar") p[2:5] = [b"*", b"Z", b"T"] p[1:3] = b"XY" assert list(p) == [b"f", b"X", b"Y", b"Z", b"T", b"r", b"\x00"] py.test.raises(TypeError, "p[1:5] = u+'XYZT'") py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]") # BUniChar = new_primitive_type("wchar_t") BArray = new_array_type(new_pointer_type(BUniChar), None) p = newp(BArray, u+"foobar") p[2:5] = [u+"*", u+"Z", u+"T"] p[1:3] = u+"XY" assert list(p) == [u+"f", u+"X", u+"Y", u+"Z", u+"T", u+"r", u+"\x00"] py.test.raises(TypeError, "p[1:5] = b'XYZT'") py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]") def test_void_p_arithmetic(): BVoid = new_void_type() BInt = new_primitive_type("intptr_t") p = cast(new_pointer_type(BVoid), 100000) assert int(cast(BInt, p)) == 100000 assert int(cast(BInt, p + 42)) == 100042 assert int(cast(BInt, p - (-42))) == 100042 assert (p + 42) - p == 42 q = cast(new_pointer_type(new_primitive_type("char")), 100000) py.test.raises(TypeError, "p - q") py.test.raises(TypeError, "q - p") py.test.raises(TypeError, "p + cast(new_primitive_type('int'), 42)") py.test.raises(TypeError, "p - cast(new_primitive_type('int'), 42)") def test_sizeof_sliced_array(): BInt = new_primitive_type("int") BArray = new_array_type(new_pointer_type(BInt), 10) p = newp(BArray, None) assert sizeof(p[2:9]) == 7 * sizeof(BInt) def test_packed(): BLong = new_primitive_type("long") BChar = new_primitive_type("char") BShort = new_primitive_type("short") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BLong, -1), ('a2', BChar, -1), ('a3', BShort, -1)], None, -1, -1, SF_PACKED) d = BStruct.fields assert len(d) == 3 assert d[0][0] == 'a1' assert d[0][1].type is BLong assert d[0][1].offset == 0 assert d[0][1].bitshift == -1 assert d[0][1].bitsize == -1 assert d[1][0] == 'a2' assert d[1][1].type is BChar assert d[1][1].offset == sizeof(BLong) assert d[1][1].bitshift == -1 assert d[1][1].bitsize == -1 assert d[2][0] == 'a3' assert d[2][1].type is BShort assert d[2][1].offset == sizeof(BLong) + sizeof(BChar) assert d[2][1].bitshift == -1 assert d[2][1].bitsize == -1 assert sizeof(BStruct) == sizeof(BLong) + sizeof(BChar) + sizeof(BShort) assert alignof(BStruct) == 1 def test_packed_with_bitfields(): if sys.platform == "win32": py.test.skip("testing gcc behavior") BLong = new_primitive_type("long") BChar = new_primitive_type("char") BStruct = new_struct_type("struct foo") py.test.raises(NotImplementedError, complete_struct_or_union, BStruct, [('a1', BLong, 30), ('a2', BChar, 5)], None, -1, -1, SF_PACKED) def test_version(): # this test is here mostly for PyPy assert __version__ == "0.8.2" cffi-0.8.2/c/x.py0000644000076400001440000000101512172012547013755 0ustar arigousers00000000000000import gc import sys import cffi def make_callback(): container = [data] callback = ffi.callback('int()', lambda: len(vars(container))) container.append(callback) # Ref cycle: callback -> lambda (closure) -> container -> callback return callback ffi = cffi.FFI() data = 'something' initial_refcount = sys.getrefcount(data) callback = make_callback() assert sys.getrefcount(data) == initial_refcount + 1 del callback gc.collect() assert sys.getrefcount(data) == initial_refcount # Fails, data is leaked cffi-0.8.2/c/wchar_helper.h0000644000076400001440000000657211776620115015772 0ustar arigousers00000000000000/* * wchar_t helpers */ #if (Py_UNICODE_SIZE == 2) && (SIZEOF_WCHAR_T == 4) # define CONVERT_WCHAR_TO_SURROGATES #endif #ifdef CONVERT_WCHAR_TO_SURROGATES /* Before Python 2.7, PyUnicode_FromWideChar is not able to convert wchar_t values greater than 65535 into two-unicode-characters surrogates. But even the Python 2.7 version doesn't detect wchar_t values that are out of range(1114112), and just returns nonsense. */ static PyObject * _my_PyUnicode_FromWideChar(register const wchar_t *w, Py_ssize_t size) { PyObject *unicode; register Py_ssize_t i; Py_ssize_t alloc; const wchar_t *orig_w; if (w == NULL) { PyErr_BadInternalCall(); return NULL; } alloc = size; orig_w = w; for (i = size; i > 0; i--) { if (*w > 0xFFFF) alloc++; w++; } w = orig_w; unicode = PyUnicode_FromUnicode(NULL, alloc); if (!unicode) return NULL; /* Copy the wchar_t data into the new object */ { register Py_UNICODE *u; u = PyUnicode_AS_UNICODE(unicode); for (i = size; i > 0; i--) { if (((unsigned int)*w) > 0xFFFF) { wchar_t ordinal; if (((unsigned int)*w) > 0x10FFFF) { PyErr_Format(PyExc_ValueError, "wchar_t out of range for " "conversion to unicode: 0x%x", (int)*w); Py_DECREF(unicode); return NULL; } ordinal = *w++; ordinal -= 0x10000; *u++ = 0xD800 | (ordinal >> 10); *u++ = 0xDC00 | (ordinal & 0x3FF); } else *u++ = *w++; } } return unicode; } #else # define _my_PyUnicode_FromWideChar PyUnicode_FromWideChar #endif #define IS_SURROGATE(u) (0xD800 <= (u)[0] && (u)[0] <= 0xDBFF && \ 0xDC00 <= (u)[1] && (u)[1] <= 0xDFFF) #define AS_SURROGATE(u) (0x10000 + (((u)[0] - 0xD800) << 10) + \ ((u)[1] - 0xDC00)) static int _my_PyUnicode_AsSingleWideChar(PyObject *unicode, wchar_t *result) { Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); if (PyUnicode_GET_SIZE(unicode) == 1) { *result = (wchar_t)(u[0]); return 0; } #ifdef CONVERT_WCHAR_TO_SURROGATES if (PyUnicode_GET_SIZE(unicode) == 2 && IS_SURROGATE(u)) { *result = AS_SURROGATE(u); return 0; } #endif return -1; } static Py_ssize_t _my_PyUnicode_SizeAsWideChar(PyObject *unicode) { Py_ssize_t length = PyUnicode_GET_SIZE(unicode); Py_ssize_t result = length; #ifdef CONVERT_WCHAR_TO_SURROGATES Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); Py_ssize_t i; for (i=0; i #include #include /* Round up to FFI_SIZEOF_ARG. */ #define STACK_ARG_SIZE(x) ALIGN(x, FFI_SIZEOF_ARG) /* Perform machine independent initialization of aggregate type specifications. */ static ffi_status initialize_aggregate(/*@out@*/ ffi_type *arg) { ffi_type **ptr; FFI_ASSERT(arg != NULL); /*@-usedef@*/ FFI_ASSERT(arg->elements != NULL); FFI_ASSERT(arg->size == 0); FFI_ASSERT(arg->alignment == 0); ptr = &(arg->elements[0]); while ((*ptr) != NULL) { if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK)) return FFI_BAD_TYPEDEF; /* Perform a sanity check on the argument type */ FFI_ASSERT_VALID_TYPE(*ptr); arg->size = ALIGN(arg->size, (*ptr)->alignment); arg->size += (*ptr)->size; arg->alignment = (arg->alignment > (*ptr)->alignment) ? arg->alignment : (*ptr)->alignment; ptr++; } /* Structure size includes tail padding. This is important for structures that fit in one register on ABIs like the PowerPC64 Linux ABI that right justify small structs in a register. It's also needed for nested structure layout, for example struct A { long a; char b; }; struct B { struct A x; char y; }; should find y at an offset of 2*sizeof(long) and result in a total size of 3*sizeof(long). */ arg->size = ALIGN (arg->size, arg->alignment); if (arg->size == 0) return FFI_BAD_TYPEDEF; else return FFI_OK; /*@=usedef@*/ } /* Perform machine independent ffi_cif preparation, then call machine dependent routine. */ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, ffi_abi abi, unsigned int nargs, /*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type *rtype, /*@dependent@*/ ffi_type **atypes) { unsigned bytes = 0; unsigned int i; ffi_type **ptr; FFI_ASSERT(cif != NULL); FFI_ASSERT((abi > FFI_FIRST_ABI) && (abi <= FFI_DEFAULT_ABI)); cif->abi = abi; cif->arg_types = atypes; cif->nargs = nargs; cif->rtype = rtype; cif->flags = 0; /* Initialize the return type if necessary */ /*@-usedef@*/ if ((cif->rtype->size == 0) && (initialize_aggregate(cif->rtype) != FFI_OK)) return FFI_BAD_TYPEDEF; /*@=usedef@*/ /* Perform a sanity check on the return type */ FFI_ASSERT_VALID_TYPE(cif->rtype); /* x86-64 and s390 stack space allocation is handled in prep_machdep. */ #if !defined M68K && !defined __x86_64__ && !defined S390 /* Make space for the return structure pointer */ if (cif->rtype->type == FFI_TYPE_STRUCT #ifdef _WIN32 && (cif->rtype->size > 8) /* MSVC returns small structs in registers */ #endif #ifdef SPARC && (cif->abi != FFI_V9 || cif->rtype->size > 32) #endif ) bytes = STACK_ARG_SIZE(sizeof(void*)); #endif for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) { /* Initialize any uninitialized aggregate type definitions */ if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK)) return FFI_BAD_TYPEDEF; /* Perform a sanity check on the argument type, do this check after the initialization. */ FFI_ASSERT_VALID_TYPE(*ptr); #if !defined __x86_64__ && !defined S390 #ifdef SPARC if (((*ptr)->type == FFI_TYPE_STRUCT && ((*ptr)->size > 16 || cif->abi != FFI_V9)) || ((*ptr)->type == FFI_TYPE_LONGDOUBLE && cif->abi != FFI_V9)) bytes += sizeof(void*); else #endif { #if !defined(_MSC_VER) && !defined(__MINGW32__) /* Don't know if this is a libffi bug or not. At least on Windows with MSVC, function call parameters are *not* aligned in the same way as structure fields are, they are only aligned in integer boundaries. This doesn't do any harm for cdecl functions and closures, since the caller cleans up the stack, but it is wrong for stdcall functions where the callee cleans. */ /* Add any padding if necessary */ if (((*ptr)->alignment - 1) & bytes) bytes = ALIGN(bytes, (*ptr)->alignment); #endif bytes += STACK_ARG_SIZE((*ptr)->size); } #endif } #ifdef _WIN64 /* Function call needs at least 40 bytes stack size, on win64 AMD64 */ if (bytes < 40) bytes = 40; #endif cif->bytes = bytes; /* Perform machine dependent cif processing */ return ffi_prep_cif_machdep(cif); } cffi-0.8.2/c/libffi_msvc/win64.asm0000644000076400001440000000603011562010250017061 0ustar arigousers00000000000000PUBLIC ffi_call_AMD64 EXTRN __chkstk:NEAR EXTRN ffi_closure_SYSV:NEAR _TEXT SEGMENT ;;; ffi_closure_OUTER will be called with these registers set: ;;; rax points to 'closure' ;;; r11 contains a bit mask that specifies which of the ;;; first four parameters are float or double ;;; ;;; It must move the parameters passed in registers to their stack location, ;;; call ffi_closure_SYSV for the actual work, then return the result. ;;; ffi_closure_OUTER PROC FRAME ;; save actual arguments to their stack space. test r11, 1 jne first_is_float mov QWORD PTR [rsp+8], rcx jmp second first_is_float: movlpd QWORD PTR [rsp+8], xmm0 second: test r11, 2 jne second_is_float mov QWORD PTR [rsp+16], rdx jmp third second_is_float: movlpd QWORD PTR [rsp+16], xmm1 third: test r11, 4 jne third_is_float mov QWORD PTR [rsp+24], r8 jmp forth third_is_float: movlpd QWORD PTR [rsp+24], xmm2 forth: test r11, 8 jne forth_is_float mov QWORD PTR [rsp+32], r9 jmp done forth_is_float: movlpd QWORD PTR [rsp+32], xmm3 done: .ALLOCSTACK 40 sub rsp, 40 .ENDPROLOG mov rcx, rax ; context is first parameter mov rdx, rsp ; stack is second parameter add rdx, 40 ; correct our own area mov rax, ffi_closure_SYSV call rax ; call the real closure function ;; Here, code is missing that handles float return values add rsp, 40 movd xmm0, rax ; In case the closure returned a float. ret 0 ffi_closure_OUTER ENDP ;;; ffi_call_AMD64 stack$ = 0 prepfunc$ = 32 ecif$ = 40 bytes$ = 48 flags$ = 56 rvalue$ = 64 fn$ = 72 ffi_call_AMD64 PROC FRAME mov QWORD PTR [rsp+32], r9 mov QWORD PTR [rsp+24], r8 mov QWORD PTR [rsp+16], rdx mov QWORD PTR [rsp+8], rcx .PUSHREG rbp push rbp .ALLOCSTACK 48 sub rsp, 48 ; 00000030H .SETFRAME rbp, 32 lea rbp, QWORD PTR [rsp+32] .ENDPROLOG mov eax, DWORD PTR bytes$[rbp] add rax, 15 and rax, -16 call __chkstk sub rsp, rax lea rax, QWORD PTR [rsp+32] mov QWORD PTR stack$[rbp], rax mov rdx, QWORD PTR ecif$[rbp] mov rcx, QWORD PTR stack$[rbp] call QWORD PTR prepfunc$[rbp] mov rsp, QWORD PTR stack$[rbp] movlpd xmm3, QWORD PTR [rsp+24] movd r9, xmm3 movlpd xmm2, QWORD PTR [rsp+16] movd r8, xmm2 movlpd xmm1, QWORD PTR [rsp+8] movd rdx, xmm1 movlpd xmm0, QWORD PTR [rsp] movd rcx, xmm0 call QWORD PTR fn$[rbp] ret_int$: cmp DWORD PTR flags$[rbp], 1 ; FFI_TYPE_INT jne ret_float$ mov rcx, QWORD PTR rvalue$[rbp] mov DWORD PTR [rcx], eax jmp SHORT ret_nothing$ ret_float$: cmp DWORD PTR flags$[rbp], 2 ; FFI_TYPE_FLOAT jne SHORT ret_double$ mov rax, QWORD PTR rvalue$[rbp] movlpd QWORD PTR [rax], xmm0 jmp SHORT ret_nothing$ ret_double$: cmp DWORD PTR flags$[rbp], 3 ; FFI_TYPE_DOUBLE jne SHORT ret_int64$ mov rax, QWORD PTR rvalue$[rbp] movlpd QWORD PTR [rax], xmm0 jmp SHORT ret_nothing$ ret_int64$: cmp DWORD PTR flags$[rbp], 12 ; FFI_TYPE_SINT64 jne ret_nothing$ mov rcx, QWORD PTR rvalue$[rbp] mov QWORD PTR [rcx], rax jmp SHORT ret_nothing$ ret_nothing$: xor eax, eax lea rsp, QWORD PTR [rbp+16] pop rbp ret 0 ffi_call_AMD64 ENDP _TEXT ENDS END cffi-0.8.2/c/libffi_msvc/win64.obj0000644000076400001440000000223012176747716017103 0ustar arigousers00000000000000d†w!èQ8.text8Ü P`.data@PÀ.pdata(@@0@.xdata|@@@.debug$S¨@BI÷ÃuH‰L$ëfD$I÷ÃuH‰T$ëfL$I÷ÃuL‰D$ëfT$I÷ÃuL‰L$ ëf\$ Hƒì(H‹ÈH‹ÔHƒÂ(H¸ÿÐHƒÄ(fHnÀÃL‰L$ L‰D$H‰T$H‰L$UHƒì0Hl$ ‹E0HƒÀHƒàðèH+àHD$ H‰EH‹U(H‹MÿU H‹ef\$fI~ÙfT$fI~ÐfL$fH~Êf$fH~ÁÿUHƒ}8uH‹M@‰ë/ƒ}8u H‹E@fëƒ}8u H‹E@fëƒ}8 u H‹M@H‰ë3ÀHe]Ãh ¦ |¼   \XB%SRPñ™^C:\Users\matti\pypy_stuff\cffi\build\temp.win-amd64-2.7\Release\c\libffi_msvc\win64.obj7<Ð xMicrosoft (R) Macro Assembler@comp.id x•ÿÿ.text8.data.pdata.xdata.debug$S¨__chkstk '1| @ffi_closure_SYSVffi_closure_OUTER$xdatasymffi_call_AMD64cffi-0.8.2/c/libffi_msvc/ffi_common.h0000644000076400001440000000410211562010250017673 0ustar arigousers00000000000000/* ----------------------------------------------------------------------- ffi_common.h - Copyright (c) 1996 Red Hat, Inc. Common internal definitions and macros. Only necessary for building libffi. ----------------------------------------------------------------------- */ #ifndef FFI_COMMON_H #define FFI_COMMON_H #ifdef __cplusplus extern "C" { #endif #include #include /* Check for the existence of memcpy. */ #if STDC_HEADERS # include #else # ifndef HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if defined(FFI_DEBUG) #include #endif #ifdef FFI_DEBUG /*@exits@*/ void ffi_assert(/*@temp@*/ char *expr, /*@temp@*/ char *file, int line); void ffi_stop_here(void); void ffi_type_test(/*@temp@*/ /*@out@*/ ffi_type *a, /*@temp@*/ char *file, int line); #define FFI_ASSERT(x) ((x) ? (void)0 : ffi_assert(#x, __FILE__,__LINE__)) #define FFI_ASSERT_AT(x, f, l) ((x) ? 0 : ffi_assert(#x, (f), (l))) #define FFI_ASSERT_VALID_TYPE(x) ffi_type_test (x, __FILE__, __LINE__) #else #define FFI_ASSERT(x) #define FFI_ASSERT_AT(x, f, l) #define FFI_ASSERT_VALID_TYPE(x) #endif #define ALIGN(v, a) (((((size_t) (v))-1) | ((a)-1))+1) /* Perform machine dependent cif processing */ ffi_status ffi_prep_cif_machdep(ffi_cif *cif); /* Extended cif, used in callback from assembly routine */ typedef struct { /*@dependent@*/ ffi_cif *cif; /*@dependent@*/ void *rvalue; /*@dependent@*/ void **avalue; } extended_cif; /* Terse sized type definitions. */ typedef unsigned int UINT8 __attribute__((__mode__(__QI__))); typedef signed int SINT8 __attribute__((__mode__(__QI__))); typedef unsigned int UINT16 __attribute__((__mode__(__HI__))); typedef signed int SINT16 __attribute__((__mode__(__HI__))); typedef unsigned int UINT32 __attribute__((__mode__(__SI__))); typedef signed int SINT32 __attribute__((__mode__(__SI__))); typedef unsigned int UINT64 __attribute__((__mode__(__DI__))); typedef signed int SINT64 __attribute__((__mode__(__DI__))); typedef float FLOAT32; #ifdef __cplusplus } #endif #endif cffi-0.8.2/c/libffi_msvc/ffitarget.h0000644000076400001440000000521311562010250017536 0ustar arigousers00000000000000/* -----------------------------------------------------------------*-C-*- ffitarget.h - Copyright (c) 1996-2003 Red Hat, Inc. Target configuration macros for x86 and x86-64. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------- */ #ifndef LIBFFI_TARGET_H #define LIBFFI_TARGET_H /* ---- System specific configurations ----------------------------------- */ #if defined (X86_64) && defined (__i386__) #undef X86_64 #define X86 #endif /* ---- Generic type definitions ----------------------------------------- */ #ifndef LIBFFI_ASM #ifndef _WIN64 typedef unsigned long ffi_arg; #else typedef unsigned __int64 ffi_arg; #endif typedef signed long ffi_sarg; typedef enum ffi_abi { FFI_FIRST_ABI = 0, /* ---- Intel x86 Win32 ---------- */ FFI_SYSV, #ifndef _WIN64 FFI_STDCALL, #endif /* TODO: Add fastcall support for the sake of completeness */ FFI_DEFAULT_ABI = FFI_SYSV, /* ---- Intel x86 and AMD x86-64 - */ /* #if !defined(X86_WIN32) && (defined(__i386__) || defined(__x86_64__)) */ /* FFI_SYSV, */ /* FFI_UNIX64,*/ /* Unix variants all use the same ABI for x86-64 */ /* #ifdef __i386__ */ /* FFI_DEFAULT_ABI = FFI_SYSV, */ /* #else */ /* FFI_DEFAULT_ABI = FFI_UNIX64, */ /* #endif */ /* #endif */ FFI_LAST_ABI = FFI_DEFAULT_ABI + 1 } ffi_abi; #endif /* ---- Definitions for closures ----------------------------------------- */ #define FFI_CLOSURES 1 #ifdef _WIN64 #define FFI_TRAMPOLINE_SIZE 29 #define FFI_NATIVE_RAW_API 0 #else #define FFI_TRAMPOLINE_SIZE 15 #define FFI_NATIVE_RAW_API 1 /* x86 has native raw api support */ #endif #endif cffi-0.8.2/c/libffi_msvc/win32.c0000644000076400001440000001115111562010250016516 0ustar arigousers00000000000000/* ----------------------------------------------------------------------- win32.S - Copyright (c) 1996, 1998, 2001, 2002 Red Hat, Inc. Copyright (c) 2001 John Beniton Copyright (c) 2002 Ranjit Mathew X86 Foreign Function Interface Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------- */ /* theller: almost verbatim translation from gas syntax to MSVC inline assembler code. */ /* theller: ffi_call_x86 now returns an integer - the difference of the stack pointer before and after the function call. If everything is ok, zero is returned. If stdcall functions are passed the wrong number of arguments, the difference will be nonzero. */ #include #include __declspec(naked) int ffi_call_x86(void (* prepfunc)(char *, extended_cif *), /* 8 */ extended_cif *ecif, /* 12 */ unsigned bytes, /* 16 */ unsigned flags, /* 20 */ unsigned *rvalue, /* 24 */ void (*fn)()) /* 28 */ { _asm { push ebp mov ebp, esp push esi // NEW: this register must be preserved across function calls // XXX SAVE ESP NOW! mov esi, esp // save stack pointer before the call // Make room for all of the new args. mov ecx, [ebp+16] sub esp, ecx // sub esp, bytes mov eax, esp // Place all of the ffi_prep_args in position push [ebp + 12] // ecif push eax call [ebp + 8] // prepfunc // Return stack to previous state and call the function add esp, 8 // FIXME: Align the stack to a 128-bit boundary to avoid // potential performance hits. call [ebp + 28] // Load ecif->cif->abi mov ecx, [ebp + 12] mov ecx, [ecx]ecif.cif mov ecx, [ecx]ecif.cif.abi cmp ecx, FFI_STDCALL je noclean // STDCALL: Remove the space we pushed for the args mov ecx, [ebp + 16] add esp, ecx // CDECL: Caller has already cleaned the stack noclean: // Check that esp has the same value as before! sub esi, esp // Load %ecx with the return type code mov ecx, [ebp + 20] // If the return value pointer is NULL, assume no return value. /* Intel asm is weird. We have to explicitely specify 'DWORD PTR' in the nexr instruction, otherwise only one BYTE will be compared (instead of a DWORD)! */ cmp DWORD PTR [ebp + 24], 0 jne sc_retint // Even if there is no space for the return value, we are // obliged to handle floating-point values. cmp ecx, FFI_TYPE_FLOAT jne sc_noretval // fstp %st(0) fstp st(0) jmp sc_epilogue sc_retint: cmp ecx, FFI_TYPE_INT jne sc_retfloat // # Load %ecx with the pointer to storage for the return value mov ecx, [ebp + 24] mov [ecx + 0], eax jmp sc_epilogue sc_retfloat: cmp ecx, FFI_TYPE_FLOAT jne sc_retdouble // Load %ecx with the pointer to storage for the return value mov ecx, [ebp+24] // fstps (%ecx) fstp DWORD PTR [ecx] jmp sc_epilogue sc_retdouble: cmp ecx, FFI_TYPE_DOUBLE jne sc_retlongdouble // movl 24(%ebp),%ecx mov ecx, [ebp+24] fstp QWORD PTR [ecx] jmp sc_epilogue jmp sc_retlongdouble // avoid warning about unused label sc_retlongdouble: cmp ecx, FFI_TYPE_LONGDOUBLE jne sc_retint64 // Load %ecx with the pointer to storage for the return value mov ecx, [ebp+24] // fstpt (%ecx) fstp QWORD PTR [ecx] /* XXX ??? */ jmp sc_epilogue sc_retint64: cmp ecx, FFI_TYPE_SINT64 jne sc_retstruct // Load %ecx with the pointer to storage for the return value mov ecx, [ebp+24] mov [ecx+0], eax mov [ecx+4], edx sc_retstruct: // Nothing to do! sc_noretval: sc_epilogue: mov eax, esi pop esi // NEW restore: must be preserved across function calls mov esp, ebp pop ebp ret } } cffi-0.8.2/c/libffi_msvc/ffi.h0000644000076400001440000002170611571663036016354 0ustar arigousers00000000000000/* -----------------------------------------------------------------*-C-*- libffi 2.00-beta - Copyright (c) 1996-2003 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------- */ /* ------------------------------------------------------------------- The basic API is described in the README file. The raw API is designed to bypass some of the argument packing and unpacking on architectures for which it can be avoided. The closure API allows interpreted functions to be packaged up inside a C function pointer, so that they can be called as C functions, with no understanding on the client side that they are interpreted. It can also be used in other cases in which it is necessary to package up a user specified parameter and a function pointer as a single function pointer. The closure API must be implemented in order to get its functionality, e.g. for use by gij. Routines are provided to emulate the raw API if the underlying platform doesn't allow faster implementation. More details on the raw and cloure API can be found in: http://gcc.gnu.org/ml/java/1999-q3/msg00138.html and http://gcc.gnu.org/ml/java/1999-q3/msg00174.html -------------------------------------------------------------------- */ #ifndef LIBFFI_H #define LIBFFI_H #ifdef __cplusplus extern "C" { #endif /* Specify which architecture libffi is configured for. */ //XXX #define X86 /* ---- System configuration information --------------------------------- */ #include #ifndef LIBFFI_ASM #include #include /* LONG_LONG_MAX is not always defined (not if STRICT_ANSI, for example). But we can find it either under the correct ANSI name, or under GNU C's internal name. */ #ifdef LONG_LONG_MAX # define FFI_LONG_LONG_MAX LONG_LONG_MAX #else # ifdef LLONG_MAX # define FFI_LONG_LONG_MAX LLONG_MAX # else # ifdef __GNUC__ # define FFI_LONG_LONG_MAX __LONG_LONG_MAX__ # endif # ifdef _MSC_VER # define FFI_LONG_LONG_MAX _I64_MAX # endif # endif #endif #if SCHAR_MAX == 127 # define ffi_type_uchar ffi_type_uint8 # define ffi_type_schar ffi_type_sint8 #else #error "char size not supported" #endif #if SHRT_MAX == 32767 # define ffi_type_ushort ffi_type_uint16 # define ffi_type_sshort ffi_type_sint16 #elif SHRT_MAX == 2147483647 # define ffi_type_ushort ffi_type_uint32 # define ffi_type_sshort ffi_type_sint32 #else #error "short size not supported" #endif #if INT_MAX == 32767 # define ffi_type_uint ffi_type_uint16 # define ffi_type_sint ffi_type_sint16 #elif INT_MAX == 2147483647 # define ffi_type_uint ffi_type_uint32 # define ffi_type_sint ffi_type_sint32 #elif INT_MAX == 9223372036854775807 # define ffi_type_uint ffi_type_uint64 # define ffi_type_sint ffi_type_sint64 #else #error "int size not supported" #endif #define ffi_type_ulong ffi_type_uint64 #define ffi_type_slong ffi_type_sint64 #if LONG_MAX == 2147483647 # if FFI_LONG_LONG_MAX != 9223372036854775807 #error "no 64-bit data type supported" # endif #elif LONG_MAX != 9223372036854775807 #error "long size not supported" #endif /* The closure code assumes that this works on pointers, i.e. a size_t */ /* can hold a pointer. */ typedef struct _ffi_type { size_t size; unsigned short alignment; unsigned short type; /*@null@*/ struct _ffi_type **elements; } ffi_type; /* These are defined in types.c */ extern ffi_type ffi_type_void; extern ffi_type ffi_type_uint8; extern ffi_type ffi_type_sint8; extern ffi_type ffi_type_uint16; extern ffi_type ffi_type_sint16; extern ffi_type ffi_type_uint32; extern ffi_type ffi_type_sint32; extern ffi_type ffi_type_uint64; extern ffi_type ffi_type_sint64; extern ffi_type ffi_type_float; extern ffi_type ffi_type_double; extern ffi_type ffi_type_longdouble; extern ffi_type ffi_type_pointer; typedef enum { FFI_OK = 0, FFI_BAD_TYPEDEF, FFI_BAD_ABI } ffi_status; typedef unsigned FFI_TYPE; typedef struct { ffi_abi abi; unsigned nargs; /*@dependent@*/ ffi_type **arg_types; /*@dependent@*/ ffi_type *rtype; unsigned bytes; unsigned flags; #ifdef FFI_EXTRA_CIF_FIELDS FFI_EXTRA_CIF_FIELDS; #endif } ffi_cif; /* ---- Definitions for the raw API -------------------------------------- */ #ifdef _WIN64 #define FFI_SIZEOF_ARG 8 #else #define FFI_SIZEOF_ARG 4 #endif typedef union { ffi_sarg sint; ffi_arg uint; float flt; char data[FFI_SIZEOF_ARG]; void* ptr; } ffi_raw; void ffi_raw_call (/*@dependent@*/ ffi_cif *cif, void (*fn)(), /*@out@*/ void *rvalue, /*@dependent@*/ ffi_raw *avalue); void ffi_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw); void ffi_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args); size_t ffi_raw_size (ffi_cif *cif); /* This is analogous to the raw API, except it uses Java parameter */ /* packing, even on 64-bit machines. I.e. on 64-bit machines */ /* longs and doubles are followed by an empty 64-bit word. */ void ffi_java_raw_call (/*@dependent@*/ ffi_cif *cif, void (*fn)(), /*@out@*/ void *rvalue, /*@dependent@*/ ffi_raw *avalue); void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw); void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args); size_t ffi_java_raw_size (ffi_cif *cif); /* ---- Definitions for closures ----------------------------------------- */ #if FFI_CLOSURES typedef struct { char tramp[FFI_TRAMPOLINE_SIZE]; ffi_cif *cif; void (*fun)(ffi_cif*,void*,void**,void*); void *user_data; } ffi_closure; void ffi_closure_free(void *); void *ffi_closure_alloc (size_t size, void **code); ffi_status ffi_prep_closure_loc (ffi_closure*, ffi_cif *, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data, void *codeloc); typedef struct { char tramp[FFI_TRAMPOLINE_SIZE]; ffi_cif *cif; #if !FFI_NATIVE_RAW_API /* if this is enabled, then a raw closure has the same layout as a regular closure. We use this to install an intermediate handler to do the transaltion, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); void *this_closure; #endif void (*fun)(ffi_cif*,void*,ffi_raw*,void*); void *user_data; } ffi_raw_closure; ffi_status ffi_prep_raw_closure (ffi_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_raw*,void*), void *user_data); ffi_status ffi_prep_java_raw_closure (ffi_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_raw*,void*), void *user_data); #endif /* FFI_CLOSURES */ /* ---- Public interface definition -------------------------------------- */ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, ffi_abi abi, unsigned int nargs, /*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type *rtype, /*@dependent@*/ ffi_type **atypes); int ffi_call(/*@dependent@*/ ffi_cif *cif, void (*fn)(), /*@out@*/ void *rvalue, /*@dependent@*/ void **avalue); /* Useful for eliminating compiler warnings */ #define FFI_FN(f) ((void (*)())f) /* ---- Definitions shared with assembly code ---------------------------- */ #endif /* If these change, update src/mips/ffitarget.h. */ #define FFI_TYPE_VOID 0 #define FFI_TYPE_INT 1 #define FFI_TYPE_FLOAT 2 #define FFI_TYPE_DOUBLE 3 #if 1 #define FFI_TYPE_LONGDOUBLE 4 #else #define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE #endif #define FFI_TYPE_UINT8 5 #define FFI_TYPE_SINT8 6 #define FFI_TYPE_UINT16 7 #define FFI_TYPE_SINT16 8 #define FFI_TYPE_UINT32 9 #define FFI_TYPE_SINT32 10 #define FFI_TYPE_UINT64 11 #define FFI_TYPE_SINT64 12 #define FFI_TYPE_STRUCT 13 #define FFI_TYPE_POINTER 14 /* This should always refer to the last type code (for sanity checks) */ #define FFI_TYPE_LAST FFI_TYPE_POINTER #ifdef __cplusplus } #endif #endif cffi-0.8.2/c/libffi_msvc/fficonfig.h0000644000076400001440000000563511571663036017545 0ustar arigousers00000000000000/* fficonfig.h. Originally created by configure, now hand_maintained for MSVC. */ /* fficonfig.h. Generated automatically by configure. */ /* fficonfig.h.in. Generated automatically from configure.in by autoheader. */ /* Define this for MSVC, but not for mingw32! */ #ifdef _MSC_VER #define __attribute__(x) /* */ #endif #define alloca _alloca /*----------------------------------------------------------------*/ /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ /* #undef CRAY_STACKSEG_END */ /* Define if you have alloca, as a function or macro. */ #define HAVE_ALLOCA 1 /* Define if you have and it should be used (not on Ultrix). */ /* #define HAVE_ALLOCA_H 1 */ /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you have the memcpy function. */ #define HAVE_MEMCPY 1 /* Define if read-only mmap of a plain file works. */ //#define HAVE_MMAP_FILE 1 /* Define if mmap of /dev/zero works. */ //#define HAVE_MMAP_DEV_ZERO 1 /* Define if mmap with MAP_ANON(YMOUS) works. */ //#define HAVE_MMAP_ANON 1 /* The number of bytes in type double */ #define SIZEOF_DOUBLE 8 /* The number of bytes in type long double */ #define SIZEOF_LONG_DOUBLE 12 /* Define if you have the long double type and it is bigger than a double */ #define HAVE_LONG_DOUBLE 1 /* whether byteorder is bigendian */ /* #undef WORDS_BIGENDIAN */ /* Define if the host machine stores words of multi-word integers in big-endian order. */ /* #undef HOST_WORDS_BIG_ENDIAN */ /* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ #define BYTEORDER 1234 /* Define if your assembler and linker support unaligned PC relative relocs. */ /* #undef HAVE_AS_SPARC_UA_PCREL */ /* Define if your assembler supports .register. */ /* #undef HAVE_AS_REGISTER_PSEUDO_OP */ /* Define if .eh_frame sections should be read-only. */ /* #undef HAVE_RO_EH_FRAME */ /* Define to the flags needed for the .section .eh_frame directive. */ /* #define EH_FRAME_FLAGS "aw" */ /* Define to the flags needed for the .section .eh_frame directive. */ /* #define EH_FRAME_FLAGS "aw" */ /* Define this if you want extra debugging. */ /* #undef FFI_DEBUG */ /* Define this is you do not want support for aggregate types. */ /* #undef FFI_NO_STRUCTS */ /* Define this is you do not want support for the raw API. */ /* #undef FFI_NO_RAW_API */ /* Define this if you are using Purify and want to suppress spurious messages. */ /* #undef USING_PURIFY */ cffi-0.8.2/c/libffi_msvc/ffi.c0000644000076400001440000002747312176747716016370 0ustar arigousers00000000000000/* ----------------------------------------------------------------------- ffi.c - Copyright (c) 1996, 1998, 1999, 2001 Red Hat, Inc. Copyright (c) 2002 Ranjit Mathew Copyright (c) 2002 Bo Thorsen Copyright (c) 2002 Roger Sayle x86 Foreign Function Interface Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------- */ #include #include #include /* ffi_prep_args is called by the assembly routine once stack space has been allocated for the function's arguments */ extern void Py_FatalError(const char *msg); /*@-exportheader@*/ void ffi_prep_args(char *stack, extended_cif *ecif) /*@=exportheader@*/ { register unsigned int i; register void **p_argv; register char *argp; register ffi_type **p_arg; argp = stack; if (ecif->cif->flags == FFI_TYPE_STRUCT) { *(void **) argp = ecif->rvalue; argp += sizeof(void *); } p_argv = ecif->avalue; for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; i != 0; i--, p_arg++) { size_t z; /* Align if necessary */ if ((sizeof(void *) - 1) & (size_t) argp) argp = (char *) ALIGN(argp, sizeof(void *)); z = (*p_arg)->size; if (z < sizeof(int)) { z = sizeof(int); switch ((*p_arg)->type) { case FFI_TYPE_SINT8: *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv); break; case FFI_TYPE_UINT8: *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv); break; case FFI_TYPE_SINT16: *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv); break; case FFI_TYPE_UINT16: *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv); break; case FFI_TYPE_SINT32: *(signed int *) argp = (signed int)*(SINT32 *)(* p_argv); break; case FFI_TYPE_UINT32: *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); break; case FFI_TYPE_STRUCT: *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); break; default: FFI_ASSERT(0); } } #ifdef _WIN64 else if (z > 8) { /* On Win64, if a single argument takes more than 8 bytes, then it is always passed by reference. */ *(void **)argp = *p_argv; z = 8; } #endif else { memcpy(argp, *p_argv, z); } p_argv++; argp += z; } if (argp - stack > ecif->cif->bytes) { Py_FatalError("FFI BUG: not enough stack space for arguments"); } return; } /* Perform machine dependent cif processing */ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) { /* Set the return type flag */ switch (cif->rtype->type) { case FFI_TYPE_VOID: case FFI_TYPE_SINT64: case FFI_TYPE_FLOAT: case FFI_TYPE_DOUBLE: case FFI_TYPE_LONGDOUBLE: cif->flags = (unsigned) cif->rtype->type; break; case FFI_TYPE_STRUCT: /* MSVC returns small structures in registers. Put in cif->flags the value FFI_TYPE_STRUCT only if the structure is big enough; otherwise, put the 4- or 8-bytes integer type. */ if (cif->rtype->size <= 4) cif->flags = FFI_TYPE_INT; else if (cif->rtype->size <= 8) cif->flags = FFI_TYPE_SINT64; else cif->flags = FFI_TYPE_STRUCT; break; case FFI_TYPE_UINT64: #ifdef _WIN64 case FFI_TYPE_POINTER: #endif cif->flags = FFI_TYPE_SINT64; break; default: cif->flags = FFI_TYPE_INT; break; } return FFI_OK; } #ifdef _WIN32 extern int ffi_call_x86(void (*)(char *, extended_cif *), /*@out@*/ extended_cif *, unsigned, unsigned, /*@out@*/ unsigned *, void (*fn)()); #endif #ifdef _WIN64 extern int ffi_call_AMD64(void (*)(char *, extended_cif *), /*@out@*/ extended_cif *, unsigned, unsigned, /*@out@*/ unsigned *, void (*fn)()); #endif int ffi_call(/*@dependent@*/ ffi_cif *cif, void (*fn)(), /*@out@*/ void *rvalue, /*@dependent@*/ void **avalue) { extended_cif ecif; ecif.cif = cif; ecif.avalue = avalue; /* If the return value is a struct and we don't have a return */ /* value address then we need to make one */ if ((rvalue == NULL) && (cif->flags == FFI_TYPE_STRUCT)) { /*@-sysunrecog@*/ ecif.rvalue = alloca(cif->rtype->size); /*@=sysunrecog@*/ } else ecif.rvalue = rvalue; switch (cif->abi) { #if !defined(_WIN64) case FFI_SYSV: case FFI_STDCALL: return ffi_call_x86(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue, fn); break; #else case FFI_SYSV: /*@-usedef@*/ return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue, fn); /*@=usedef@*/ break; #endif default: FFI_ASSERT(0); break; } return -1; /* theller: Hrm. */ } /** private members **/ static void ffi_prep_incoming_args_SYSV (char *stack, void **ret, void** args, ffi_cif* cif); /* This function is jumped to by the trampoline */ #ifdef _WIN64 void * #else static void __fastcall #endif ffi_closure_SYSV (ffi_closure *closure, char *argp) { // this is our return value storage long double res; // our various things... ffi_cif *cif; void **arg_area; unsigned short rtype; void *resp = (void*)&res; void *args = argp + sizeof(void *); cif = closure->cif; arg_area = (void**) alloca (cif->nargs * sizeof (void*)); /* this call will initialize ARG_AREA, such that each * element in that array points to the corresponding * value on the stack; and if the function returns * a structure, it will re-set RESP to point to the * structure return address. */ ffi_prep_incoming_args_SYSV(args, (void**)&resp, arg_area, cif); (closure->fun) (cif, resp, arg_area, closure->user_data); rtype = cif->flags; #if defined(_WIN32) && !defined(_WIN64) #ifdef _MSC_VER /* now, do a generic return based on the value of rtype */ if (rtype == FFI_TYPE_INT) { _asm mov eax, resp ; _asm mov eax, [eax] ; } else if (rtype == FFI_TYPE_FLOAT) { _asm mov eax, resp ; _asm fld DWORD PTR [eax] ; // asm ("flds (%0)" : : "r" (resp) : "st" ); } else if (rtype == FFI_TYPE_DOUBLE) { _asm mov eax, resp ; _asm fld QWORD PTR [eax] ; // asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" ); } else if (rtype == FFI_TYPE_LONGDOUBLE) { // asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" ); } else if (rtype == FFI_TYPE_SINT64) { _asm mov edx, resp ; _asm mov eax, [edx] ; _asm mov edx, [edx + 4] ; // asm ("movl 0(%0),%%eax;" // "movl 4(%0),%%edx" // : : "r"(resp) // : "eax", "edx"); } #else /* now, do a generic return based on the value of rtype */ if (rtype == FFI_TYPE_INT) { asm ("movl (%0),%%eax" : : "r" (resp) : "eax"); } else if (rtype == FFI_TYPE_FLOAT) { asm ("flds (%0)" : : "r" (resp) : "st" ); } else if (rtype == FFI_TYPE_DOUBLE) { asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" ); } else if (rtype == FFI_TYPE_LONGDOUBLE) { asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" ); } else if (rtype == FFI_TYPE_SINT64) { asm ("movl 0(%0),%%eax;" "movl 4(%0),%%edx" : : "r"(resp) : "eax", "edx"); } #endif #endif #ifdef _WIN64 /* The result is returned in rax. This does the right thing for result types except for floats; we have to 'mov xmm0, rax' in the caller to correct this. */ return *(void **)resp; #endif } /*@-exportheader@*/ static void ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, void **avalue, ffi_cif *cif) /*@=exportheader@*/ { register unsigned int i; register void **p_argv; register char *argp; register ffi_type **p_arg; argp = stack; if ( cif->flags == FFI_TYPE_STRUCT ) { *rvalue = *(void **) argp; argp += 4; } p_argv = avalue; for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) { size_t z; /* Align if necessary */ if ((sizeof(char *) - 1) & (size_t) argp) { argp = (char *) ALIGN(argp, sizeof(char*)); } z = (*p_arg)->size; /* because we're little endian, this is what it turns into. */ #ifdef _WIN64 if (z > 8) { /* On Win64, if a single argument takes more than 8 bytes, then it is always passed by reference. */ *p_argv = *((void**) argp); z = 8; } else #endif *p_argv = (void*) argp; p_argv++; argp += z; } return; } /* the cif must already be prep'ed */ extern void ffi_closure_OUTER(); ffi_status ffi_prep_closure_loc (ffi_closure* closure, ffi_cif* cif, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data, void *codeloc) { short bytes; char *tramp; #ifdef _WIN64 int mask = 0; #endif FFI_ASSERT (cif->abi == FFI_SYSV); if (cif->abi == FFI_SYSV) bytes = 0; #if !defined(_WIN64) else if (cif->abi == FFI_STDCALL) bytes = cif->bytes; #endif else return FFI_BAD_ABI; tramp = &closure->tramp[0]; #define BYTES(text) memcpy(tramp, text, sizeof(text)), tramp += sizeof(text)-1 #define POINTER(x) *(void**)tramp = (void*)(x), tramp += sizeof(void*) #define SHORT(x) *(short*)tramp = x, tramp += sizeof(short) #define INT(x) *(int*)tramp = x, tramp += sizeof(int) #ifdef _WIN64 if (cif->nargs >= 1 && (cif->arg_types[0]->type == FFI_TYPE_FLOAT || cif->arg_types[0]->type == FFI_TYPE_DOUBLE)) mask |= 1; if (cif->nargs >= 2 && (cif->arg_types[1]->type == FFI_TYPE_FLOAT || cif->arg_types[1]->type == FFI_TYPE_DOUBLE)) mask |= 2; if (cif->nargs >= 3 && (cif->arg_types[2]->type == FFI_TYPE_FLOAT || cif->arg_types[2]->type == FFI_TYPE_DOUBLE)) mask |= 4; if (cif->nargs >= 4 && (cif->arg_types[3]->type == FFI_TYPE_FLOAT || cif->arg_types[3]->type == FFI_TYPE_DOUBLE)) mask |= 8; /* 41 BB ---- mov r11d,mask */ BYTES("\x41\xBB"); INT(mask); /* 48 B8 -------- mov rax, closure */ BYTES("\x48\xB8"); POINTER(closure); /* 49 BA -------- mov r10, ffi_closure_OUTER */ BYTES("\x49\xBA"); POINTER(ffi_closure_OUTER); /* 41 FF E2 jmp r10 */ BYTES("\x41\xFF\xE2"); #else /* mov ecx, closure */ BYTES("\xb9"); POINTER(closure); /* mov edx, esp */ BYTES("\x8b\xd4"); /* call ffi_closure_SYSV */ BYTES("\xe8"); POINTER((char*)&ffi_closure_SYSV - (tramp + 4)); /* ret bytes */ BYTES("\xc2"); SHORT(bytes); #endif if (tramp - &closure->tramp[0] > FFI_TRAMPOLINE_SIZE) Py_FatalError("FFI_TRAMPOLINE_SIZE too small in " __FILE__); closure->cif = cif; closure->user_data = user_data; closure->fun = fun; return FFI_OK; } cffi-0.8.2/c/libffi_msvc/types.c0000644000076400001440000000674412176747716016766 0ustar arigousers00000000000000/* ----------------------------------------------------------------------- types.c - Copyright (c) 1996, 1998 Red Hat, Inc. Predefined ffi_types needed by libffi. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------- */ #include #include /* Type definitions */ #define FFI_INTEGRAL_TYPEDEF(n, s, a, t) ffi_type ffi_type_##n = { s, a, t, NULL } #define FFI_AGGREGATE_TYPEDEF(n, e) ffi_type ffi_type_##n = { 0, 0, FFI_TYPE_STRUCT, e } /* Size and alignment are fake here. They must not be 0. */ FFI_INTEGRAL_TYPEDEF(void, 1, 1, FFI_TYPE_VOID); FFI_INTEGRAL_TYPEDEF(uint8, 1, 1, FFI_TYPE_UINT8); FFI_INTEGRAL_TYPEDEF(sint8, 1, 1, FFI_TYPE_SINT8); FFI_INTEGRAL_TYPEDEF(uint16, 2, 2, FFI_TYPE_UINT16); FFI_INTEGRAL_TYPEDEF(sint16, 2, 2, FFI_TYPE_SINT16); FFI_INTEGRAL_TYPEDEF(uint32, 4, 4, FFI_TYPE_UINT32); FFI_INTEGRAL_TYPEDEF(sint32, 4, 4, FFI_TYPE_SINT32); FFI_INTEGRAL_TYPEDEF(float, 4, 4, FFI_TYPE_FLOAT); #if defined ALPHA || defined SPARC64 || defined X86_64 || defined S390X \ || defined IA64 || defined _WIN64 FFI_INTEGRAL_TYPEDEF(pointer, 8, 8, FFI_TYPE_POINTER); #else FFI_INTEGRAL_TYPEDEF(pointer, 4, 4, FFI_TYPE_POINTER); #endif #if defined X86 || defined X86_WIN32 || defined ARM || defined M68K FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64); FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64); #elif defined SH FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64); FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64); #else FFI_INTEGRAL_TYPEDEF(uint64, 8, 8, FFI_TYPE_UINT64); FFI_INTEGRAL_TYPEDEF(sint64, 8, 8, FFI_TYPE_SINT64); #endif #if defined X86 || defined X86_WIN32 || defined M68K FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE); FFI_INTEGRAL_TYPEDEF(longdouble, 12, 4, FFI_TYPE_LONGDOUBLE); #elif defined ARM || defined SH || defined POWERPC_AIX || defined POWERPC_DARWIN FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE); FFI_INTEGRAL_TYPEDEF(longdouble, 8, 4, FFI_TYPE_LONGDOUBLE); #elif defined SPARC FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); #ifdef SPARC64 FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE); #else FFI_INTEGRAL_TYPEDEF(longdouble, 16, 8, FFI_TYPE_LONGDOUBLE); #endif #elif defined X86_64 FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE); #else FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); FFI_INTEGRAL_TYPEDEF(longdouble, 8, 8, FFI_TYPE_LONGDOUBLE); #endif cffi-0.8.2/c/minibuffer.h0000644000076400001440000002134512306266214015444 0ustar arigousers00000000000000 /* Implementation of a C object with the 'buffer' or 'memoryview' * interface at C-level (as approriate for the version of Python we're * compiling for), but only a minimal but *consistent* part of the * 'buffer' interface at application level. */ typedef struct { PyObject_HEAD char *mb_data; Py_ssize_t mb_size; PyObject *mb_keepalive; PyObject *mb_weakreflist; /* weakref support */ } MiniBufferObj; static Py_ssize_t mb_length(MiniBufferObj *self) { return self->mb_size; } static PyObject *mb_item(MiniBufferObj *self, Py_ssize_t idx) { if (idx < 0 || idx >= self->mb_size ) { PyErr_SetString(PyExc_IndexError, "buffer index out of range"); return NULL; } return PyBytes_FromStringAndSize(self->mb_data + idx, 1); } static PyObject *mb_slice(MiniBufferObj *self, Py_ssize_t left, Py_ssize_t right) { Py_ssize_t size = self->mb_size; if (left < 0) left = 0; if (right > size) right = size; if (left > right) left = right; return PyBytes_FromStringAndSize(self->mb_data + left, right - left); } static int mb_ass_item(MiniBufferObj *self, Py_ssize_t idx, PyObject *other) { if (idx < 0 || idx >= self->mb_size) { PyErr_SetString(PyExc_IndexError, "buffer assignment index out of range"); return -1; } if (PyBytes_Check(other) && PyBytes_GET_SIZE(other) == 1) { self->mb_data[idx] = PyBytes_AS_STRING(other)[0]; return 0; } else { PyErr_Format(PyExc_TypeError, "must assign a "STR_OR_BYTES " of length 1, not %.200s", Py_TYPE(other)->tp_name); return -1; } } static int mb_ass_slice(MiniBufferObj *self, Py_ssize_t left, Py_ssize_t right, PyObject *other) { const void *buffer; Py_ssize_t buffer_len, count; Py_ssize_t size = self->mb_size; if (PyObject_AsReadBuffer(other, &buffer, &buffer_len) < 0) return -1; if (left < 0) left = 0; if (right > size) right = size; if (left > right) left = right; count = right - left; if (count != buffer_len) { PyErr_SetString(PyExc_ValueError, "right operand length must match slice length"); return -1; } memcpy(self->mb_data + left, buffer, count); return 0; } #if PY_MAJOR_VERSION < 3 static Py_ssize_t mb_getdata(MiniBufferObj *self, Py_ssize_t idx, void **pp) { *pp = self->mb_data; return self->mb_size; } static Py_ssize_t mb_getsegcount(MiniBufferObj *self, Py_ssize_t *lenp) { if (lenp) *lenp = self->mb_size; return 1; } static PyObject *mb_str(MiniBufferObj *self) { /* Python 2: we want str(buffer) to behave like buffer[:], because that's what bytes(buffer) does on Python 3 and there is no way we can prevent this. */ return PyString_FromStringAndSize(self->mb_data, self->mb_size); } #endif static int mb_getbuf(MiniBufferObj *self, Py_buffer *view, int flags) { return PyBuffer_FillInfo(view, NULL, self->mb_data, self->mb_size, /*readonly=*/0, PyBUF_CONTIG | PyBUF_FORMAT); } static PySequenceMethods mb_as_sequence = { (lenfunc)mb_length, /*sq_length*/ (binaryfunc)0, /*sq_concat*/ (ssizeargfunc)0, /*sq_repeat*/ (ssizeargfunc)mb_item, /*sq_item*/ (ssizessizeargfunc)mb_slice, /*sq_slice*/ (ssizeobjargproc)mb_ass_item, /*sq_ass_item*/ (ssizessizeobjargproc)mb_ass_slice, /*sq_ass_slice*/ }; static PyBufferProcs mb_as_buffer = { #if PY_MAJOR_VERSION < 3 (readbufferproc)mb_getdata, (writebufferproc)mb_getdata, (segcountproc)mb_getsegcount, (charbufferproc)mb_getdata, #endif (getbufferproc)mb_getbuf, (releasebufferproc)0, }; static void mb_dealloc(MiniBufferObj *ob) { PyObject_GC_UnTrack(ob); if (ob->mb_weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)ob); Py_XDECREF(ob->mb_keepalive); Py_TYPE(ob)->tp_free((PyObject *)ob); } static int mb_traverse(MiniBufferObj *ob, visitproc visit, void *arg) { Py_VISIT(ob->mb_keepalive); return 0; } static int mb_clear(MiniBufferObj *ob) { Py_CLEAR(ob->mb_keepalive); return 0; } #if PY_MAJOR_VERSION >= 3 /* pfffffffffffff pages of copy-paste from listobject.c */ static PyObject *mb_subscript(MiniBufferObj *self, PyObject *item) { if (PyIndex_Check(item)) { Py_ssize_t i; i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return NULL; if (i < 0) i += self->mb_size; return mb_item(self, i); } else if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelength; if (PySlice_GetIndicesEx(item, self->mb_size, &start, &stop, &step, &slicelength) < 0) return NULL; if (step == 1) return mb_slice(self, start, stop); else { PyErr_SetString(PyExc_TypeError, "buffer doesn't support slicing with step != 1"); return NULL; } } else { PyErr_Format(PyExc_TypeError, "buffer indices must be integers, not %.200s", item->ob_type->tp_name); return NULL; } } static int mb_ass_subscript(MiniBufferObj* self, PyObject* item, PyObject* value) { if (PyIndex_Check(item)) { Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return -1; if (i < 0) i += self->mb_size; return mb_ass_item(self, i, value); } else if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelength; if (PySlice_GetIndicesEx(item, self->mb_size, &start, &stop, &step, &slicelength) < 0) { return -1; } if (step == 1) return mb_ass_slice(self, start, stop, value); else { PyErr_SetString(PyExc_TypeError, "buffer doesn't support slicing with step != 1"); return -1; } } else { PyErr_Format(PyExc_TypeError, "buffer indices must be integers, not %.200s", item->ob_type->tp_name); return -1; } } static PyMappingMethods mb_as_mapping = { (lenfunc)mb_length, /*mp_length*/ (binaryfunc)mb_subscript, /*mp_subscript*/ (objobjargproc)mb_ass_subscript, /*mp_ass_subscript*/ }; #endif #if PY_MAJOR_VERSION >= 3 # define MINIBUF_TPFLAGS 0 #else # define MINIBUF_TPFLAGS (Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_NEWBUFFER) #endif static PyTypeObject MiniBuffer_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.buffer", sizeof(MiniBufferObj), 0, (destructor)mb_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ &mb_as_sequence, /* tp_as_sequence */ #if PY_MAJOR_VERSION < 3 0, /* tp_as_mapping */ #else &mb_as_mapping, /* tp_as_mapping */ #endif 0, /* tp_hash */ 0, /* tp_call */ #if PY_MAJOR_VERSION < 3 (reprfunc)mb_str, /* tp_str */ #else 0, /* tp_str */ #endif PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ &mb_as_buffer, /* tp_as_buffer */ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | MINIBUF_TPFLAGS), /* tp_flags */ 0, /* tp_doc */ (traverseproc)mb_traverse, /* tp_traverse */ (inquiry)mb_clear, /* tp_clear */ 0, /* tp_richcompare */ offsetof(MiniBufferObj, mb_weakreflist), /* tp_weaklistoffset */ }; static PyObject *minibuffer_new(char *data, Py_ssize_t size, PyObject *keepalive) { MiniBufferObj *ob = PyObject_GC_New(MiniBufferObj, &MiniBuffer_Type); if (ob != NULL) { ob->mb_data = data; ob->mb_size = size; ob->mb_keepalive = keepalive; Py_INCREF(keepalive); ob->mb_weakreflist = NULL; PyObject_GC_Track(ob); } return (PyObject *)ob; } cffi-0.8.2/c/malloc_closure.h0000644000076400001440000000643611767034145016333 0ustar arigousers00000000000000/* * This file is from CPython's Modules/_ctypes/malloc_closure.c * and has received some edits. */ #include #ifdef MS_WIN32 #include #else #include #include # if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) # define MAP_ANONYMOUS MAP_ANON # endif #endif /* 'allocate_num_pages' is dynamically adjusted starting from one page. It grows by a factor of PAGE_ALLOCATION_GROWTH_RATE. This is meant to handle both the common case of not needing a lot of pages, and the rare case of needing many of them. Systems in general have a limit of how many mmap'd blocks can be open. */ #define PAGE_ALLOCATION_GROWTH_RATE 1.3 static Py_ssize_t allocate_num_pages = 0; /* #define MALLOC_CLOSURE_DEBUG */ /* enable for some debugging output */ /******************************************************************/ union mmaped_block { ffi_closure closure; union mmaped_block *next; }; static union mmaped_block *free_list = 0; static Py_ssize_t _pagesize = 0; static void more_core(void) { union mmaped_block *item; Py_ssize_t count, i; /* determine the pagesize */ #ifdef MS_WIN32 if (!_pagesize) { SYSTEM_INFO systeminfo; GetSystemInfo(&systeminfo); _pagesize = systeminfo.dwPageSize; } #else if (!_pagesize) { #ifdef _SC_PAGESIZE _pagesize = sysconf(_SC_PAGESIZE); #else _pagesize = getpagesize(); #endif } #endif if (_pagesize <= 0) _pagesize = 4096; /* bump 'allocate_num_pages' */ allocate_num_pages = 1 + ( (Py_ssize_t)(allocate_num_pages * PAGE_ALLOCATION_GROWTH_RATE)); /* calculate the number of mmaped_blocks to allocate */ count = (allocate_num_pages * _pagesize) / sizeof(union mmaped_block); /* allocate a memory block */ #ifdef MS_WIN32 item = (union mmaped_block *)VirtualAlloc(NULL, count * sizeof(union mmaped_block), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (item == NULL) return; #else item = (union mmaped_block *)mmap(NULL, allocate_num_pages * _pagesize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (item == (void *)MAP_FAILED) return; #endif #ifdef MALLOC_CLOSURE_DEBUG printf("block at %p allocated (%ld bytes), %ld mmaped_blocks\n", item, (long)(allocate_num_pages * _pagesize), (long)count); #endif /* put them into the free list */ for (i = 0; i < count; ++i) { item->next = free_list; free_list = item; ++item; } } /******************************************************************/ /* put the item back into the free list */ static void cffi_closure_free(ffi_closure *p) { union mmaped_block *item = (union mmaped_block *)p; item->next = free_list; free_list = item; } /* return one item from the free list, allocating more if needed */ static ffi_closure *cffi_closure_alloc(void) { union mmaped_block *item; if (!free_list) more_core(); if (!free_list) return NULL; item = free_list; free_list = item->next; return &item->closure; } cffi-0.8.2/c/misc_thread.h0000644000076400001440000000077411767641042015611 0ustar arigousers00000000000000#include /* This is only included if GCC doesn't support "__thread" global variables. * See USE__THREAD in _ffi_backend.c. */ static pthread_key_t cffi_tls_key; static void init_errno(void) { (void) pthread_key_create(&cffi_tls_key, NULL); } static void save_errno(void) { intptr_t value = errno; (void) pthread_setspecific(cffi_tls_key, (void *)value); } static void restore_errno(void) { intptr_t value = (intptr_t)pthread_getspecific(cffi_tls_key); errno = value; } cffi-0.8.2/c/_cffi_backend.c0000644000076400001440000054202012306266214016024 0ustar arigousers00000000000000#define PY_SSIZE_T_CLEAN #include #include "structmember.h" #ifdef MS_WIN32 #include #include "misc_win32.h" #include /* for alloca() */ #else #include #include #include #include #include #include #if (defined (__SVR4) && defined (__sun)) || defined(_AIX) # include #endif #endif #include "malloc_closure.h" #if PY_MAJOR_VERSION >= 3 # define STR_OR_BYTES "bytes" # define PyText_Type PyUnicode_Type # define PyText_Check PyUnicode_Check # define PyTextAny_Check PyUnicode_Check # define PyText_FromFormat PyUnicode_FromFormat # define PyText_AsUTF8 _PyUnicode_AsString /* PyUnicode_AsUTF8 in Py3.3 */ # define PyText_AS_UTF8 _PyUnicode_AsString # define PyText_GetSize PyUnicode_GetSize # define PyText_FromString PyUnicode_FromString # define PyText_FromStringAndSize PyUnicode_FromStringAndSize # define PyText_InternInPlace PyUnicode_InternInPlace # define PyIntOrLong_Check PyLong_Check #else # define STR_OR_BYTES "str" # define PyText_Type PyString_Type # define PyText_Check PyString_Check # define PyTextAny_Check(op) (PyString_Check(op) || PyUnicode_Check(op)) # define PyText_FromFormat PyString_FromFormat # define PyText_AsUTF8 PyString_AsString # define PyText_AS_UTF8 PyString_AS_STRING # define PyText_GetSize PyString_Size # define PyText_FromString PyString_FromString # define PyText_FromStringAndSize PyString_FromStringAndSize # define PyText_InternInPlace PyString_InternInPlace # define PyIntOrLong_Check(op) (PyInt_Check(op) || PyLong_Check(op)) #endif #if PY_MAJOR_VERSION >= 3 # define PyInt_FromLong PyLong_FromLong # define PyInt_FromSsize_t PyLong_FromSsize_t # define PyInt_AsSsize_t PyLong_AsSsize_t #endif #if PY_MAJOR_VERSION >= 3 /* This is the default on Python3 and constant has been removed. */ # define Py_TPFLAGS_CHECKTYPES 0 #endif #if PY_MAJOR_VERSION < 3 #define PyCapsule_New(pointer, name, destructor) \ (PyCObject_FromVoidPtr(pointer, destructor)) #endif /************************************************************/ /* base type flag: exactly one of the following: */ #define CT_PRIMITIVE_SIGNED 1 /* signed integer */ #define CT_PRIMITIVE_UNSIGNED 2 /* unsigned integer */ #define CT_PRIMITIVE_CHAR 4 /* char, wchar_t */ #define CT_PRIMITIVE_FLOAT 8 /* float, double, long double */ #define CT_POINTER 16 /* pointer, excluding ptr-to-func */ #define CT_ARRAY 32 /* array */ #define CT_STRUCT 64 /* struct */ #define CT_UNION 128 /* union */ #define CT_FUNCTIONPTR 256 /* pointer to function */ #define CT_VOID 512 /* void */ /* other flags that may also be set in addition to the base flag: */ #define CT_CAST_ANYTHING 1024 /* 'char *' and 'void *' only */ #define CT_PRIMITIVE_FITS_LONG 2048 #define CT_IS_OPAQUE 4096 #define CT_IS_ENUM 8192 #define CT_IS_PTR_TO_OWNED 16384 #define CT_CUSTOM_FIELD_POS 32768 #define CT_IS_LONGDOUBLE 65536 #define CT_IS_BOOL 131072 #define CT_IS_FILE 262144 #define CT_IS_VOID_PTR 524288 #define CT_WITH_VAR_ARRAY 1048576 #define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \ CT_PRIMITIVE_UNSIGNED | \ CT_PRIMITIVE_CHAR | \ CT_PRIMITIVE_FLOAT) typedef struct _ctypedescr { PyObject_VAR_HEAD struct _ctypedescr *ct_itemdescr; /* ptrs and arrays: the item type */ PyObject *ct_stuff; /* structs: dict of the fields arrays: ctypedescr of the ptr type function: tuple(abi, ctres, ctargs..) enum: pair {"name":x},{x:"name"} ptrs: lazily, ctypedescr of array */ void *ct_extra; /* structs: first field (not a ref!) function types: cif_description primitives: prebuilt "cif" object */ PyObject *ct_weakreflist; /* weakref support */ Py_ssize_t ct_size; /* size of instances, or -1 if unknown */ Py_ssize_t ct_length; /* length of arrays, or -1 if unknown; or alignment of primitive and struct types; always -1 for pointers */ int ct_flags; /* CT_xxx flags */ int ct_name_position; /* index in ct_name of where to put a var name */ char ct_name[1]; /* string, e.g. "int *" for pointers to ints */ } CTypeDescrObject; typedef struct { PyObject_HEAD CTypeDescrObject *c_type; char *c_data; PyObject *c_weakreflist; } CDataObject; typedef struct cfieldobject_s { PyObject_HEAD CTypeDescrObject *cf_type; Py_ssize_t cf_offset; short cf_bitshift; /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */ short cf_bitsize; struct cfieldobject_s *cf_next; } CFieldObject; #define BS_REGULAR (-1) /* a regular field, not with bitshift */ #define BS_EMPTY_ARRAY (-2) /* a field which is an array 'type[0]' */ static PyTypeObject CTypeDescr_Type; static PyTypeObject CField_Type; static PyTypeObject CData_Type; static PyTypeObject CDataOwning_Type; static PyTypeObject CDataOwningGC_Type; #define CTypeDescr_Check(ob) (Py_TYPE(ob) == &CTypeDescr_Type) #define CData_Check(ob) (Py_TYPE(ob) == &CData_Type || \ Py_TYPE(ob) == &CDataOwning_Type || \ Py_TYPE(ob) == &CDataOwningGC_Type) #define CDataOwn_Check(ob) (Py_TYPE(ob) == &CDataOwning_Type || \ Py_TYPE(ob) == &CDataOwningGC_Type) typedef union { unsigned char m_char; unsigned short m_short; unsigned int m_int; unsigned long m_long; unsigned long long m_longlong; float m_float; double m_double; long double m_longdouble; } union_alignment; typedef struct { CDataObject head; union_alignment alignment; } CDataObject_casted_primitive; typedef struct { CDataObject head; union_alignment alignment; } CDataObject_own_nolength; typedef struct { CDataObject head; Py_ssize_t length; union_alignment alignment; } CDataObject_own_length; typedef struct { CDataObject head; PyObject *structobj; } CDataObject_own_structptr; typedef struct { ffi_cif cif; /* the following information is used when doing the call: - a buffer of size 'exchange_size' is malloced - the arguments are converted from Python objects to raw data - the i'th raw data is stored at 'buffer + exchange_offset_arg[1+i]' - the call is done - the result is read back from 'buffer + exchange_offset_arg[0]' */ Py_ssize_t exchange_size; Py_ssize_t exchange_offset_arg[1]; } cif_description_t; /* whenever running Python code, the errno is saved in this thread-local variable */ #ifndef MS_WIN32 # ifdef USE__THREAD /* This macro ^^^ is defined by setup.py if it finds that it is syntactically valid to use "__thread" with this C compiler. */ static __thread int cffi_saved_errno = 0; static void save_errno(void) { cffi_saved_errno = errno; } static void restore_errno(void) { errno = cffi_saved_errno; } static void init_errno(void) { } # else # include "misc_thread.h" # endif # define save_errno_only save_errno # define restore_errno_only restore_errno #endif #include "minibuffer.h" #if PY_MAJOR_VERSION >= 3 # include "file_emulator.h" #endif #ifdef HAVE_WCHAR_H # include "wchar_helper.h" #endif /************************************************************/ static CTypeDescrObject * ctypedescr_new(int name_size) { CTypeDescrObject *ct = PyObject_GC_NewVar(CTypeDescrObject, &CTypeDescr_Type, name_size); if (ct == NULL) return NULL; ct->ct_itemdescr = NULL; ct->ct_stuff = NULL; ct->ct_weakreflist = NULL; PyObject_GC_Track(ct); return ct; } static CTypeDescrObject * ctypedescr_new_on_top(CTypeDescrObject *ct_base, const char *extra_text, int extra_position) { int base_name_len = strlen(ct_base->ct_name); int extra_name_len = strlen(extra_text); CTypeDescrObject *ct = ctypedescr_new(base_name_len + extra_name_len + 1); char *p; if (ct == NULL) return NULL; Py_INCREF(ct_base); ct->ct_itemdescr = ct_base; ct->ct_name_position = ct_base->ct_name_position + extra_position; p = ct->ct_name; memcpy(p, ct_base->ct_name, ct_base->ct_name_position); p += ct_base->ct_name_position; memcpy(p, extra_text, extra_name_len); p += extra_name_len; memcpy(p, ct_base->ct_name + ct_base->ct_name_position, base_name_len - ct_base->ct_name_position + 1); return ct; } static PyObject * ctypedescr_repr(CTypeDescrObject *ct) { return PyText_FromFormat("", ct->ct_name); } static void ctypedescr_dealloc(CTypeDescrObject *ct) { PyObject_GC_UnTrack(ct); if (ct->ct_weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) ct); Py_XDECREF(ct->ct_itemdescr); Py_XDECREF(ct->ct_stuff); if (ct->ct_flags & CT_FUNCTIONPTR) PyObject_Free(ct->ct_extra); Py_TYPE(ct)->tp_free((PyObject *)ct); } static int ctypedescr_traverse(CTypeDescrObject *ct, visitproc visit, void *arg) { Py_VISIT(ct->ct_itemdescr); Py_VISIT(ct->ct_stuff); return 0; } static int ctypedescr_clear(CTypeDescrObject *ct) { Py_CLEAR(ct->ct_itemdescr); Py_CLEAR(ct->ct_stuff); return 0; } static PyObject *nosuchattr(const char *attr) { PyErr_SetString(PyExc_AttributeError, attr); return NULL; } static PyObject *ctypeget_kind(CTypeDescrObject *ct, void *context) { char *result; if (ct->ct_flags & CT_PRIMITIVE_ANY) { if (ct->ct_flags & CT_IS_ENUM) result = "enum"; else result = "primitive"; } else if (ct->ct_flags & CT_POINTER) { result = "pointer"; } else if (ct->ct_flags & CT_ARRAY) { result = "array"; } else if (ct->ct_flags & CT_VOID) { result = "void"; } else if (ct->ct_flags & CT_STRUCT) { result = "struct"; } else if (ct->ct_flags & CT_UNION) { result = "union"; } else if (ct->ct_flags & CT_FUNCTIONPTR) { result = "function"; } else result = "?"; return PyText_FromString(result); } static PyObject *ctypeget_cname(CTypeDescrObject *ct, void *context) { return PyText_FromString(ct->ct_name); } static PyObject *ctypeget_item(CTypeDescrObject *ct, void *context) { if (ct->ct_flags & (CT_POINTER | CT_ARRAY)) { Py_INCREF(ct->ct_itemdescr); return (PyObject *)ct->ct_itemdescr; } return nosuchattr("item"); } static PyObject *ctypeget_length(CTypeDescrObject *ct, void *context) { if (ct->ct_flags & CT_ARRAY) { if (ct->ct_length >= 0) { return PyInt_FromSsize_t(ct->ct_length); } else { Py_INCREF(Py_None); return Py_None; } } return nosuchattr("length"); } static PyObject * get_field_name(CTypeDescrObject *ct, CFieldObject *cf); /* forward */ static PyObject *ctypeget_fields(CTypeDescrObject *ct, void *context) { if (ct->ct_flags & (CT_STRUCT | CT_UNION)) { if (!(ct->ct_flags & CT_IS_OPAQUE)) { CFieldObject *cf; PyObject *res = PyList_New(0); if (res == NULL) return NULL; for (cf = (CFieldObject *)ct->ct_extra; cf != NULL; cf = cf->cf_next) { PyObject *o = PyTuple_Pack(2, get_field_name(ct, cf), (PyObject *)cf); int err = (o != NULL) ? PyList_Append(res, o) : -1; Py_XDECREF(o); if (err < 0) { Py_DECREF(res); return NULL; } } return res; } else { Py_INCREF(Py_None); return Py_None; } } return nosuchattr("fields"); } static PyObject *ctypeget_args(CTypeDescrObject *ct, void *context) { if (ct->ct_flags & CT_FUNCTIONPTR) { PyObject *t = ct->ct_stuff; return PyTuple_GetSlice(t, 2, PyTuple_GET_SIZE(t)); } return nosuchattr("args"); } static PyObject *ctypeget_result(CTypeDescrObject *ct, void *context) { if (ct->ct_flags & CT_FUNCTIONPTR) { PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1); Py_XINCREF(res); return res; } return nosuchattr("result"); } static PyObject *ctypeget_ellipsis(CTypeDescrObject *ct, void *context) { if (ct->ct_flags & CT_FUNCTIONPTR) { PyObject *res = ct->ct_extra ? Py_False : Py_True; Py_INCREF(res); return res; } return nosuchattr("ellipsis"); } static PyObject *ctypeget_abi(CTypeDescrObject *ct, void *context) { if (ct->ct_flags & CT_FUNCTIONPTR) { PyObject *res = PyTuple_GetItem(ct->ct_stuff, 0); Py_XINCREF(res); return res; } return nosuchattr("abi"); } static PyObject *ctypeget_elements(CTypeDescrObject *ct, void *context) { if (ct->ct_flags & CT_IS_ENUM) { PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1); if (res) res = PyDict_Copy(res); return res; } return nosuchattr("elements"); } static PyObject *ctypeget_relements(CTypeDescrObject *ct, void *context) { if (ct->ct_flags & CT_IS_ENUM) { PyObject *res = PyTuple_GetItem(ct->ct_stuff, 0); if (res) res = PyDict_Copy(res); return res; } return nosuchattr("relements"); } static PyGetSetDef ctypedescr_getsets[] = { {"kind", (getter)ctypeget_kind, NULL, "kind"}, {"cname", (getter)ctypeget_cname, NULL, "C name"}, {"item", (getter)ctypeget_item, NULL, "pointer to, or array of"}, {"length", (getter)ctypeget_length, NULL, "array length or None"}, {"fields", (getter)ctypeget_fields, NULL, "struct or union fields"}, {"args", (getter)ctypeget_args, NULL, "function argument types"}, {"result", (getter)ctypeget_result, NULL, "function result type"}, {"ellipsis", (getter)ctypeget_ellipsis, NULL, "function has '...'"}, {"abi", (getter)ctypeget_abi, NULL, "function ABI"}, {"elements", (getter)ctypeget_elements, NULL, "enum elements"}, {"relements", (getter)ctypeget_relements, NULL, "enum elements, reverse"}, {NULL} /* sentinel */ }; static PyObject * ctypedescr_dir(PyObject *ct, PyObject *noarg) { int err; struct PyGetSetDef *gsdef; PyObject *res = PyList_New(0); if (res == NULL) return NULL; for (gsdef = ctypedescr_getsets; gsdef->name; gsdef++) { PyObject *x = PyObject_GetAttrString(ct, gsdef->name); if (x == NULL) { PyErr_Clear(); } else { Py_DECREF(x); x = PyText_FromString(gsdef->name); err = (x != NULL) ? PyList_Append(res, x) : -1; Py_XDECREF(x); if (err < 0) { Py_DECREF(res); return NULL; } } } return res; } static PyMethodDef ctypedescr_methods[] = { {"__dir__", ctypedescr_dir, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; static PyTypeObject CTypeDescr_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CTypeDescr", offsetof(CTypeDescrObject, ct_name), sizeof(char), (destructor)ctypedescr_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)ctypedescr_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)ctypedescr_traverse, /* tp_traverse */ (inquiry)ctypedescr_clear, /* tp_clear */ 0, /* tp_richcompare */ offsetof(CTypeDescrObject, ct_weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ ctypedescr_methods, /* tp_methods */ 0, /* tp_members */ ctypedescr_getsets, /* tp_getset */ }; /************************************************************/ static PyObject * get_field_name(CTypeDescrObject *ct, CFieldObject *cf) { Py_ssize_t i = 0; PyObject *d_key, *d_value; while (PyDict_Next(ct->ct_stuff, &i, &d_key, &d_value)) { if (d_value == (PyObject *)cf) return d_key; } Py_FatalError("_cffi_backend: get_field_name()"); return NULL; } static void cfield_dealloc(CFieldObject *cf) { Py_DECREF(cf->cf_type); PyObject_Del(cf); } #undef OFF #define OFF(x) offsetof(CFieldObject, x) static PyMemberDef cfield_members[] = { {"type", T_OBJECT, OFF(cf_type), READONLY}, {"offset", T_PYSSIZET, OFF(cf_offset), READONLY}, {"bitshift", T_SHORT, OFF(cf_bitshift), READONLY}, {"bitsize", T_SHORT, OFF(cf_bitsize), READONLY}, {NULL} /* Sentinel */ }; #undef OFF static PyTypeObject CField_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CField", sizeof(CFieldObject), 0, (destructor)cfield_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ cfield_members, /* tp_members */ }; /************************************************************/ static int CDataObject_Or_PyFloat_Check(PyObject *ob) { return (PyFloat_Check(ob) || (CData_Check(ob) && (((CDataObject *)ob)->c_type->ct_flags & CT_PRIMITIVE_FLOAT))); } static PY_LONG_LONG _my_PyLong_AsLongLong(PyObject *ob) { /* (possibly) convert and cast a Python object to a long long. Like PyLong_AsLongLong(), this version accepts a Python int too, and does convertions from other types of objects. The difference is that this version refuses floats. */ #if PY_MAJOR_VERSION < 3 if (PyInt_Check(ob)) { return PyInt_AS_LONG(ob); } else #endif if (PyLong_Check(ob)) { return PyLong_AsLongLong(ob); } else { PyObject *io; PY_LONG_LONG res; PyNumberMethods *nb = ob->ob_type->tp_as_number; if (CDataObject_Or_PyFloat_Check(ob) || nb == NULL || nb->nb_int == NULL) { PyErr_SetString(PyExc_TypeError, "an integer is required"); return -1; } io = (*nb->nb_int) (ob); if (io == NULL) return -1; if (PyIntOrLong_Check(io)) { res = _my_PyLong_AsLongLong(io); } else { PyErr_SetString(PyExc_TypeError, "integer conversion failed"); res = -1; } Py_DECREF(io); return res; } } static unsigned PY_LONG_LONG _my_PyLong_AsUnsignedLongLong(PyObject *ob, int strict) { /* (possibly) convert and cast a Python object to an unsigned long long. Like PyLong_AsLongLong(), this version accepts a Python int too, and does convertions from other types of objects. If 'strict', complains with OverflowError and refuses floats. If '!strict', rounds floats and masks the result. */ #if PY_MAJOR_VERSION < 3 if (PyInt_Check(ob)) { long value1 = PyInt_AS_LONG(ob); if (strict && value1 < 0) goto negative; return (unsigned PY_LONG_LONG)(PY_LONG_LONG)value1; } else #endif if (PyLong_Check(ob)) { if (strict) { if (_PyLong_Sign(ob) < 0) goto negative; return PyLong_AsUnsignedLongLong(ob); } else { return PyLong_AsUnsignedLongLongMask(ob); } } else { PyObject *io; unsigned PY_LONG_LONG res; PyNumberMethods *nb = ob->ob_type->tp_as_number; if ((strict && CDataObject_Or_PyFloat_Check(ob)) || nb == NULL || nb->nb_int == NULL) { PyErr_SetString(PyExc_TypeError, "an integer is required"); return (unsigned PY_LONG_LONG)-1; } io = (*nb->nb_int) (ob); if (io == NULL) return (unsigned PY_LONG_LONG)-1; if (PyIntOrLong_Check(io)) { res = _my_PyLong_AsUnsignedLongLong(io, strict); } else { PyErr_SetString(PyExc_TypeError, "integer conversion failed"); res = (unsigned PY_LONG_LONG)-1; } Py_DECREF(io); return res; } negative: PyErr_SetString(PyExc_OverflowError, "can't convert negative number to unsigned"); return (unsigned PY_LONG_LONG)-1; } static PY_LONG_LONG read_raw_signed_data(char *target, int size) { if (size == sizeof(signed char)) return *((signed char*)target); else if (size == sizeof(short)) return *((short*)target); else if (size == sizeof(int)) return *((int*)target); else if (size == sizeof(long)) return *((long*)target); else if (size == sizeof(PY_LONG_LONG)) return *((PY_LONG_LONG*)target); else { Py_FatalError("read_raw_signed_data: bad integer size"); return 0; } } static unsigned PY_LONG_LONG read_raw_unsigned_data(char *target, int size) { if (size == sizeof(unsigned char)) return *((unsigned char*)target); else if (size == sizeof(unsigned short)) return *((unsigned short*)target); else if (size == sizeof(unsigned int)) return *((unsigned int*)target); else if (size == sizeof(unsigned long)) return *((unsigned long*)target); else if (size == sizeof(unsigned PY_LONG_LONG)) return *((unsigned PY_LONG_LONG*)target); else { Py_FatalError("read_raw_unsigned_data: bad integer size"); return 0; } } static void write_raw_integer_data(char *target, unsigned PY_LONG_LONG source, int size) { if (size == sizeof(unsigned char)) *((unsigned char*)target) = (unsigned char)source; else if (size == sizeof(unsigned short)) *((unsigned short*)target) = (unsigned short)source; else if (size == sizeof(unsigned int)) *((unsigned int*)target) = (unsigned int)source; else if (size == sizeof(unsigned long)) *((unsigned long*)target) = (unsigned long)source; else if (size == sizeof(unsigned PY_LONG_LONG)) *((unsigned PY_LONG_LONG*)target) = source; else Py_FatalError("write_raw_integer_data: bad integer size"); } static double read_raw_float_data(char *target, int size) { if (size == sizeof(float)) return *((float*)target); else if (size == sizeof(double)) return *((double*)target); else { Py_FatalError("read_raw_float_data: bad float size"); return 0; } } static long double read_raw_longdouble_data(char *target) { return *((long double*)target); } static void write_raw_float_data(char *target, double source, int size) { if (size == sizeof(float)) *((float*)target) = (float)source; else if (size == sizeof(double)) *((double*)target) = source; else Py_FatalError("write_raw_float_data: bad float size"); } static void write_raw_longdouble_data(char *target, long double source) { *((long double*)target) = source; } static PyObject * new_simple_cdata(char *data, CTypeDescrObject *ct) { CDataObject *cd = PyObject_New(CDataObject, &CData_Type); if (cd == NULL) return NULL; Py_INCREF(ct); cd->c_data = data; cd->c_type = ct; cd->c_weakreflist = NULL; return (PyObject *)cd; } static CDataObject *_new_casted_primitive(CTypeDescrObject *ct); /*forward*/ static PyObject * convert_to_object(char *data, CTypeDescrObject *ct) { if (!(ct->ct_flags & CT_PRIMITIVE_ANY)) { /* non-primitive types (check done just for performance) */ if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { char *ptrdata = *(char **)data; /*READ(data, sizeof(char *))*/ return new_simple_cdata(ptrdata, ct); } else if (ct->ct_flags & CT_IS_OPAQUE) { PyErr_Format(PyExc_TypeError, "cdata '%s' is opaque", ct->ct_name); return NULL; } else if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { return new_simple_cdata(data, ct); } else if (ct->ct_flags & CT_ARRAY) { if (ct->ct_length < 0) { /* we can't return a here, because we don't know the length to give it. As a compromize, returns in this case. */ ct = (CTypeDescrObject *)ct->ct_stuff; } return new_simple_cdata(data, ct); } } else if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { PY_LONG_LONG value; /*READ(data, ct->ct_size)*/ value = read_raw_signed_data(data, ct->ct_size); if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) return PyInt_FromLong((long)value); else return PyLong_FromLongLong(value); } else if (ct->ct_flags & CT_PRIMITIVE_UNSIGNED) { unsigned PY_LONG_LONG value; /*READ(data, ct->ct_size)*/ value = read_raw_unsigned_data(data, ct->ct_size); if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) return PyInt_FromLong((long)value); else return PyLong_FromUnsignedLongLong(value); } else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { /*READ(data, ct->ct_size)*/ if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) { double value = read_raw_float_data(data, ct->ct_size); return PyFloat_FromDouble(value); } else { long double value = read_raw_longdouble_data(data); CDataObject *cd = _new_casted_primitive(ct); if (cd != NULL) write_raw_longdouble_data(cd->c_data, value); return (PyObject *)cd; } } else if (ct->ct_flags & CT_PRIMITIVE_CHAR) { /*READ(data, ct->ct_size)*/ if (ct->ct_size == sizeof(char)) return PyBytes_FromStringAndSize(data, 1); #ifdef HAVE_WCHAR_H else return _my_PyUnicode_FromWideChar((wchar_t *)data, 1); #endif } PyErr_Format(PyExc_SystemError, "convert_to_object: '%s'", ct->ct_name); return NULL; } static PyObject * convert_to_object_bitfield(char *data, CFieldObject *cf) { CTypeDescrObject *ct = cf->cf_type; /*READ(data, ct->ct_size)*/ if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { unsigned PY_LONG_LONG value, valuemask, shiftforsign; PY_LONG_LONG result; value = (unsigned PY_LONG_LONG)read_raw_signed_data(data, ct->ct_size); valuemask = (1ULL << cf->cf_bitsize) - 1ULL; shiftforsign = 1ULL << (cf->cf_bitsize - 1); value = ((value >> cf->cf_bitshift) + shiftforsign) & valuemask; result = ((PY_LONG_LONG)value) - (PY_LONG_LONG)shiftforsign; if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) return PyInt_FromLong((long)result); else return PyLong_FromLongLong(result); } else { unsigned PY_LONG_LONG value, valuemask; value = read_raw_unsigned_data(data, ct->ct_size); valuemask = (1ULL << cf->cf_bitsize) - 1ULL; value = (value >> cf->cf_bitshift) & valuemask; if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) return PyInt_FromLong((long)value); else return PyLong_FromUnsignedLongLong(value); } } static int _convert_overflow(PyObject *init, const char *ct_name) { PyObject *s; if (PyErr_Occurred()) /* already an exception pending */ return -1; s = PyObject_Str(init); if (s == NULL) return -1; PyErr_Format(PyExc_OverflowError, "integer %s does not fit '%s'", PyText_AS_UTF8(s), ct_name); Py_DECREF(s); return -1; } static int _convert_to_char(PyObject *init) { if (PyBytes_Check(init) && PyBytes_GET_SIZE(init) == 1) { return (unsigned char)(PyBytes_AS_STRING(init)[0]); } if (CData_Check(init) && (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && (((CDataObject *)init)->c_type->ct_size == sizeof(char))) { char *data = ((CDataObject *)init)->c_data; /*READ(data, 1)*/ return *(unsigned char *)data; } PyErr_Format(PyExc_TypeError, "initializer for ctype 'char' must be a "STR_OR_BYTES " of length 1, not %.200s", Py_TYPE(init)->tp_name); return -1; } #ifdef HAVE_WCHAR_H static wchar_t _convert_to_wchar_t(PyObject *init) { if (PyUnicode_Check(init)) { wchar_t ordinal; if (_my_PyUnicode_AsSingleWideChar(init, &ordinal) == 0) return ordinal; } if (CData_Check(init) && (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && (((CDataObject *)init)->c_type->ct_size == sizeof(wchar_t))) { char *data = ((CDataObject *)init)->c_data; /*READ(data, sizeof(wchar_t))*/ return *(wchar_t *)data; } PyErr_Format(PyExc_TypeError, "initializer for ctype 'wchar_t' must be a unicode string " "of length 1, not %.200s", Py_TYPE(init)->tp_name); return (wchar_t)-1; } #endif static int _convert_error(PyObject *init, const char *ct_name, const char *expected) { if (CData_Check(init)) PyErr_Format(PyExc_TypeError, "initializer for ctype '%s' must be a %s, " "not cdata '%s'", ct_name, expected, ((CDataObject *)init)->c_type->ct_name); else PyErr_Format(PyExc_TypeError, "initializer for ctype '%s' must be a %s, " "not %.200s", ct_name, expected, Py_TYPE(init)->tp_name); return -1; } static int /* forward */ convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init); static int /* forward */ convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init); static Py_ssize_t get_new_array_length(PyObject **pvalue) { PyObject *value = *pvalue; if (PyList_Check(value) || PyTuple_Check(value)) { return PySequence_Fast_GET_SIZE(value); } else if (PyBytes_Check(value)) { /* from a string, we add the null terminator */ return PyBytes_GET_SIZE(value) + 1; } else if (PyUnicode_Check(value)) { /* from a unicode, we add the null terminator */ return _my_PyUnicode_SizeAsWideChar(value) + 1; } else { Py_ssize_t explicitlength; explicitlength = PyNumber_AsSsize_t(value, PyExc_OverflowError); if (explicitlength < 0) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "negative array length"); return -1; } *pvalue = Py_None; return explicitlength; } } static int convert_field_from_object(char *data, CFieldObject *cf, PyObject *value) { data += cf->cf_offset; if (cf->cf_bitshift >= 0) return convert_from_object_bitfield(data, cf, value); else return convert_from_object(data, cf->cf_type, value); } static int convert_vfield_from_object(char *data, CFieldObject *cf, PyObject *value, Py_ssize_t *optvarsize) { /* a special case for var-sized C99 arrays */ if ((cf->cf_type->ct_flags & CT_ARRAY) && cf->cf_type->ct_size < 0) { Py_ssize_t varsizelength = get_new_array_length(&value); if (varsizelength < 0) return -1; if (optvarsize != NULL) { /* in this mode, the only purpose of this function is to compute the real size of the structure from a var-sized C99 array */ Py_ssize_t size, itemsize; assert(data == NULL); itemsize = cf->cf_type->ct_itemdescr->ct_size; size = cf->cf_offset + itemsize * varsizelength; if (size < 0 || ((size - cf->cf_offset) / itemsize) != varsizelength) { PyErr_SetString(PyExc_OverflowError, "array size would overflow a Py_ssize_t"); return -1; } if (size > *optvarsize) *optvarsize = size; return 0; } /* if 'value' was only an integer, get_new_array_length() returns it and convert 'value' to be None. Detect if this was the case, and if so, stop here, leaving the content uninitialized (it should be zero-initialized from somewhere else). */ if (value == Py_None) return 0; } if (optvarsize == NULL) return convert_field_from_object(data, cf, value); else return 0; } static int convert_array_from_object(char *data, CTypeDescrObject *ct, PyObject *init) { /* used by convert_from_object(), and also to decode lists/tuples/unicodes passed as function arguments. 'ct' is an CT_ARRAY in the first case and a CT_POINTER in the second case. */ const char *expected; CTypeDescrObject *ctitem = ct->ct_itemdescr; if (PyList_Check(init) || PyTuple_Check(init)) { PyObject **items; Py_ssize_t i, n; n = PySequence_Fast_GET_SIZE(init); if (ct->ct_length >= 0 && n > ct->ct_length) { PyErr_Format(PyExc_IndexError, "too many initializers for '%s' (got %zd)", ct->ct_name, n); return -1; } items = PySequence_Fast_ITEMS(init); for (i=0; ict_size; } return 0; } else if ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) || ((ctitem->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) && (ctitem->ct_size == sizeof(char)))) { if (ctitem->ct_size == sizeof(char)) { char *srcdata; Py_ssize_t n; if (!PyBytes_Check(init)) { expected = STR_OR_BYTES" or list or tuple"; goto cannot_convert; } n = PyBytes_GET_SIZE(init); if (ct->ct_length >= 0 && n > ct->ct_length) { PyErr_Format(PyExc_IndexError, "initializer "STR_OR_BYTES" is too long for '%s' " "(got %zd characters)", ct->ct_name, n); return -1; } if (n != ct->ct_length) n++; srcdata = PyBytes_AS_STRING(init); memcpy(data, srcdata, n); return 0; } #ifdef HAVE_WCHAR_H else { Py_ssize_t n; if (!PyUnicode_Check(init)) { expected = "unicode or list or tuple"; goto cannot_convert; } n = _my_PyUnicode_SizeAsWideChar(init); if (ct->ct_length >= 0 && n > ct->ct_length) { PyErr_Format(PyExc_IndexError, "initializer unicode is too long for '%s' " "(got %zd characters)", ct->ct_name, n); return -1; } if (n != ct->ct_length) n++; _my_PyUnicode_AsWideChar(init, (wchar_t *)data, n); return 0; } #endif } else { expected = "list or tuple"; goto cannot_convert; } cannot_convert: return _convert_error(init, ct->ct_name, expected); } static int convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init, Py_ssize_t *optvarsize) { const char *expected; if (ct->ct_flags & CT_UNION) { Py_ssize_t n = PyObject_Size(init); if (n < 0) return -1; if (n > 1) { PyErr_Format(PyExc_ValueError, "initializer for '%s': %zd items given, but " "only one supported (use a dict if needed)", ct->ct_name, n); return -1; } } if (PyList_Check(init) || PyTuple_Check(init)) { PyObject **items = PySequence_Fast_ITEMS(init); Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init); CFieldObject *cf = (CFieldObject *)ct->ct_extra; for (i=0; ict_name, n); return -1; } if (convert_vfield_from_object(data, cf, items[i], optvarsize) < 0) return -1; cf = cf->cf_next; } return 0; } if (PyDict_Check(init)) { PyObject *d_key, *d_value; Py_ssize_t i = 0; CFieldObject *cf; while (PyDict_Next(init, &i, &d_key, &d_value)) { cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, d_key); if (cf == NULL) { PyErr_SetObject(PyExc_KeyError, d_key); return -1; } if (convert_vfield_from_object(data, cf, d_value, optvarsize) < 0) return -1; } return 0; } expected = optvarsize == NULL ? "list or tuple or dict or struct-cdata" : "list or tuple or dict"; return _convert_error(init, ct->ct_name, expected); } static int convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init) { const char *expected; char buf[sizeof(PY_LONG_LONG)]; /*if (ct->ct_size > 0)*/ /*WRITE(data, ct->ct_size)*/ if (ct->ct_flags & CT_ARRAY) { return convert_array_from_object(data, ct, init); } if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { char *ptrdata; CTypeDescrObject *ctinit; if (!CData_Check(init)) { expected = "cdata pointer"; goto cannot_convert; } ctinit = ((CDataObject *)init)->c_type; if (!(ctinit->ct_flags & (CT_POINTER|CT_FUNCTIONPTR))) { if (ctinit->ct_flags & CT_ARRAY) ctinit = (CTypeDescrObject *)ctinit->ct_stuff; else { expected = "pointer or array"; goto cannot_convert; } } if (ctinit != ct) { if ((ct->ct_flags & CT_CAST_ANYTHING) || (ctinit->ct_flags & CT_CAST_ANYTHING)) ; /* accept void* or char* as either source or target */ else { expected = "pointer to same type"; goto cannot_convert; } } ptrdata = ((CDataObject *)init)->c_data; *(char **)data = ptrdata; return 0; } if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { PY_LONG_LONG value = _my_PyLong_AsLongLong(init); if (value == -1 && PyErr_Occurred()) return -1; write_raw_integer_data(buf, value, ct->ct_size); if (value != read_raw_signed_data(buf, ct->ct_size)) goto overflow; write_raw_integer_data(data, value, ct->ct_size); return 0; } if (ct->ct_flags & CT_PRIMITIVE_UNSIGNED) { unsigned PY_LONG_LONG value = _my_PyLong_AsUnsignedLongLong(init, 1); if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) return -1; if (ct->ct_flags & CT_IS_BOOL) if (value & ~1) /* value != 0 && value != 1 */ goto overflow; write_raw_integer_data(buf, value, ct->ct_size); if (value != read_raw_unsigned_data(buf, ct->ct_size)) goto overflow; write_raw_integer_data(data, value, ct->ct_size); return 0; } if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { double value; if ((ct->ct_flags & CT_IS_LONGDOUBLE) && CData_Check(init) && (((CDataObject *)init)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { long double lvalue; char *initdata = ((CDataObject *)init)->c_data; /*READ(initdata, sizeof(long double))*/ lvalue = read_raw_longdouble_data(initdata); write_raw_longdouble_data(data, lvalue); return 0; } value = PyFloat_AsDouble(init); if (value == -1.0 && PyErr_Occurred()) return -1; if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) write_raw_float_data(data, value, ct->ct_size); else write_raw_longdouble_data(data, (long double)value); return 0; } if (ct->ct_flags & CT_PRIMITIVE_CHAR) { if (ct->ct_size == sizeof(char)) { int res = _convert_to_char(init); if (res < 0) return -1; data[0] = res; return 0; } #ifdef HAVE_WCHAR_H else { wchar_t res = _convert_to_wchar_t(init); if (res == (wchar_t)-1 && PyErr_Occurred()) return -1; *(wchar_t *)data = res; return 0; } #endif } if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { if (CData_Check(init)) { if (((CDataObject *)init)->c_type == ct && ct->ct_size >= 0) { memcpy(data, ((CDataObject *)init)->c_data, ct->ct_size); return 0; } } return convert_struct_from_object(data, ct, init, NULL); } PyErr_Format(PyExc_SystemError, "convert_from_object: '%s'", ct->ct_name); return -1; overflow: return _convert_overflow(init, ct->ct_name); cannot_convert: return _convert_error(init, ct->ct_name, expected); } static int convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init) { CTypeDescrObject *ct = cf->cf_type; PY_LONG_LONG fmin, fmax, value = PyLong_AsLongLong(init); unsigned PY_LONG_LONG rawfielddata, rawvalue, rawmask; if (value == -1 && PyErr_Occurred()) return -1; if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { fmin = -(1LL << (cf->cf_bitsize-1)); fmax = (1LL << (cf->cf_bitsize-1)) - 1LL; if (fmax == 0) fmax = 1; /* special case to let "int x:1" receive "1" */ } else { fmin = 0LL; fmax = (PY_LONG_LONG)((1ULL << cf->cf_bitsize) - 1ULL); } if (value < fmin || value > fmax) { /* phew, PyErr_Format does not support "%lld" in Python 2.6 */ PyObject *svalue = NULL, *sfmin = NULL, *sfmax = NULL; PyObject *lfmin = NULL, *lfmax = NULL; svalue = PyObject_Str(init); if (svalue == NULL) goto skip; lfmin = PyLong_FromLongLong(fmin); if (lfmin == NULL) goto skip; sfmin = PyObject_Str(lfmin); if (sfmin == NULL) goto skip; lfmax = PyLong_FromLongLong(fmax); if (lfmax == NULL) goto skip; sfmax = PyObject_Str(lfmax); if (sfmax == NULL) goto skip; PyErr_Format(PyExc_OverflowError, "value %s outside the range allowed by the " "bit field width: %s <= x <= %s", PyText_AS_UTF8(svalue), PyText_AS_UTF8(sfmin), PyText_AS_UTF8(sfmax)); skip: Py_XDECREF(svalue); Py_XDECREF(sfmin); Py_XDECREF(sfmax); Py_XDECREF(lfmin); Py_XDECREF(lfmax); return -1; } rawmask = ((1ULL << cf->cf_bitsize) - 1ULL) << cf->cf_bitshift; rawvalue = ((unsigned PY_LONG_LONG)value) << cf->cf_bitshift; /*WRITE(data, ct->ct_size)*/ rawfielddata = read_raw_unsigned_data(data, ct->ct_size); rawfielddata = (rawfielddata & ~rawmask) | (rawvalue & rawmask); write_raw_integer_data(data, rawfielddata, ct->ct_size); return 0; } static Py_ssize_t get_array_length(CDataObject *cd) { if (cd->c_type->ct_length < 0) return ((CDataObject_own_length *)cd)->length; else return cd->c_type->ct_length; } static int get_alignment(CTypeDescrObject *ct) { int align; retry: if ((ct->ct_flags & (CT_PRIMITIVE_ANY|CT_STRUCT|CT_UNION)) && !(ct->ct_flags & CT_IS_OPAQUE)) { align = ct->ct_length; } else if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { struct aligncheck_ptr { char x; char *y; }; align = offsetof(struct aligncheck_ptr, y); } else if (ct->ct_flags & CT_ARRAY) { ct = ct->ct_itemdescr; goto retry; } else { PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown alignment", ct->ct_name); return -1; } if ((align < 1) || (align & (align-1))) { PyErr_Format(PyExc_SystemError, "found for ctype '%s' bogus alignment '%d'", ct->ct_name, align); return -1; } return align; } static void cdata_dealloc(CDataObject *cd) { if (cd->c_weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) cd); Py_DECREF(cd->c_type); #ifndef CFFI_MEM_LEAK /* never release anything, tests only */ Py_TYPE(cd)->tp_free((PyObject *)cd); #endif } static void cdataowning_dealloc(CDataObject *cd) { assert(!(cd->c_type->ct_flags & (CT_IS_VOID_PTR | CT_FUNCTIONPTR))); if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { Py_DECREF(((CDataObject_own_structptr *)cd)->structobj); } #if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK) if (cd->c_type->ct_flags & (CT_PRIMITIVE_ANY | CT_STRUCT | CT_UNION)) { assert(cd->c_type->ct_size >= 0); memset(cd->c_data, 0xDD, cd->c_type->ct_size); } else if (cd->c_type->ct_flags & CT_ARRAY) { Py_ssize_t x = get_array_length(cd); assert(x >= 0); x *= cd->c_type->ct_itemdescr->ct_size; assert(x >= 0); memset(cd->c_data, 0xDD, x); } #endif cdata_dealloc(cd); } static void cdataowninggc_dealloc(CDataObject *cd) { assert(!(cd->c_type->ct_flags & (CT_IS_PTR_TO_OWNED | CT_PRIMITIVE_ANY | CT_STRUCT | CT_UNION))); PyObject_GC_UnTrack(cd); if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ PyObject *x = (PyObject *)(cd->c_data + 42); Py_DECREF(x); } else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ ffi_closure *closure = (ffi_closure *)cd->c_data; PyObject *args = (PyObject *)(closure->user_data); Py_XDECREF(args); cffi_closure_free(closure); } cdata_dealloc(cd); } static int cdataowninggc_traverse(CDataObject *cd, visitproc visit, void *arg) { if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ PyObject *x = (PyObject *)(cd->c_data + 42); Py_VISIT(x); } else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ ffi_closure *closure = (ffi_closure *)cd->c_data; PyObject *args = (PyObject *)(closure->user_data); Py_VISIT(args); } return 0; } static int cdataowninggc_clear(CDataObject *cd) { if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ PyObject *x = (PyObject *)(cd->c_data + 42); Py_INCREF(Py_None); cd->c_data = ((char *)Py_None) - 42; Py_DECREF(x); } else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ ffi_closure *closure = (ffi_closure *)cd->c_data; PyObject *args = (PyObject *)(closure->user_data); closure->user_data = NULL; Py_XDECREF(args); } return 0; } static PyObject *cdata_float(CDataObject *cd); /*forward*/ static PyObject *convert_cdata_to_enum_string(CDataObject *cd, int both) { PyObject *d_key, *d_value; CTypeDescrObject *ct = cd->c_type; assert(ct->ct_flags & CT_IS_ENUM); d_key = convert_to_object(cd->c_data, ct); if (d_key == NULL) return NULL; d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key); if (d_value != NULL) { if (both) { PyObject *o = PyObject_Str(d_key); if (o == NULL) d_value = NULL; else { d_value = PyText_FromFormat("%s: %s", PyText_AS_UTF8(o), PyText_AS_UTF8(d_value)); Py_DECREF(o); } } else Py_INCREF(d_value); } else d_value = PyObject_Str(d_key); Py_DECREF(d_key); return d_value; } static PyObject *cdata_repr(CDataObject *cd) { char *extra; PyObject *result, *s; if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) { if (cd->c_type->ct_flags & CT_IS_ENUM) { s = convert_cdata_to_enum_string(cd, 1); } else if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) { long double lvalue; char buffer[128]; /* big enough */ /*READ(cd->c_data, sizeof(long double)*/ lvalue = read_raw_longdouble_data(cd->c_data); sprintf(buffer, "%LE", lvalue); s = PyText_FromString(buffer); } else { PyObject *o = convert_to_object(cd->c_data, cd->c_type); if (o == NULL) return NULL; s = PyObject_Repr(o); Py_DECREF(o); } } else if ((cd->c_type->ct_flags & CT_ARRAY) && cd->c_type->ct_length < 0) { s = PyText_FromFormat("sliced length %zd", get_array_length(cd)); } else { if (cd->c_data != NULL) { s = PyText_FromFormat("%p", cd->c_data); } else s = PyText_FromString("NULL"); } if (s == NULL) return NULL; /* it's slightly confusing to get "" because the struct foo is not owned. Trying to make it clearer, write in this case "". */ if (cd->c_type->ct_flags & (CT_STRUCT|CT_UNION)) extra = " &"; else extra = ""; result = PyText_FromFormat("", cd->c_type->ct_name, extra, PyText_AsUTF8(s)); Py_DECREF(s); return result; } static PyObject *_cdata_repr2(CDataObject *cd, char *text, PyObject *x) { PyObject *res, *s = PyObject_Repr(x); if (s == NULL) return NULL; res = PyText_FromFormat("", cd->c_type->ct_name, text, PyText_AsUTF8(s)); Py_DECREF(s); return res; } static PyObject *cdataowning_repr(CDataObject *cd) { Py_ssize_t size; if (cd->c_type->ct_flags & CT_POINTER) { if (cd->c_type->ct_flags & CT_IS_VOID_PTR) goto handle_repr; size = cd->c_type->ct_itemdescr->ct_size; } else if (cd->c_type->ct_flags & CT_ARRAY) size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) goto callback_repr; else size = cd->c_type->ct_size; return PyText_FromFormat("", cd->c_type->ct_name, size); callback_repr: { PyObject *args = (PyObject *)((ffi_closure *)cd->c_data)->user_data; if (args == NULL) return cdata_repr(cd); else return _cdata_repr2(cd, "calling", PyTuple_GET_ITEM(args, 1)); } handle_repr: { PyObject *x = (PyObject *)(cd->c_data + 42); return _cdata_repr2(cd, "handle to", x); } } static int cdata_nonzero(CDataObject *cd) { return cd->c_data != NULL; } static PyObject *cdata_int(CDataObject *cd) { if ((cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_FITS_LONG)) == (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_FITS_LONG)) { /* this case is to handle enums, but also serves as a slight performance improvement for some other primitive types */ long value; /*READ(cd->c_data, cd->c_type->ct_size)*/ value = (long)read_raw_signed_data(cd->c_data, cd->c_type->ct_size); return PyInt_FromLong(value); } if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) { return convert_to_object(cd->c_data, cd->c_type); } else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) { /*READ(cd->c_data, cd->c_type->ct_size)*/ if (cd->c_type->ct_size == sizeof(char)) return PyInt_FromLong((unsigned char)cd->c_data[0]); #ifdef HAVE_WCHAR_H else return PyInt_FromLong((long)*(wchar_t *)cd->c_data); #endif } else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { PyObject *o = cdata_float(cd); #if PY_MAJOR_VERSION < 3 PyObject *r = o ? PyNumber_Int(o) : NULL; #else PyObject *r = o ? PyNumber_Long(o) : NULL; #endif Py_XDECREF(o); return r; } PyErr_Format(PyExc_TypeError, "int() not supported on cdata '%s'", cd->c_type->ct_name); return NULL; } #if PY_MAJOR_VERSION < 3 static PyObject *cdata_long(CDataObject *cd) { PyObject *res = cdata_int(cd); if (res != NULL && PyInt_CheckExact(res)) { PyObject *o = PyLong_FromLong(PyInt_AS_LONG(res)); Py_DECREF(res); res = o; } return res; } #endif static PyObject *cdata_float(CDataObject *cd) { if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { double value; /*READ(cd->c_data, cd->c_type->ct_size)*/ if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) { value = read_raw_float_data(cd->c_data, cd->c_type->ct_size); } else { value = (double)read_raw_longdouble_data(cd->c_data); } return PyFloat_FromDouble(value); } PyErr_Format(PyExc_TypeError, "float() not supported on cdata '%s'", cd->c_type->ct_name); return NULL; } static PyObject *cdata_richcompare(PyObject *v, PyObject *w, int op) { int res; PyObject *pyres; char *v_cdata, *w_cdata; assert(CData_Check(v)); if (!CData_Check(w)) { pyres = Py_NotImplemented; goto done; } if ((op != Py_EQ && op != Py_NE) && ((((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY) || (((CDataObject *)w)->c_type->ct_flags & CT_PRIMITIVE_ANY))) goto Error; v_cdata = ((CDataObject *)v)->c_data; w_cdata = ((CDataObject *)w)->c_data; switch (op) { case Py_EQ: res = (v_cdata == w_cdata); break; case Py_NE: res = (v_cdata != w_cdata); break; case Py_LT: res = (v_cdata < w_cdata); break; case Py_LE: res = (v_cdata <= w_cdata); break; case Py_GT: res = (v_cdata > w_cdata); break; case Py_GE: res = (v_cdata >= w_cdata); break; default: res = -1; } pyres = res ? Py_True : Py_False; done: Py_INCREF(pyres); return pyres; Error: PyErr_SetString(PyExc_TypeError, "cannot do comparison on a primitive cdata"); return NULL; } static long cdata_hash(CDataObject *cd) { return _Py_HashPointer(cd->c_data); } static Py_ssize_t cdata_length(CDataObject *cd) { if (cd->c_type->ct_flags & CT_ARRAY) { return get_array_length(cd); } PyErr_Format(PyExc_TypeError, "cdata of type '%s' has no len()", cd->c_type->ct_name); return -1; } static char * _cdata_get_indexed_ptr(CDataObject *cd, PyObject *key) { Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return NULL; if (cd->c_type->ct_flags & CT_POINTER) { if (CDataOwn_Check(cd) && i != 0) { PyErr_Format(PyExc_IndexError, "cdata '%s' can only be indexed by 0", cd->c_type->ct_name); return NULL; } } else if (cd->c_type->ct_flags & CT_ARRAY) { if (i < 0) { PyErr_SetString(PyExc_IndexError, "negative index not supported"); return NULL; } if (i >= get_array_length(cd)) { PyErr_Format(PyExc_IndexError, "index too large for cdata '%s' (expected %zd < %zd)", cd->c_type->ct_name, i, get_array_length(cd)); return NULL; } } else { PyErr_Format(PyExc_TypeError, "cdata of type '%s' cannot be indexed", cd->c_type->ct_name); return NULL; } return cd->c_data + i * cd->c_type->ct_itemdescr->ct_size; } static PyObject * new_array_type(CTypeDescrObject *ctptr, PyObject *lengthobj); /* forward */ static CTypeDescrObject * _cdata_getslicearg(CDataObject *cd, PySliceObject *slice, Py_ssize_t bounds[]) { Py_ssize_t start, stop; CTypeDescrObject *ct; start = PyInt_AsSsize_t(slice->start); if (start == -1 && PyErr_Occurred()) { if (slice->start == Py_None) PyErr_SetString(PyExc_IndexError, "slice start must be specified"); return NULL; } stop = PyInt_AsSsize_t(slice->stop); if (stop == -1 && PyErr_Occurred()) { if (slice->stop == Py_None) PyErr_SetString(PyExc_IndexError, "slice stop must be specified"); return NULL; } if (slice->step != Py_None) { PyErr_SetString(PyExc_IndexError, "slice with step not supported"); return NULL; } if (start > stop) { PyErr_SetString(PyExc_IndexError, "slice start > stop"); return NULL; } ct = cd->c_type; if (ct->ct_flags & CT_ARRAY) { if (start < 0) { PyErr_SetString(PyExc_IndexError, "negative index not supported"); return NULL; } if (stop > get_array_length(cd)) { PyErr_Format(PyExc_IndexError, "index too large (expected %zd <= %zd)", stop, get_array_length(cd)); return NULL; } ct = (CTypeDescrObject *)ct->ct_stuff; } else if (!(ct->ct_flags & CT_POINTER)) { PyErr_Format(PyExc_TypeError, "cdata of type '%s' cannot be indexed", ct->ct_name); return NULL; } bounds[0] = start; bounds[1] = stop - start; return ct; } static PyObject * cdata_slice(CDataObject *cd, PySliceObject *slice) { Py_ssize_t bounds[2]; CDataObject_own_length *scd; CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds); if (ct == NULL) return NULL; if (ct->ct_stuff == NULL) { ct->ct_stuff = new_array_type(ct, Py_None); if (ct->ct_stuff == NULL) return NULL; } ct = (CTypeDescrObject *)ct->ct_stuff; scd = (CDataObject_own_length *)PyObject_Malloc( offsetof(CDataObject_own_length, alignment)); if (PyObject_Init((PyObject *)scd, &CData_Type) == NULL) return NULL; Py_INCREF(ct); scd->head.c_type = ct; scd->head.c_data = cd->c_data + ct->ct_itemdescr->ct_size * bounds[0]; scd->head.c_weakreflist = NULL; scd->length = bounds[1]; return (PyObject *)scd; } static int cdata_ass_slice(CDataObject *cd, PySliceObject *slice, PyObject *v) { Py_ssize_t bounds[2], i, length, itemsize; PyObject *it, *item; PyObject *(*iternext)(PyObject *); char *cdata; int err; CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds); if (ct == NULL) return -1; ct = ct->ct_itemdescr; itemsize = ct->ct_size; cdata = cd->c_data + itemsize * bounds[0]; length = bounds[1]; if (CData_Check(v)) { CTypeDescrObject *ctv = ((CDataObject *)v)->c_type; if ((ctv->ct_flags & CT_ARRAY) && (ctv->ct_itemdescr == ct) && (get_array_length((CDataObject *)v) == length)) { /* fast path: copying from exactly the correct type */ memcpy(cdata, ((CDataObject *)v)->c_data, itemsize * length); return 0; } } /* A fast path for [0:N] = b"somestring", which also adds support for Python 3: otherwise, you get integers while enumerating the string, and you can't set them to characters :-/ */ if (PyBytes_Check(v) && (ct->ct_flags & CT_PRIMITIVE_CHAR) && itemsize == sizeof(char)) { if (PyBytes_GET_SIZE(v) != length) { PyErr_Format(PyExc_ValueError, "need a string of length %zd, got %zd", length, PyBytes_GET_SIZE(v)); return -1; } memcpy(cdata, PyBytes_AS_STRING(v), length); return 0; } it = PyObject_GetIter(v); if (it == NULL) return -1; iternext = *it->ob_type->tp_iternext; for (i = 0; i < length; i++) { item = iternext(it); if (item == NULL) { if (!PyErr_Occurred()) PyErr_Format(PyExc_ValueError, "need %zd values to unpack, got %zd", length, i); goto error; } err = convert_from_object(cdata, ct, item); Py_DECREF(item); if (err < 0) goto error; cdata += itemsize; } item = iternext(it); if (item != NULL) { Py_DECREF(item); PyErr_Format(PyExc_ValueError, "got more than %zd values to unpack", length); } error: Py_DECREF(it); return PyErr_Occurred() ? -1 : 0; } static PyObject * cdataowning_subscript(CDataObject *cd, PyObject *key) { char *c; if (PySlice_Check(key)) return cdata_slice(cd, (PySliceObject *)key); c = _cdata_get_indexed_ptr(cd, key); /* use 'mp_subscript' instead of 'sq_item' because we don't want negative indexes to be corrected automatically */ if (c == NULL && PyErr_Occurred()) return NULL; if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { PyObject *res = ((CDataObject_own_structptr *)cd)->structobj; Py_INCREF(res); return res; } else { return convert_to_object(c, cd->c_type->ct_itemdescr); } } static PyObject * cdata_subscript(CDataObject *cd, PyObject *key) { char *c; if (PySlice_Check(key)) return cdata_slice(cd, (PySliceObject *)key); c = _cdata_get_indexed_ptr(cd, key); /* use 'mp_subscript' instead of 'sq_item' because we don't want negative indexes to be corrected automatically */ if (c == NULL && PyErr_Occurred()) return NULL; return convert_to_object(c, cd->c_type->ct_itemdescr); } static int cdata_ass_sub(CDataObject *cd, PyObject *key, PyObject *v) { char *c; CTypeDescrObject *ctitem; if (PySlice_Check(key)) return cdata_ass_slice(cd, (PySliceObject *)key, v); c = _cdata_get_indexed_ptr(cd, key); ctitem = cd->c_type->ct_itemdescr; /* use 'mp_ass_subscript' instead of 'sq_ass_item' because we don't want negative indexes to be corrected automatically */ if (c == NULL && PyErr_Occurred()) return -1; if (v == NULL) { PyErr_SetString(PyExc_TypeError, "'del x[n]' not supported for cdata objects"); return -1; } return convert_from_object(c, ctitem, v); } static PyObject * _cdata_add_or_sub(PyObject *v, PyObject *w, int sign) { Py_ssize_t i, itemsize; CDataObject *cd; CTypeDescrObject *ctptr; if (!CData_Check(v)) { PyObject *swap; assert(CData_Check(w)); if (sign != 1) goto not_implemented; swap = v; v = w; w = swap; } i = PyNumber_AsSsize_t(w, PyExc_OverflowError); if (i == -1 && PyErr_Occurred()) return NULL; i *= sign; cd = (CDataObject *)v; if (cd->c_type->ct_flags & CT_POINTER) ctptr = cd->c_type; else if (cd->c_type->ct_flags & CT_ARRAY) { ctptr = (CTypeDescrObject *)cd->c_type->ct_stuff; } else { PyErr_Format(PyExc_TypeError, "cannot add a cdata '%s' and a number", cd->c_type->ct_name); return NULL; } itemsize = ctptr->ct_itemdescr->ct_size; if (itemsize < 0) { if (ctptr->ct_flags & CT_IS_VOID_PTR) { itemsize = 1; } else { PyErr_Format(PyExc_TypeError, "ctype '%s' points to items of unknown size", cd->c_type->ct_name); return NULL; } } return new_simple_cdata(cd->c_data + i * itemsize, ctptr); not_implemented: Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } static PyObject * cdata_add(PyObject *v, PyObject *w) { return _cdata_add_or_sub(v, w, +1); } static PyObject * cdata_sub(PyObject *v, PyObject *w) { if (CData_Check(v) && CData_Check(w)) { CDataObject *cdv = (CDataObject *)v; CDataObject *cdw = (CDataObject *)w; CTypeDescrObject *ct = cdw->c_type; Py_ssize_t diff, itemsize; if (ct->ct_flags & CT_ARRAY) /* ptr_to_T - array_of_T: ok */ ct = (CTypeDescrObject *)ct->ct_stuff; if (ct != cdv->c_type || !(ct->ct_flags & CT_POINTER) || (ct->ct_itemdescr->ct_size <= 0 && !(ct->ct_flags & CT_IS_VOID_PTR))) { PyErr_Format(PyExc_TypeError, "cannot subtract cdata '%s' and cdata '%s'", cdv->c_type->ct_name, ct->ct_name); return NULL; } itemsize = ct->ct_itemdescr->ct_size; if (itemsize <= 0) itemsize = 1; diff = (cdv->c_data - cdw->c_data) / itemsize; #if PY_MAJOR_VERSION < 3 return PyInt_FromSsize_t(diff); #else return PyLong_FromSsize_t(diff); #endif } return _cdata_add_or_sub(v, w, -1); } static PyObject * cdata_getattro(CDataObject *cd, PyObject *attr) { CFieldObject *cf; CTypeDescrObject *ct = cd->c_type; if (ct->ct_flags & CT_POINTER) ct = ct->ct_itemdescr; if ((ct->ct_flags & (CT_STRUCT|CT_UNION)) && ct->ct_stuff != NULL) { cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr); if (cf != NULL) { /* read the field 'cf' */ char *data = cd->c_data + cf->cf_offset; if (cf->cf_bitshift == BS_REGULAR) return convert_to_object(data, cf->cf_type); else if (cf->cf_bitshift == BS_EMPTY_ARRAY) return new_simple_cdata(data, (CTypeDescrObject *)cf->cf_type->ct_stuff); else return convert_to_object_bitfield(data, cf); } } return PyObject_GenericGetAttr((PyObject *)cd, attr); } static int cdata_setattro(CDataObject *cd, PyObject *attr, PyObject *value) { CFieldObject *cf; CTypeDescrObject *ct = cd->c_type; if (ct->ct_flags & CT_POINTER) ct = ct->ct_itemdescr; if ((ct->ct_flags & (CT_STRUCT|CT_UNION)) && ct->ct_stuff != NULL) { cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr); if (cf != NULL) { /* write the field 'cf' */ if (value != NULL) { return convert_field_from_object(cd->c_data, cf, value); } else { PyErr_SetString(PyExc_AttributeError, "cannot delete struct field"); return -1; } } } return PyObject_GenericSetAttr((PyObject *)cd, attr, value); } static PyObject * convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/ static cif_description_t * fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, ffi_abi); /*forward*/ static PyObject * b_new_primitive_type(PyObject *self, PyObject *args); /*forward*/ static CTypeDescrObject *_get_ct_int(void) { static CTypeDescrObject *ct_int = NULL; if (ct_int == NULL) { PyObject *args = Py_BuildValue("(s)", "int"); if (args == NULL) return NULL; ct_int = (CTypeDescrObject *)b_new_primitive_type(NULL, args); Py_DECREF(args); } return ct_int; } static Py_ssize_t _prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init, char **output_data) { /* 'ctptr' is here a pointer type 'ITEM *'. Accept as argument an initializer for an array 'ITEM[]'. This includes the case of passing a Python byte string to a 'char *' argument. This function returns -1 if an error occurred, 0 if conversion succeeded (into *output_data), or N > 0 if conversion would require N bytes of storage. */ Py_ssize_t length, datasize; CTypeDescrObject *ctitem; if (CData_Check(init)) goto convert_default; ctitem = ctptr->ct_itemdescr; /* XXX some code duplication, how to avoid it? */ if (PyBytes_Check(init)) { /* from a string: just returning the string here is fine. We assume that the C code won't modify the 'char *' data. */ if ((ctptr->ct_flags & CT_CAST_ANYTHING) || ((ctitem->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) && (ctitem->ct_size == sizeof(char)))) { #if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK) length = PyBytes_GET_SIZE(init) + 1; #else *output_data = PyBytes_AS_STRING(init); return 0; #endif } else goto convert_default; } else if (PyList_Check(init) || PyTuple_Check(init)) { length = PySequence_Fast_GET_SIZE(init); } else if (PyUnicode_Check(init)) { /* from a unicode, we add the null terminator */ length = _my_PyUnicode_SizeAsWideChar(init) + 1; } else if ((ctitem->ct_flags & CT_IS_FILE) && PyFile_Check(init)) { *output_data = (char *)PyFile_AsFile(init); if (*output_data == NULL && PyErr_Occurred()) return -1; return 0; } else { /* refuse to receive just an integer (and interpret it as the array size) */ goto convert_default; } if (ctitem->ct_size <= 0) goto convert_default; datasize = length * ctitem->ct_size; if ((datasize / ctitem->ct_size) != length) { PyErr_SetString(PyExc_OverflowError, "array size would overflow a Py_ssize_t"); return -1; } if (datasize <= 0) datasize = 1; return datasize; convert_default: return convert_from_object((char *)output_data, ctptr, init); } static PyObject* cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds) { char *buffer; void** buffer_array; cif_description_t *cif_descr; Py_ssize_t i, nargs, nargs_declared; PyObject *signature, *res = NULL, *fvarargs; CTypeDescrObject *fresult; char *resultdata; char *errormsg; if (!(cd->c_type->ct_flags & CT_FUNCTIONPTR)) { PyErr_Format(PyExc_TypeError, "cdata '%s' is not callable", cd->c_type->ct_name); return NULL; } if (kwds != NULL && PyDict_Size(kwds) != 0) { PyErr_SetString(PyExc_TypeError, "a cdata function cannot be called with keyword arguments"); return NULL; } signature = cd->c_type->ct_stuff; nargs = PyTuple_Size(args); if (nargs < 0) return NULL; nargs_declared = PyTuple_GET_SIZE(signature) - 2; fresult = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 1); fvarargs = NULL; buffer = NULL; cif_descr = (cif_description_t *)cd->c_type->ct_extra; if (cif_descr != NULL) { /* regular case: this function does not take '...' arguments */ if (nargs != nargs_declared) { errormsg = "'%s' expects %zd arguments, got %zd"; bad_number_of_arguments: PyErr_Format(PyExc_TypeError, errormsg, cd->c_type->ct_name, nargs_declared, nargs); goto error; } } else { /* call of a variadic function */ ffi_abi fabi; if (nargs < nargs_declared) { errormsg = "'%s' expects at least %zd arguments, got %zd"; goto bad_number_of_arguments; } fvarargs = PyTuple_New(nargs); if (fvarargs == NULL) goto error; for (i = 0; i < nargs_declared; i++) { PyObject *o = PyTuple_GET_ITEM(signature, 2 + i); Py_INCREF(o); PyTuple_SET_ITEM(fvarargs, i, o); } for (i = nargs_declared; i < nargs; i++) { PyObject *obj = PyTuple_GET_ITEM(args, i); CTypeDescrObject *ct; if (CData_Check(obj)) { ct = ((CDataObject *)obj)->c_type; if (ct->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_UNSIGNED | CT_PRIMITIVE_SIGNED)) { if (ct->ct_size < sizeof(int)) { ct = _get_ct_int(); if (ct == NULL) goto error; } } else if (ct->ct_flags & CT_ARRAY) { ct = (CTypeDescrObject *)ct->ct_stuff; } Py_INCREF(ct); } else { PyErr_Format(PyExc_TypeError, "argument %zd passed in the variadic part " "needs to be a cdata object (got %.200s)", i + 1, Py_TYPE(obj)->tp_name); goto error; } PyTuple_SET_ITEM(fvarargs, i, (PyObject *)ct); } #if PY_MAJOR_VERSION < 3 fabi = PyInt_AS_LONG(PyTuple_GET_ITEM(signature, 0)); #else fabi = PyLong_AS_LONG(PyTuple_GET_ITEM(signature, 0)); #endif cif_descr = fb_prepare_cif(fvarargs, fresult, fabi); if (cif_descr == NULL) goto error; } buffer = PyObject_Malloc(cif_descr->exchange_size); if (buffer == NULL) { PyErr_NoMemory(); goto error; } buffer_array = (void **)buffer; for (i=0; iexchange_offset_arg[1 + i]; PyObject *obj = PyTuple_GET_ITEM(args, i); buffer_array[i] = data; if (i < nargs_declared) argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 2 + i); else argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i); if (argtype->ct_flags & CT_POINTER) { char *tmpbuf; Py_ssize_t datasize = _prepare_pointer_call_argument( argtype, obj, (char **)data); if (datasize == 0) ; /* successfully filled '*data' */ else if (datasize < 0) goto error; else { tmpbuf = alloca(datasize); memset(tmpbuf, 0, datasize); *(char **)data = tmpbuf; if (convert_array_from_object(tmpbuf, argtype, obj) < 0) goto error; } } else if (convert_from_object(data, argtype, obj) < 0) goto error; } resultdata = buffer + cif_descr->exchange_offset_arg[0]; /*READ(cd->c_data, sizeof(void(*)(void)))*/ Py_BEGIN_ALLOW_THREADS restore_errno(); ffi_call(&cif_descr->cif, (void (*)(void))(cd->c_data), resultdata, buffer_array); save_errno(); Py_END_ALLOW_THREADS if (fresult->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_UNSIGNED)) { #ifdef WORDS_BIGENDIAN /* For results of precisely these types, libffi has a strange rule that they will be returned as a whole 'ffi_arg' if they are smaller. The difference only matters on big-endian. */ if (fresult->ct_size < sizeof(ffi_arg)) resultdata += (sizeof(ffi_arg) - fresult->ct_size); #endif res = convert_to_object(resultdata, fresult); } else if (fresult->ct_flags & CT_VOID) { res = Py_None; Py_INCREF(res); } else if (fresult->ct_flags & CT_STRUCT) { res = convert_struct_to_owning_object(resultdata, fresult); } else { res = convert_to_object(resultdata, fresult); } /* fall-through */ error: if (buffer) PyObject_Free(buffer); if (fvarargs != NULL) { Py_DECREF(fvarargs); if (cif_descr != NULL) /* but only if fvarargs != NULL, if variadic */ PyObject_Free(cif_descr); } return res; } static PyObject *cdata_iter(CDataObject *); static PyNumberMethods CData_as_number = { (binaryfunc)cdata_add, /*nb_add*/ (binaryfunc)cdata_sub, /*nb_subtract*/ 0, /*nb_multiply*/ #if PY_MAJOR_VERSION < 3 0, /*nb_divide*/ #endif 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ (inquiry)cdata_nonzero, /*nb_nonzero*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ 0, /*nb_and*/ 0, /*nb_xor*/ 0, /*nb_or*/ #if PY_MAJOR_VERSION < 3 0, /*nb_coerce*/ #endif (unaryfunc)cdata_int, /*nb_int*/ #if PY_MAJOR_VERSION < 3 (unaryfunc)cdata_long, /*nb_long*/ #else 0, #endif (unaryfunc)cdata_float, /*nb_float*/ 0, /*nb_oct*/ 0, /*nb_hex*/ }; static PyMappingMethods CData_as_mapping = { (lenfunc)cdata_length, /*mp_length*/ (binaryfunc)cdata_subscript, /*mp_subscript*/ (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/ }; static PyMappingMethods CDataOwn_as_mapping = { (lenfunc)cdata_length, /*mp_length*/ (binaryfunc)cdataowning_subscript, /*mp_subscript*/ (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/ }; static PyTypeObject CData_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CData", sizeof(CDataObject), 0, (destructor)cdata_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)cdata_repr, /* tp_repr */ &CData_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ &CData_as_mapping, /* tp_as_mapping */ (hashfunc)cdata_hash, /* tp_hash */ (ternaryfunc)cdata_call, /* tp_call */ 0, /* tp_str */ (getattrofunc)cdata_getattro, /* tp_getattro */ (setattrofunc)cdata_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ cdata_richcompare, /* tp_richcompare */ offsetof(CDataObject, c_weakreflist), /* tp_weaklistoffset */ (getiterfunc)cdata_iter, /* tp_iter */ 0, /* tp_iternext */ }; static PyTypeObject CDataOwning_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CDataOwn", sizeof(CDataObject), 0, (destructor)cdataowning_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)cdataowning_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ &CDataOwn_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ &CData_Type, /* tp_base */ }; static PyTypeObject CDataOwningGC_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CDataOwnGC", sizeof(CDataObject), 0, (destructor)cdataowninggc_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES /* tp_flags */ | Py_TPFLAGS_HAVE_GC, 0, /* tp_doc */ (traverseproc)cdataowninggc_traverse, /* tp_traverse */ (inquiry)cdataowninggc_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ &CDataOwning_Type, /* tp_base */ }; /************************************************************/ typedef struct { PyObject_HEAD char *di_next, *di_stop; CDataObject *di_object; CTypeDescrObject *di_itemtype; } CDataIterObject; static PyObject * cdataiter_next(CDataIterObject *it) { char *result = it->di_next; if (result != it->di_stop) { it->di_next = result + it->di_itemtype->ct_size; return convert_to_object(result, it->di_itemtype); } return NULL; } static void cdataiter_dealloc(CDataIterObject *it) { Py_DECREF(it->di_object); PyObject_Del(it); } static PyTypeObject CDataIter_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CDataIter", /* tp_name */ sizeof(CDataIterObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)cdataiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)cdataiter_next, /* tp_iternext */ }; static PyObject * cdata_iter(CDataObject *cd) { CDataIterObject *it; if (!(cd->c_type->ct_flags & CT_ARRAY)) { PyErr_Format(PyExc_TypeError, "cdata '%s' does not support iteration", cd->c_type->ct_name); return NULL; } it = PyObject_New(CDataIterObject, &CDataIter_Type); if (it == NULL) return NULL; Py_INCREF(cd); it->di_object = cd; it->di_itemtype = cd->c_type->ct_itemdescr; it->di_next = cd->c_data; it->di_stop = cd->c_data + get_array_length(cd) * it->di_itemtype->ct_size; return (PyObject *)it; } /************************************************************/ static CDataObject *allocate_owning_object(Py_ssize_t size, CTypeDescrObject *ct) { CDataObject *cd; cd = (CDataObject *)PyObject_Malloc(size); if (PyObject_Init((PyObject *)cd, &CDataOwning_Type) == NULL) return NULL; Py_INCREF(ct); cd->c_type = ct; cd->c_weakreflist = NULL; return cd; } static PyObject * convert_struct_to_owning_object(char *data, CTypeDescrObject *ct) { CDataObject *cd; Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment); Py_ssize_t datasize = ct->ct_size; if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) != CT_STRUCT) { PyErr_SetString(PyExc_TypeError, "return type is not a struct or is opaque"); return NULL; } cd = allocate_owning_object(dataoffset + datasize, ct); if (cd == NULL) return NULL; cd->c_data = ((char *)cd) + dataoffset; memcpy(cd->c_data, data, datasize); return (PyObject *)cd; } static PyObject *b_newp(PyObject *self, PyObject *args) { CTypeDescrObject *ct, *ctitem; CDataObject *cd; PyObject *init = Py_None; Py_ssize_t dataoffset, datasize, explicitlength; if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init)) return NULL; explicitlength = -1; if (ct->ct_flags & CT_POINTER) { dataoffset = offsetof(CDataObject_own_nolength, alignment); ctitem = ct->ct_itemdescr; datasize = ctitem->ct_size; if (datasize < 0) { PyErr_Format(PyExc_TypeError, "cannot instantiate ctype '%s' of unknown size", ctitem->ct_name); return NULL; } if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) datasize *= 2; /* forcefully add another character: a null */ if ((ctitem->ct_flags & CT_WITH_VAR_ARRAY) && init != Py_None) { Py_ssize_t optvarsize = datasize; if (convert_struct_from_object(NULL,ctitem, init, &optvarsize) < 0) return NULL; datasize = optvarsize; } } else if (ct->ct_flags & CT_ARRAY) { dataoffset = offsetof(CDataObject_own_nolength, alignment); datasize = ct->ct_size; if (datasize < 0) { explicitlength = get_new_array_length(&init); if (explicitlength < 0) return NULL; ctitem = ct->ct_itemdescr; dataoffset = offsetof(CDataObject_own_length, alignment); datasize = explicitlength * ctitem->ct_size; if (explicitlength > 0 && (datasize / explicitlength) != ctitem->ct_size) { PyErr_SetString(PyExc_OverflowError, "array size would overflow a Py_ssize_t"); return NULL; } } } else { PyErr_Format(PyExc_TypeError, "expected a pointer or array ctype, got '%s'", ct->ct_name); return NULL; } if (ct->ct_flags & CT_IS_PTR_TO_OWNED) { /* common case of ptr-to-struct (or ptr-to-union): for this case we build two objects instead of one, with the memory-owning one being really the struct (or union) and the returned one having a strong reference to it */ CDataObject *cds; cds = allocate_owning_object(dataoffset + datasize, ct->ct_itemdescr); if (cds == NULL) return NULL; cd = allocate_owning_object(sizeof(CDataObject_own_structptr), ct); if (cd == NULL) { Py_DECREF(cds); return NULL; } /* store the only reference to cds into cd */ ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds; assert(explicitlength < 0); cds->c_data = cd->c_data = ((char *)cds) + dataoffset; } else { cd = allocate_owning_object(dataoffset + datasize, ct); if (cd == NULL) return NULL; cd->c_data = ((char *)cd) + dataoffset; if (explicitlength >= 0) ((CDataObject_own_length*)cd)->length = explicitlength; } memset(cd->c_data, 0, datasize); if (init != Py_None) { if (convert_from_object(cd->c_data, (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) { Py_DECREF(cd); return NULL; } } return (PyObject *)cd; } static int _my_PyObject_AsBool(PyObject *ob) { /* convert and cast a Python object to a boolean. Accept an integer or a float object, up to a CData 'long double'. */ PyObject *io; PyNumberMethods *nb; int res; #if PY_MAJOR_VERSION < 3 if (PyInt_Check(ob)) { return PyInt_AS_LONG(ob) != 0; } else #endif if (PyLong_Check(ob)) { return _PyLong_Sign(ob) != 0; } else if (PyFloat_Check(ob)) { return PyFloat_AS_DOUBLE(ob) != 0.0; } else if (CData_Check(ob)) { CDataObject *cd = (CDataObject *)ob; if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { /*READ(cd->c_data, cd->c_type->ct_size)*/ if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) { /* 'long double' objects: return the answer directly */ return read_raw_longdouble_data(cd->c_data) != 0.0; } else { /* 'float'/'double' objects: return the answer directly */ return read_raw_float_data(cd->c_data, cd->c_type->ct_size) != 0.0; } } } nb = ob->ob_type->tp_as_number; if (nb == NULL || (nb->nb_float == NULL && nb->nb_int == NULL)) { PyErr_SetString(PyExc_TypeError, "integer/float expected"); return -1; } if (nb->nb_float && !CData_Check(ob)) io = (*nb->nb_float) (ob); else io = (*nb->nb_int) (ob); if (io == NULL) return -1; if (PyIntOrLong_Check(io) || PyFloat_Check(io)) { res = _my_PyObject_AsBool(io); } else { PyErr_SetString(PyExc_TypeError, "integer/float conversion failed"); res = -1; } Py_DECREF(io); return res; } static CDataObject *_new_casted_primitive(CTypeDescrObject *ct) { int dataoffset = offsetof(CDataObject_casted_primitive, alignment); CDataObject *cd = (CDataObject *)PyObject_Malloc(dataoffset + ct->ct_size); if (PyObject_Init((PyObject *)cd, &CData_Type) == NULL) return NULL; Py_INCREF(ct); cd->c_type = ct; cd->c_data = ((char*)cd) + dataoffset; cd->c_weakreflist = NULL; return cd; } static CDataObject *cast_to_integer_or_char(CTypeDescrObject *ct, PyObject *ob) { unsigned PY_LONG_LONG value; CDataObject *cd; if (CData_Check(ob) && ((CDataObject *)ob)->c_type->ct_flags & (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) { value = (Py_intptr_t)((CDataObject *)ob)->c_data; } #if PY_MAJOR_VERSION < 3 else if (PyString_Check(ob)) { if (PyString_GET_SIZE(ob) != 1) { PyErr_Format(PyExc_TypeError, "cannot cast string of length %zd to ctype '%s'", PyString_GET_SIZE(ob), ct->ct_name); return NULL; } value = (unsigned char)PyString_AS_STRING(ob)[0]; } #endif #ifdef HAVE_WCHAR_H else if (PyUnicode_Check(ob)) { wchar_t ordinal; if (_my_PyUnicode_AsSingleWideChar(ob, &ordinal) < 0) { PyErr_Format(PyExc_TypeError, "cannot cast unicode string of length %zd to ctype '%s'", PyUnicode_GET_SIZE(ob), ct->ct_name); return NULL; } value = (long)ordinal; } #endif else if (PyBytes_Check(ob)) { int res = _convert_to_char(ob); if (res < 0) return NULL; value = (unsigned char)res; } else if (ct->ct_flags & CT_IS_BOOL) { int res = _my_PyObject_AsBool(ob); if (res < 0) return NULL; value = res; } else { value = _my_PyLong_AsUnsignedLongLong(ob, 0); if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) return NULL; } if (ct->ct_flags & CT_IS_BOOL) value = !!value; cd = _new_casted_primitive(ct); if (cd != NULL) write_raw_integer_data(cd->c_data, value, ct->ct_size); return cd; } static PyObject *b_cast(PyObject *self, PyObject *args) { CTypeDescrObject *ct; CDataObject *cd; PyObject *ob; if (!PyArg_ParseTuple(args, "O!O:cast", &CTypeDescr_Type, &ct, &ob)) return NULL; if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY) && ct->ct_size >= 0) { /* cast to a pointer, to a funcptr, or to an array. Note that casting to an array is an extension to the C language, which seems to be necessary in order to sanely get a at some address. */ unsigned PY_LONG_LONG value; if (CData_Check(ob)) { CDataObject *cdsrc = (CDataObject *)ob; if (cdsrc->c_type->ct_flags & (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) { return new_simple_cdata(cdsrc->c_data, ct); } } if ((ct->ct_flags & CT_POINTER) && (ct->ct_itemdescr->ct_flags & CT_IS_FILE) && PyFile_Check(ob)) { FILE *f = PyFile_AsFile(ob); if (f == NULL && PyErr_Occurred()) return NULL; return new_simple_cdata((char *)f, ct); } value = _my_PyLong_AsUnsignedLongLong(ob, 0); if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) return NULL; return new_simple_cdata((char *)(Py_intptr_t)value, ct); } else if (ct->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED |CT_PRIMITIVE_CHAR)) { /* cast to an integer type or a char */ return (PyObject *)cast_to_integer_or_char(ct, ob); } else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { /* cast to a float */ double value; PyObject *io; if (CData_Check(ob)) { CDataObject *cdsrc = (CDataObject *)ob; if (!(cdsrc->c_type->ct_flags & CT_PRIMITIVE_ANY)) goto cannot_cast; io = convert_to_object(cdsrc->c_data, cdsrc->c_type); if (io == NULL) return NULL; } else { io = ob; Py_INCREF(io); } if (PyBytes_Check(io)) { if (PyBytes_GET_SIZE(io) != 1) { Py_DECREF(io); goto cannot_cast; } value = (unsigned char)PyBytes_AS_STRING(io)[0]; } #if HAVE_WCHAR_H else if (PyUnicode_Check(io)) { wchar_t ordinal; if (_my_PyUnicode_AsSingleWideChar(io, &ordinal) < 0) { Py_DECREF(io); goto cannot_cast; } value = (long)ordinal; } #endif else if ((ct->ct_flags & CT_IS_LONGDOUBLE) && CData_Check(io) && (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { long double lvalue; char *data = ((CDataObject *)io)->c_data; /*READ(data, sizeof(long double)*/ lvalue = read_raw_longdouble_data(data); cd = _new_casted_primitive(ct); if (cd != NULL) write_raw_longdouble_data(cd->c_data, lvalue); return (PyObject *)cd; } else { value = PyFloat_AsDouble(io); } Py_DECREF(io); if (value == -1.0 && PyErr_Occurred()) return NULL; cd = _new_casted_primitive(ct); if (cd != NULL) { if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) write_raw_float_data(cd->c_data, value, ct->ct_size); else write_raw_longdouble_data(cd->c_data, (long double)value); } return (PyObject *)cd; } else { PyErr_Format(PyExc_TypeError, "cannot cast to ctype '%s'", ct->ct_name); return NULL; } cannot_cast: if (CData_Check(ob)) PyErr_Format(PyExc_TypeError, "cannot cast ctype '%s' to ctype '%s'", ((CDataObject *)ob)->c_type->ct_name, ct->ct_name); else PyErr_Format(PyExc_TypeError, "cannot cast %.200s object to ctype '%s'", Py_TYPE(ob)->tp_name, ct->ct_name); return NULL; } /************************************************************/ typedef struct { PyObject_HEAD void *dl_handle; char *dl_name; } DynLibObject; static void dl_dealloc(DynLibObject *dlobj) { dlclose(dlobj->dl_handle); free(dlobj->dl_name); PyObject_Del(dlobj); } static PyObject *dl_repr(DynLibObject *dlobj) { return PyText_FromFormat("", dlobj->dl_name); } static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args) { CTypeDescrObject *ct; char *funcname; void *funcptr; int ok; if (!PyArg_ParseTuple(args, "O!s:load_function", &CTypeDescr_Type, &ct, &funcname)) return NULL; ok = 0; if (ct->ct_flags & CT_FUNCTIONPTR) ok = 1; if ((ct->ct_flags & CT_POINTER) && (ct->ct_itemdescr->ct_flags & CT_VOID)) ok = 1; if (!ok) { PyErr_Format(PyExc_TypeError, "function cdata expected, got '%s'", ct->ct_name); return NULL; } dlerror(); /* clear error condition */ funcptr = dlsym(dlobj->dl_handle, funcname); if (funcptr == NULL) { const char *error = dlerror(); PyErr_Format(PyExc_KeyError, "function '%s' not found in library '%s': %s", funcname, dlobj->dl_name, error); return NULL; } return new_simple_cdata(funcptr, ct); } static PyObject *dl_read_variable(DynLibObject *dlobj, PyObject *args) { CTypeDescrObject *ct; char *varname; char *data; if (!PyArg_ParseTuple(args, "O!s:read_variable", &CTypeDescr_Type, &ct, &varname)) return NULL; dlerror(); /* clear error condition */ data = dlsym(dlobj->dl_handle, varname); if (data == NULL) { const char *error = dlerror(); if (error != NULL) { PyErr_Format(PyExc_KeyError, "variable '%s' not found in library '%s': %s", varname, dlobj->dl_name, error); return NULL; } } return convert_to_object(data, ct); } static PyObject *dl_write_variable(DynLibObject *dlobj, PyObject *args) { CTypeDescrObject *ct; PyObject *value; char *varname; char *data; if (!PyArg_ParseTuple(args, "O!sO:write_variable", &CTypeDescr_Type, &ct, &varname, &value)) return NULL; dlerror(); /* clear error condition */ data = dlsym(dlobj->dl_handle, varname); if (data == NULL) { const char *error = dlerror(); PyErr_Format(PyExc_KeyError, "variable '%s' not found in library '%s': %s", varname, dlobj->dl_name, error); return NULL; } if (convert_from_object(data, ct, value) < 0) return NULL; Py_INCREF(Py_None); return Py_None; } static PyMethodDef dl_methods[] = { {"load_function", (PyCFunction)dl_load_function, METH_VARARGS}, {"read_variable", (PyCFunction)dl_read_variable, METH_VARARGS}, {"write_variable", (PyCFunction)dl_write_variable, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; static PyTypeObject dl_type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.Library", /* tp_name */ sizeof(DynLibObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dl_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)dl_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ dl_methods, /* tp_methods */ }; static PyObject *b_load_library(PyObject *self, PyObject *args) { char *filename_or_null, *printable_filename; void *handle; DynLibObject *dlobj; int flags = 0; if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) { PyObject *dummy; if (!PyArg_ParseTuple(args, "|Oi:load_library", &dummy, &flags)) return NULL; filename_or_null = NULL; } else if (!PyArg_ParseTuple(args, "et|i:load_library", Py_FileSystemDefaultEncoding, &filename_or_null, &flags)) return NULL; if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0) flags |= RTLD_NOW; printable_filename = filename_or_null ? filename_or_null : ""; handle = dlopen(filename_or_null, flags); if (handle == NULL) { const char *error = dlerror(); PyErr_Format(PyExc_OSError, "cannot load library %s: %s", printable_filename, error); return NULL; } dlobj = PyObject_New(DynLibObject, &dl_type); if (dlobj == NULL) { dlclose(handle); return NULL; } dlobj->dl_handle = handle; dlobj->dl_name = strdup(printable_filename); return (PyObject *)dlobj; } /************************************************************/ static PyObject *b_new_primitive_type(PyObject *self, PyObject *args) { #define ENUM_PRIMITIVE_TYPES \ EPTYPE(c, char, CT_PRIMITIVE_CHAR) \ EPTYPE(s, short, CT_PRIMITIVE_SIGNED ) \ EPTYPE(i, int, CT_PRIMITIVE_SIGNED ) \ EPTYPE(l, long, CT_PRIMITIVE_SIGNED ) \ EPTYPE(ll, long long, CT_PRIMITIVE_SIGNED ) \ EPTYPE(sc, signed char, CT_PRIMITIVE_SIGNED ) \ EPTYPE(uc, unsigned char, CT_PRIMITIVE_UNSIGNED ) \ EPTYPE(us, unsigned short, CT_PRIMITIVE_UNSIGNED ) \ EPTYPE(ui, unsigned int, CT_PRIMITIVE_UNSIGNED ) \ EPTYPE(ul, unsigned long, CT_PRIMITIVE_UNSIGNED ) \ EPTYPE(ull, unsigned long long, CT_PRIMITIVE_UNSIGNED ) \ EPTYPE(f, float, CT_PRIMITIVE_FLOAT ) \ EPTYPE(d, double, CT_PRIMITIVE_FLOAT ) \ EPTYPE(ld, long double, CT_PRIMITIVE_FLOAT | CT_IS_LONGDOUBLE ) \ ENUM_PRIMITIVE_TYPES_WCHAR \ EPTYPE(b, _Bool, CT_PRIMITIVE_UNSIGNED | CT_IS_BOOL ) \ /* the following types are not primitive in the C sense */ \ EPTYPE(i8, int8_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(u8, uint8_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(i16, int16_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(u16, uint16_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(i32, int32_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(u32, uint32_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(i64, int64_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(u64, uint64_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(ip, intptr_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(up, uintptr_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(pd, ptrdiff_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(sz, size_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(ssz, ssize_t, CT_PRIMITIVE_SIGNED) #ifdef HAVE_WCHAR_H # define ENUM_PRIMITIVE_TYPES_WCHAR \ EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR ) #else # define ENUM_PRIMITIVE_TYPES_WCHAR /* nothing */ #endif #define EPTYPE(code, typename, flags) \ struct aligncheck_##code { char x; typename y; }; ENUM_PRIMITIVE_TYPES #undef EPTYPE CTypeDescrObject *td; const char *name; static const struct descr_s { const char *name; int size, align, flags; } types[] = { #define EPTYPE(code, typename, flags) \ { #typename, \ sizeof(typename), \ offsetof(struct aligncheck_##code, y), \ flags \ }, ENUM_PRIMITIVE_TYPES #undef EPTYPE #undef ENUM_PRIMITIVE_TYPES_WCHAR #undef ENUM_PRIMITIVE_TYPES { NULL } }; const struct descr_s *ptypes; int name_size; ffi_type *ffitype; if (!PyArg_ParseTuple(args, "s:new_primitive_type", &name)) return NULL; for (ptypes=types; ; ptypes++) { if (ptypes->name == NULL) { #ifndef HAVE_WCHAR_H if (strcmp(name, "wchar_t")) PyErr_SetString(PyExc_NotImplementedError, name); else #endif PyErr_SetString(PyExc_KeyError, name); return NULL; } if (strcmp(name, ptypes->name) == 0) break; } if (ptypes->flags & CT_PRIMITIVE_SIGNED) { switch (ptypes->size) { case 1: ffitype = &ffi_type_sint8; break; case 2: ffitype = &ffi_type_sint16; break; case 4: ffitype = &ffi_type_sint32; break; case 8: ffitype = &ffi_type_sint64; break; default: goto bad_ffi_type; } } else if (ptypes->flags & CT_PRIMITIVE_FLOAT) { if (strcmp(ptypes->name, "float") == 0) ffitype = &ffi_type_float; else if (strcmp(ptypes->name, "double") == 0) ffitype = &ffi_type_double; else if (strcmp(ptypes->name, "long double") == 0) ffitype = &ffi_type_longdouble; else goto bad_ffi_type; } else { switch (ptypes->size) { case 1: ffitype = &ffi_type_uint8; break; case 2: ffitype = &ffi_type_uint16; break; case 4: ffitype = &ffi_type_uint32; break; case 8: ffitype = &ffi_type_uint64; break; default: goto bad_ffi_type; } } name_size = strlen(ptypes->name) + 1; td = ctypedescr_new(name_size); if (td == NULL) return NULL; memcpy(td->ct_name, name, name_size); td->ct_size = ptypes->size; td->ct_length = ptypes->align; td->ct_extra = ffitype; td->ct_flags = ptypes->flags; if (td->ct_flags & (CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_CHAR)) { if (td->ct_size <= sizeof(long)) td->ct_flags |= CT_PRIMITIVE_FITS_LONG; } else if (td->ct_flags & CT_PRIMITIVE_UNSIGNED) { if (td->ct_size < sizeof(long)) td->ct_flags |= CT_PRIMITIVE_FITS_LONG; } td->ct_name_position = strlen(td->ct_name); return (PyObject *)td; bad_ffi_type: PyErr_Format(PyExc_NotImplementedError, "primitive type '%s' with a non-standard size %d", name, (int)ptypes->size); return NULL; } static PyObject *b_new_pointer_type(PyObject *self, PyObject *args) { CTypeDescrObject *td, *ctitem; const char *extra; if (!PyArg_ParseTuple(args, "O!:new_pointer_type", &CTypeDescr_Type, &ctitem)) return NULL; if (ctitem->ct_flags & CT_ARRAY) extra = "(*)"; /* obscure case: see test_array_add */ else extra = " *"; td = ctypedescr_new_on_top(ctitem, extra, 2); if (td == NULL) return NULL; td->ct_size = sizeof(void *); td->ct_length = -1; td->ct_flags = CT_POINTER; if (ctitem->ct_flags & (CT_STRUCT|CT_UNION)) td->ct_flags |= CT_IS_PTR_TO_OWNED; if (ctitem->ct_flags & CT_VOID) td->ct_flags |= CT_IS_VOID_PTR; if ((ctitem->ct_flags & CT_VOID) || ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) && ctitem->ct_size == sizeof(char))) td->ct_flags |= CT_CAST_ANYTHING; /* 'void *' or 'char *' only */ return (PyObject *)td; } static PyObject *b_new_array_type(PyObject *self, PyObject *args) { PyObject *lengthobj; CTypeDescrObject *ctptr; if (!PyArg_ParseTuple(args, "O!O:new_array_type", &CTypeDescr_Type, &ctptr, &lengthobj)) return NULL; return new_array_type(ctptr, lengthobj); } static PyObject * new_array_type(CTypeDescrObject *ctptr, PyObject *lengthobj) { CTypeDescrObject *td, *ctitem; char extra_text[32]; Py_ssize_t length, arraysize; if (!(ctptr->ct_flags & CT_POINTER)) { PyErr_SetString(PyExc_TypeError, "first arg must be a pointer ctype"); return NULL; } ctitem = ctptr->ct_itemdescr; if (ctitem->ct_size < 0) { PyErr_Format(PyExc_ValueError, "array item of unknown size: '%s'", ctitem->ct_name); return NULL; } if (lengthobj == Py_None) { sprintf(extra_text, "[]"); length = -1; arraysize = -1; } else { length = PyNumber_AsSsize_t(lengthobj, PyExc_OverflowError); if (length < 0) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "negative array length"); return NULL; } sprintf(extra_text, "[%llu]", (unsigned PY_LONG_LONG)length); arraysize = length * ctitem->ct_size; if (length > 0 && (arraysize / length) != ctitem->ct_size) { PyErr_SetString(PyExc_OverflowError, "array size would overflow a Py_ssize_t"); return NULL; } } td = ctypedescr_new_on_top(ctitem, extra_text, 0); if (td == NULL) return NULL; Py_INCREF(ctptr); td->ct_stuff = (PyObject *)ctptr; td->ct_size = arraysize; td->ct_length = length; td->ct_flags = CT_ARRAY; return (PyObject *)td; } static PyObject *b_new_void_type(PyObject *self, PyObject *args) { int name_size = strlen("void") + 1; CTypeDescrObject *td = ctypedescr_new(name_size); if (td == NULL) return NULL; memcpy(td->ct_name, "void", name_size); td->ct_size = -1; td->ct_flags = CT_VOID | CT_IS_OPAQUE; td->ct_name_position = strlen("void"); return (PyObject *)td; } static PyObject *_b_struct_or_union_type(const char *name, int flag) { int namelen = strlen(name); CTypeDescrObject *td = ctypedescr_new(namelen + 1); if (td == NULL) return NULL; td->ct_size = -1; td->ct_length = -1; td->ct_flags = flag | CT_IS_OPAQUE; memcpy(td->ct_name, name, namelen + 1); td->ct_name_position = namelen; return (PyObject *)td; } static PyObject *b_new_struct_type(PyObject *self, PyObject *args) { char *name; int flag; if (!PyArg_ParseTuple(args, "s:new_struct_type", &name)) return NULL; flag = CT_STRUCT; if (strcmp(name, "struct _IO_FILE") == 0 || strcmp(name, "FILE") == 0) flag |= CT_IS_FILE; return _b_struct_or_union_type(name, flag); } static PyObject *b_new_union_type(PyObject *self, PyObject *args) { char *name; if (!PyArg_ParseTuple(args, "s:new_union_type", &name)) return NULL; return _b_struct_or_union_type(name, CT_UNION); } static CFieldObject * _add_field(PyObject *interned_fields, PyObject *fname, CTypeDescrObject *ftype, Py_ssize_t offset, int bitshift, int fbitsize) { int err; Py_ssize_t prev_size; CFieldObject *cf = PyObject_New(CFieldObject, &CField_Type); if (cf == NULL) return NULL; Py_INCREF(ftype); cf->cf_type = ftype; cf->cf_offset = offset; cf->cf_bitshift = bitshift; cf->cf_bitsize = fbitsize; Py_INCREF(fname); PyText_InternInPlace(&fname); prev_size = PyDict_Size(interned_fields); err = PyDict_SetItem(interned_fields, fname, (PyObject *)cf); Py_DECREF(fname); Py_DECREF(cf); if (err < 0) return NULL; if (PyDict_Size(interned_fields) != prev_size + 1) { PyErr_Format(PyExc_KeyError, "duplicate field name '%s'", PyText_AS_UTF8(fname)); return NULL; } return cf; /* borrowed reference */ } #define SF_MSVC_BITFIELDS 0x01 #define SF_GCC_ARM_BITFIELDS 0x02 #define SF_GCC_X86_BITFIELDS 0x10 #define SF_GCC_BIG_ENDIAN 0x04 #define SF_GCC_LITTLE_ENDIAN 0x40 #define SF_PACKED 0x08 static int complete_sflags(int sflags) { /* add one of the SF_xxx_BITFIELDS flags if none is specified */ if (!(sflags & (SF_MSVC_BITFIELDS | SF_GCC_ARM_BITFIELDS | SF_GCC_X86_BITFIELDS))) { #ifdef MS_WIN32 sflags |= SF_MSVC_BITFIELDS; #else # ifdef __arm__ sflags |= SF_GCC_ARM_BITFIELDS; # else sflags |= SF_GCC_X86_BITFIELDS; # endif #endif } /* add one of SF_GCC_xx_ENDIAN if none is specified */ if (!(sflags & (SF_GCC_BIG_ENDIAN | SF_GCC_LITTLE_ENDIAN))) { int _check_endian = 1; if (*(char *)&_check_endian == 0) sflags |= SF_GCC_BIG_ENDIAN; else sflags |= SF_GCC_LITTLE_ENDIAN; } return sflags; } static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) { CTypeDescrObject *ct; PyObject *fields, *interned_fields, *ignored; int is_union, alignment; Py_ssize_t boffset, i, nb_fields, boffsetmax; Py_ssize_t totalsize = -1; int totalalignment = -1; CFieldObject **previous; int prev_bitfield_size, prev_bitfield_free; int sflags = 0; if (!PyArg_ParseTuple(args, "O!O!|Onii:complete_struct_or_union", &CTypeDescr_Type, &ct, &PyList_Type, &fields, &ignored, &totalsize, &totalalignment, &sflags)) return NULL; sflags = complete_sflags(sflags); if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) == (CT_STRUCT|CT_IS_OPAQUE)) { is_union = 0; } else if ((ct->ct_flags & (CT_UNION|CT_IS_OPAQUE)) == (CT_UNION|CT_IS_OPAQUE)) { is_union = 1; } else { PyErr_SetString(PyExc_TypeError, "first arg must be a non-initialized struct or union ctype"); return NULL; } alignment = 1; boffset = 0; /* this number is in *bits*, not bytes! */ boffsetmax = 0; /* the maximum value of boffset, in bits too */ prev_bitfield_size = 0; prev_bitfield_free = 0; nb_fields = PyList_GET_SIZE(fields); interned_fields = PyDict_New(); if (interned_fields == NULL) return NULL; previous = (CFieldObject **)&ct->ct_extra; for (i=0; ict_size < 0) { if ((ftype->ct_flags & CT_ARRAY) && fbitsize < 0 && (i == nb_fields - 1 || foffset != -1)) { ct->ct_flags |= CT_WITH_VAR_ARRAY; } else { PyErr_Format(PyExc_TypeError, "field '%s.%s' has ctype '%s' of unknown size", ct->ct_name, PyText_AS_UTF8(fname), ftype->ct_name); goto error; } } if (is_union) boffset = 0; /* reset each field at offset 0 */ /* update the total alignment requirement, but skip it if the field is an anonymous bitfield or if SF_PACKED */ falign = (sflags & SF_PACKED) ? 1 : get_alignment(ftype); if (falign < 0) goto error; do_align = 1; if (!(sflags & SF_GCC_ARM_BITFIELDS) && fbitsize >= 0) { if (!(sflags & SF_MSVC_BITFIELDS)) { /* GCC: anonymous bitfields (of any size) don't cause alignment */ do_align = PyText_GetSize(fname) > 0; } else { /* MSVC: zero-sized bitfields don't cause alignment */ do_align = fbitsize > 0; } } if (alignment < falign && do_align) alignment = falign; if (fbitsize < 0) { /* not a bitfield: common case */ int bs_flag; if (ftype->ct_flags & CT_ARRAY && ftype->ct_length == 0) bs_flag = BS_EMPTY_ARRAY; else bs_flag = BS_REGULAR; /* align this field to its own 'falign' by inserting padding */ boffset = (boffset + falign*8-1) & ~(falign*8-1); /* bits! */ if (foffset >= 0) { /* a forced field position: ignore the offset just computed, except to know if we must set CT_CUSTOM_FIELD_POS */ if (boffset != foffset * 8) ct->ct_flags |= CT_CUSTOM_FIELD_POS; boffset = foffset * 8; } if (PyText_GetSize(fname) == 0 && ftype->ct_flags & (CT_STRUCT|CT_UNION)) { /* a nested anonymous struct or union */ CFieldObject *cfsrc = (CFieldObject *)ftype->ct_extra; for (; cfsrc != NULL; cfsrc = cfsrc->cf_next) { /* broken complexity in the call to get_field_name(), but we'll assume you never do that with nested anonymous structures with thousand of fields */ *previous = _add_field(interned_fields, get_field_name(ftype, cfsrc), cfsrc->cf_type, boffset / 8 + cfsrc->cf_offset, cfsrc->cf_bitshift, cfsrc->cf_bitsize); if (*previous == NULL) goto error; previous = &(*previous)->cf_next; } /* always forbid such structures from being passed by value */ ct->ct_flags |= CT_CUSTOM_FIELD_POS; } else { *previous = _add_field(interned_fields, fname, ftype, boffset / 8, bs_flag, -1); if (*previous == NULL) goto error; previous = &(*previous)->cf_next; } if (ftype->ct_size >= 0) boffset += ftype->ct_size * 8; prev_bitfield_size = 0; } else { /* this is the case of a bitfield */ Py_ssize_t field_offset_bytes; int bits_already_occupied, bitshift; if (foffset >= 0) { PyErr_Format(PyExc_TypeError, "field '%s.%s' is a bitfield, " "but a fixed offset is specified", ct->ct_name, PyText_AS_UTF8(fname)); goto error; } if (!(ftype->ct_flags & (CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_UNSIGNED | CT_PRIMITIVE_CHAR))) { PyErr_Format(PyExc_TypeError, "field '%s.%s' declared as '%s' cannot be a bit field", ct->ct_name, PyText_AS_UTF8(fname), ftype->ct_name); goto error; } if (fbitsize > 8 * ftype->ct_size) { PyErr_Format(PyExc_TypeError, "bit field '%s.%s' is declared '%s:%d', which " "exceeds the width of the type", ct->ct_name, PyText_AS_UTF8(fname), ftype->ct_name, fbitsize); goto error; } /* compute the starting position of the theoretical field that covers a complete 'ftype', inside of which we will locate the real bitfield */ field_offset_bytes = boffset / 8; field_offset_bytes &= ~(falign - 1); if (fbitsize == 0) { if (PyText_GetSize(fname) > 0) { PyErr_Format(PyExc_TypeError, "field '%s.%s' is declared with :0", ct->ct_name, PyText_AS_UTF8(fname)); goto error; } if (!(sflags & SF_MSVC_BITFIELDS)) { /* GCC's notion of "ftype :0;" */ /* pad boffset to a value aligned for "ftype" */ if (boffset > field_offset_bytes * 8) { field_offset_bytes += falign; assert(boffset < field_offset_bytes * 8); } boffset = field_offset_bytes * 8; } else { /* MSVC's notion of "ftype :0;" */ /* Mostly ignored. It seems they only serve as separator between other bitfields, to force them into separate words. */ } prev_bitfield_size = 0; } else { if (!(sflags & SF_MSVC_BITFIELDS)) { /* GCC's algorithm */ /* Can the field start at the offset given by 'boffset'? It can if it would entirely fit into an aligned ftype field. */ bits_already_occupied = boffset - (field_offset_bytes * 8); if (bits_already_occupied + fbitsize > 8 * ftype->ct_size) { /* it would not fit, we need to start at the next allowed position */ if ((sflags & SF_PACKED) && (bits_already_occupied & 7)) { PyErr_Format(PyExc_NotImplementedError, "with 'packed', gcc would compile field " "'%s.%s' to reuse some bits in the previous " "field", ct->ct_name, PyText_AS_UTF8(fname)); goto error; } field_offset_bytes += falign; assert(boffset < field_offset_bytes * 8); boffset = field_offset_bytes * 8; bitshift = 0; } else { bitshift = bits_already_occupied; assert(bitshift >= 0); } boffset += fbitsize; } else { /* MSVC's algorithm */ /* A bitfield is considered as taking the full width of their declared type. It can share some bits with the previous field only if it was also a bitfield and used a type of the same size. */ if (prev_bitfield_size == ftype->ct_size && prev_bitfield_free >= fbitsize) { /* yes: reuse */ bitshift = 8 * prev_bitfield_size - prev_bitfield_free; } else { /* no: start a new full field */ boffset = (boffset + falign*8-1) & ~(falign*8-1); /*align*/ boffset += ftype->ct_size * 8; bitshift = 0; prev_bitfield_size = ftype->ct_size; prev_bitfield_free = 8 * prev_bitfield_size; } prev_bitfield_free -= fbitsize; field_offset_bytes = boffset / 8 - ftype->ct_size; } if (sflags & SF_GCC_BIG_ENDIAN) bitshift = 8 * ftype->ct_size - fbitsize - bitshift; *previous = _add_field(interned_fields, fname, ftype, field_offset_bytes, bitshift, fbitsize); if (*previous == NULL) goto error; previous = &(*previous)->cf_next; } } if (boffset > boffsetmax) boffsetmax = boffset; } *previous = NULL; /* Like C, if the size of this structure would be zero, we compute it as 1 instead. But for ctypes support, we allow the manually- specified totalsize to be zero in this case. */ boffsetmax = (boffsetmax + 7) / 8; /* bits -> bytes */ if (totalsize < 0) { totalsize = (boffsetmax + alignment - 1) & ~(alignment-1); if (totalsize == 0) totalsize = 1; } else if (totalsize < boffsetmax) { PyErr_Format(PyExc_TypeError, "%s cannot be of size %zd: there are fields at least " "up to %zd", ct->ct_name, totalsize, boffsetmax); goto error; } ct->ct_size = totalsize; ct->ct_length = totalalignment < 0 ? alignment : totalalignment; ct->ct_stuff = interned_fields; ct->ct_flags &= ~CT_IS_OPAQUE; Py_INCREF(Py_None); return Py_None; error: Py_DECREF(interned_fields); return NULL; } struct funcbuilder_s { Py_ssize_t nb_bytes; char *bufferp; ffi_type **atypes; ffi_type *rtype; Py_ssize_t nargs; CTypeDescrObject *fct; }; static void *fb_alloc(struct funcbuilder_s *fb, Py_ssize_t size) { if (fb->bufferp == NULL) { fb->nb_bytes += size; return NULL; } else { char *result = fb->bufferp; fb->bufferp += size; return result; } } static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct, int is_result_type) { if (ct->ct_flags & CT_PRIMITIVE_ANY) { return (ffi_type *)ct->ct_extra; } else if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { return &ffi_type_pointer; } else if ((ct->ct_flags & CT_VOID) && is_result_type) { return &ffi_type_void; } if (ct->ct_size <= 0) { PyErr_Format(PyExc_TypeError, ct->ct_size < 0 ? "ctype '%s' has incomplete type" : "ctype '%s' has size 0", ct->ct_name); return NULL; } if (ct->ct_flags & CT_STRUCT) { ffi_type *ffistruct, *ffifield; ffi_type **elements; Py_ssize_t i, n, nflat; CFieldObject *cf; /* We can't pass a struct that was completed by verify(). Issue: assume verify() is given "struct { long b; ...; }". Then it will complete it in the same way whether it is actually "struct { long a, b; }" or "struct { double a; long b; }". But on 64-bit UNIX, these two structs are passed by value differently: e.g. on x86-64, "b" ends up in register "rsi" in the first case and "rdi" in the second case. Another reason for CT_CUSTOM_FIELD_POS would be anonymous nested structures: we lost the information about having it here, so better safe (and forbid it) than sorry (and maybe crash). */ if (ct->ct_flags & CT_CUSTOM_FIELD_POS) { PyErr_SetString(PyExc_TypeError, "cannot pass as an argument a struct that was completed " "with verify() (see _cffi_backend.c for details of why)"); return NULL; } n = PyDict_Size(ct->ct_stuff); nflat = 0; /* walk the fields, expanding arrays into repetitions; first, only count how many flattened fields there are */ cf = (CFieldObject *)ct->ct_extra; for (i=0; icf_bitshift >= 0) { PyErr_SetString(PyExc_NotImplementedError, "cannot pass as argument or return value " "a struct with bit fields"); return NULL; } flat = 1; ct = cf->cf_type; while (ct->ct_flags & CT_ARRAY) { flat *= ct->ct_length; ct = ct->ct_itemdescr; } if (flat <= 0) { PyErr_SetString(PyExc_NotImplementedError, "cannot pass as argument or return value " "a struct with a zero-length array"); return NULL; } nflat += flat; cf = cf->cf_next; } assert(cf == NULL); /* next, allocate and fill the flattened list */ elements = fb_alloc(fb, (nflat + 1) * sizeof(ffi_type*)); nflat = 0; cf = (CFieldObject *)ct->ct_extra; for (i=0; icf_type; while (ct->ct_flags & CT_ARRAY) { flat *= ct->ct_length; ct = ct->ct_itemdescr; } ffifield = fb_fill_type(fb, ct, 0); if (elements != NULL) { for (j=0; jcf_next; } /* finally, allocate the FFI_TYPE_STRUCT */ ffistruct = fb_alloc(fb, sizeof(ffi_type)); if (ffistruct != NULL) { elements[nflat] = NULL; ffistruct->size = ct->ct_size; ffistruct->alignment = ct->ct_length; ffistruct->type = FFI_TYPE_STRUCT; ffistruct->elements = elements; } return ffistruct; } else { PyErr_Format(PyExc_NotImplementedError, "ctype '%s' not supported as argument or return value", ct->ct_name); return NULL; } } #define ALIGN_ARG(n) ((n) + 7) & ~7 static int fb_build(struct funcbuilder_s *fb, PyObject *fargs, CTypeDescrObject *fresult) { Py_ssize_t i, nargs = PyTuple_GET_SIZE(fargs); Py_ssize_t exchange_offset; cif_description_t *cif_descr; /* ffi buffer: start with a cif_description */ cif_descr = fb_alloc(fb, sizeof(cif_description_t) + nargs * sizeof(Py_ssize_t)); /* ffi buffer: next comes an array of 'ffi_type*', one per argument */ fb->atypes = fb_alloc(fb, nargs * sizeof(ffi_type*)); fb->nargs = nargs; /* ffi buffer: next comes the result type */ fb->rtype = fb_fill_type(fb, fresult, 1); if (PyErr_Occurred()) return -1; if (cif_descr != NULL) { /* exchange data size */ /* first, enough room for an array of 'nargs' pointers */ exchange_offset = nargs * sizeof(void*); exchange_offset = ALIGN_ARG(exchange_offset); cif_descr->exchange_offset_arg[0] = exchange_offset; /* then enough room for the result --- which means at least sizeof(ffi_arg), according to the ffi docs */ i = fb->rtype->size; if (i < (Py_ssize_t)sizeof(ffi_arg)) i = sizeof(ffi_arg); exchange_offset += i; } else exchange_offset = 0; /* not used */ /* loop over the arguments */ for (i=0; ict_flags & CT_ARRAY) farg = (CTypeDescrObject *)farg->ct_stuff; /* ffi buffer: fill in the ffi for the i'th argument */ assert(farg != NULL); atype = fb_fill_type(fb, farg, 0); if (PyErr_Occurred()) return -1; if (fb->atypes != NULL) { fb->atypes[i] = atype; /* exchange data size */ exchange_offset = ALIGN_ARG(exchange_offset); cif_descr->exchange_offset_arg[1 + i] = exchange_offset; exchange_offset += atype->size; } } if (cif_descr != NULL) { /* exchange data size */ cif_descr->exchange_size = exchange_offset; } return 0; } #undef ALIGN_ARG static void fb_cat_name(struct funcbuilder_s *fb, char *piece, int piecelen) { if (fb->bufferp == NULL) { fb->nb_bytes += piecelen; } else { memcpy(fb->bufferp, piece, piecelen); fb->bufferp += piecelen; } } static int fb_build_name(struct funcbuilder_s *fb, PyObject *fargs, CTypeDescrObject *fresult, int ellipsis) { Py_ssize_t i, nargs = PyTuple_GET_SIZE(fargs); fb->nargs = nargs; /* name: the function type name we build here is, like in C, made as follows: RESULT_TYPE_HEAD (*)(ARG_1_TYPE, ARG_2_TYPE, etc) RESULT_TYPE_TAIL */ fb_cat_name(fb, fresult->ct_name, fresult->ct_name_position); fb_cat_name(fb, "(*)(", 4); if (fb->fct) { i = fresult->ct_name_position + 2; /* between '(*' and ')(' */ fb->fct->ct_name_position = i; } /* loop over the arguments */ for (i=0; i 0) fb_cat_name(fb, ", ", 2); fb_cat_name(fb, farg->ct_name, strlen(farg->ct_name)); } /* name: add the '...' if needed */ if (ellipsis) { if (nargs > 0) fb_cat_name(fb, ", ", 2); fb_cat_name(fb, "...", 3); } /* name: concatenate the tail of the result type */ fb_cat_name(fb, ")", 1); fb_cat_name(fb, fresult->ct_name + fresult->ct_name_position, strlen(fresult->ct_name) - fresult->ct_name_position + 1); return 0; } static CTypeDescrObject *fb_prepare_ctype(struct funcbuilder_s *fb, PyObject *fargs, CTypeDescrObject *fresult, int ellipsis) { CTypeDescrObject *fct; fb->nb_bytes = 0; fb->bufferp = NULL; fb->fct = NULL; /* compute the total size needed for the name */ if (fb_build_name(fb, fargs, fresult, ellipsis) < 0) return NULL; /* allocate the function type */ fct = ctypedescr_new(fb->nb_bytes); if (fct == NULL) return NULL; fb->fct = fct; /* call again fb_build_name() to really build the ct_name */ fb->bufferp = fct->ct_name; if (fb_build_name(fb, fargs, fresult, ellipsis) < 0) goto error; assert(fb->bufferp == fct->ct_name + fb->nb_bytes); fct->ct_extra = NULL; fct->ct_size = sizeof(void(*)(void)); fct->ct_flags = CT_FUNCTIONPTR; return fct; error: Py_DECREF(fct); return NULL; } static cif_description_t *fb_prepare_cif(PyObject *fargs, CTypeDescrObject *fresult, ffi_abi fabi) { char *buffer; cif_description_t *cif_descr; struct funcbuilder_s funcbuffer; funcbuffer.nb_bytes = 0; funcbuffer.bufferp = NULL; /* compute the total size needed in the buffer for libffi */ if (fb_build(&funcbuffer, fargs, fresult) < 0) return NULL; /* allocate the buffer */ buffer = PyObject_Malloc(funcbuffer.nb_bytes); if (buffer == NULL) { PyErr_NoMemory(); return NULL; } /* call again fb_build() to really build the libffi data structures */ funcbuffer.bufferp = buffer; if (fb_build(&funcbuffer, fargs, fresult) < 0) goto error; assert(funcbuffer.bufferp == buffer + funcbuffer.nb_bytes); cif_descr = (cif_description_t *)buffer; if (ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs, funcbuffer.rtype, funcbuffer.atypes) != FFI_OK) { PyErr_SetString(PyExc_SystemError, "libffi failed to build this function type"); goto error; } return cif_descr; error: PyObject_Free(buffer); return NULL; } static PyObject *b_new_function_type(PyObject *self, PyObject *args) { PyObject *fargs, *fabiobj; CTypeDescrObject *fresult; CTypeDescrObject *fct; int ellipsis = 0, fabi = FFI_DEFAULT_ABI; struct funcbuilder_s funcbuilder; Py_ssize_t i; if (!PyArg_ParseTuple(args, "O!O!|ii:new_function_type", &PyTuple_Type, &fargs, &CTypeDescr_Type, &fresult, &ellipsis, &fabi)) return NULL; if (fresult->ct_flags & CT_UNION) { PyErr_SetString(PyExc_NotImplementedError, "function returning a union"); return NULL; } if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) || (fresult->ct_flags & CT_ARRAY)) { char *msg; if (fresult->ct_flags & CT_IS_OPAQUE) msg = "result type '%s' is opaque"; else msg = "invalid result type: '%s'"; PyErr_Format(PyExc_TypeError, msg, fresult->ct_name); return NULL; } fct = fb_prepare_ctype(&funcbuilder, fargs, fresult, ellipsis); if (fct == NULL) return NULL; if (!ellipsis) { /* Functions with '...' varargs are stored without a cif_descr at all. The cif is computed on every call from the actual types passed in. For all other functions, the cif_descr is computed here. */ cif_description_t *cif_descr; cif_descr = fb_prepare_cif(fargs, fresult, fabi); if (cif_descr == NULL) goto error; fct->ct_extra = (char *)cif_descr; } /* build the signature, given by a tuple of ctype objects */ fct->ct_stuff = PyTuple_New(2 + funcbuilder.nargs); if (fct->ct_stuff == NULL) goto error; fabiobj = PyInt_FromLong(fabi); if (fabiobj == NULL) goto error; PyTuple_SET_ITEM(fct->ct_stuff, 0, fabiobj); Py_INCREF(fresult); PyTuple_SET_ITEM(fct->ct_stuff, 1, (PyObject *)fresult); for (i=0; ict_flags & CT_ARRAY) o = ((CTypeDescrObject *)o)->ct_stuff; Py_INCREF(o); PyTuple_SET_ITEM(fct->ct_stuff, 2 + i, o); } fct->ct_size = sizeof(void(*)(void)); fct->ct_flags = CT_FUNCTIONPTR; return (PyObject *)fct; error: Py_DECREF(fct); return NULL; } static int convert_from_object_fficallback(char *result, CTypeDescrObject *ctype, PyObject *pyobj) { /* work work work around a libffi irregularity: for integer return types we have to fill at least a complete 'ffi_arg'-sized result buffer. */ if (ctype->ct_size < (Py_ssize_t)sizeof(ffi_arg)) { if (ctype->ct_flags & CT_VOID) { if (pyobj == Py_None) { return 0; } else { PyErr_SetString(PyExc_TypeError, "callback with the return type 'void' must return None"); return -1; } } if (ctype->ct_flags & CT_PRIMITIVE_SIGNED) { PY_LONG_LONG value; /* It's probably fine to always zero-extend, but you never know: maybe some code somewhere expects a negative 'short' result to be returned into EAX as a 32-bit negative number. Better safe than sorry. This code is about that case. Let's ignore this for enums. */ /* do a first conversion only to detect overflows. This conversion produces stuff that is otherwise ignored. */ if (convert_from_object(result, ctype, pyobj) < 0) return -1; /* manual inlining and tweaking of convert_from_object() in order to write a whole 'ffi_arg'. */ value = _my_PyLong_AsLongLong(pyobj); if (value == -1 && PyErr_Occurred()) return -1; write_raw_integer_data(result, value, sizeof(ffi_arg)); return 0; } else if (ctype->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_UNSIGNED)) { /* zero extension: fill the '*result' with zeros, and (on big- endian machines) correct the 'result' pointer to write to */ memset(result, 0, sizeof(ffi_arg)); #ifdef WORDS_BIGENDIAN result += (sizeof(ffi_arg) - ctype->ct_size); #endif } } return convert_from_object(result, ctype, pyobj); } static void _my_PyErr_WriteUnraisable(PyObject *obj, char *extra_error_line) { /* like PyErr_WriteUnraisable(), but write a full traceback */ PyObject *f, *t, *v, *tb; PyErr_Fetch(&t, &v, &tb); #if PY_MAJOR_VERSION >= 3 /* jump through hoops to ensure the tb is attached to v, on Python 3 */ PyErr_NormalizeException(&t, &v, &tb); if (tb == NULL) { tb = Py_None; Py_INCREF(tb); } PyException_SetTraceback(v, tb); #endif f = PySys_GetObject("stderr"); if (f != NULL) { PyFile_WriteString("From callback ", f); PyFile_WriteObject(obj, f, 0); PyFile_WriteString(":\n", f); if (extra_error_line != NULL) PyFile_WriteString(extra_error_line, f); PyErr_Display(t, v, tb); } Py_XDECREF(t); Py_XDECREF(v); Py_XDECREF(tb); } static void invoke_callback(ffi_cif *cif, void *result, void **args, void *userdata) { save_errno(); { #ifdef WITH_THREAD PyGILState_STATE state = PyGILState_Ensure(); #endif PyObject *cb_args = (PyObject *)userdata; CTypeDescrObject *ct = (CTypeDescrObject *)PyTuple_GET_ITEM(cb_args, 0); PyObject *signature = ct->ct_stuff; PyObject *py_ob = PyTuple_GET_ITEM(cb_args, 1); PyObject *py_args = NULL; PyObject *py_res = NULL; PyObject *py_rawerr; Py_ssize_t i, n; char *extra_error_line = NULL; #define SIGNATURE(i) ((CTypeDescrObject *)PyTuple_GET_ITEM(signature, i)) Py_INCREF(cb_args); n = PyTuple_GET_SIZE(signature) - 2; py_args = PyTuple_New(n); if (py_args == NULL) goto error; for (i=0; ict_size > 0) { py_rawerr = PyTuple_GET_ITEM(cb_args, 2); memcpy(result, PyBytes_AS_STRING(py_rawerr), PyBytes_GET_SIZE(py_rawerr)); } goto done; } #undef SIGNATURE } static PyObject *b_callback(PyObject *self, PyObject *args) { CTypeDescrObject *ct, *ctresult; CDataObject *cd; PyObject *ob, *error_ob = Py_None; PyObject *py_rawerr, *infotuple = NULL; cif_description_t *cif_descr; ffi_closure *closure; Py_ssize_t size; if (!PyArg_ParseTuple(args, "O!O|O:callback", &CTypeDescr_Type, &ct, &ob, &error_ob)) return NULL; if (!(ct->ct_flags & CT_FUNCTIONPTR)) { PyErr_Format(PyExc_TypeError, "expected a function ctype, got '%s'", ct->ct_name); return NULL; } if (!PyCallable_Check(ob)) { PyErr_Format(PyExc_TypeError, "expected a callable object, not %.200s", Py_TYPE(ob)->tp_name); return NULL; } ctresult = (CTypeDescrObject *)PyTuple_GET_ITEM(ct->ct_stuff, 1); size = ctresult->ct_size; if (size < (Py_ssize_t)sizeof(ffi_arg)) size = sizeof(ffi_arg); py_rawerr = PyBytes_FromStringAndSize(NULL, size); if (py_rawerr == NULL) return NULL; memset(PyBytes_AS_STRING(py_rawerr), 0, size); if (error_ob != Py_None) { if (convert_from_object_fficallback( PyBytes_AS_STRING(py_rawerr), ctresult, error_ob) < 0) { Py_DECREF(py_rawerr); return NULL; } } infotuple = Py_BuildValue("OOO", ct, ob, py_rawerr); Py_DECREF(py_rawerr); if (infotuple == NULL) return NULL; closure = cffi_closure_alloc(); cd = PyObject_GC_New(CDataObject, &CDataOwningGC_Type); if (cd == NULL) goto error; Py_INCREF(ct); cd->c_type = ct; cd->c_data = (char *)closure; cd->c_weakreflist = NULL; PyObject_GC_Track(cd); cif_descr = (cif_description_t *)ct->ct_extra; if (cif_descr == NULL) { PyErr_SetString(PyExc_NotImplementedError, "callbacks with '...'"); goto error; } if (ffi_prep_closure(closure, &cif_descr->cif, invoke_callback, infotuple) != FFI_OK) { PyErr_SetString(PyExc_SystemError, "libffi failed to build this callback"); goto error; } assert(closure->user_data == infotuple); #ifdef WITH_THREAD PyEval_InitThreads(); #endif return (PyObject *)cd; error: closure->user_data = NULL; if (cd == NULL) cffi_closure_free(closure); else Py_DECREF(cd); Py_XDECREF(infotuple); return NULL; } static PyObject *b_new_enum_type(PyObject *self, PyObject *args) { char *ename; PyObject *enumerators, *enumvalues; PyObject *dict1 = NULL, *dict2 = NULL, *combined = NULL, *tmpkey = NULL; int name_size; CTypeDescrObject *td, *basetd; Py_ssize_t i, n; if (!PyArg_ParseTuple(args, "sO!O!O!:new_enum_type", &ename, &PyTuple_Type, &enumerators, &PyTuple_Type, &enumvalues, &CTypeDescr_Type, &basetd)) return NULL; n = PyTuple_GET_SIZE(enumerators); if (n != PyTuple_GET_SIZE(enumvalues)) { PyErr_SetString(PyExc_ValueError, "tuple args must have the same size"); return NULL; } if (!(basetd->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED))) { PyErr_SetString(PyExc_TypeError, "expected a primitive signed or unsigned base type"); return NULL; } dict1 = PyDict_New(); if (dict1 == NULL) goto error; dict2 = PyDict_New(); if (dict2 == NULL) goto error; for (i=n; --i >= 0; ) { long long lvalue; PyObject *value = PyTuple_GET_ITEM(enumvalues, i); tmpkey = PyTuple_GET_ITEM(enumerators, i); Py_INCREF(tmpkey); if (!PyText_Check(tmpkey)) { #if PY_MAJOR_VERSION < 3 if (PyUnicode_Check(tmpkey)) { char *text = PyText_AsUTF8(tmpkey); if (text == NULL) goto error; Py_DECREF(tmpkey); tmpkey = PyString_FromString(text); if (tmpkey == NULL) goto error; } else #endif { PyErr_SetString(PyExc_TypeError, "enumerators must be a list of strings"); goto error; } } if (convert_from_object((char*)&lvalue, basetd, value) < 0) goto error; /* out-of-range or badly typed 'value' */ if (PyDict_SetItem(dict1, tmpkey, value) < 0) goto error; if (PyDict_SetItem(dict2, value, tmpkey) < 0) goto error; Py_DECREF(tmpkey); tmpkey = NULL; } combined = PyTuple_Pack(2, dict1, dict2); if (combined == NULL) goto error; Py_CLEAR(dict2); Py_CLEAR(dict1); name_size = strlen(ename) + 1; td = ctypedescr_new(name_size); if (td == NULL) goto error; memcpy(td->ct_name, ename, name_size); td->ct_stuff = combined; td->ct_size = basetd->ct_size; td->ct_length = basetd->ct_length; /* alignment */ td->ct_extra = basetd->ct_extra; /* ffi type */ td->ct_flags = basetd->ct_flags | CT_IS_ENUM; td->ct_name_position = name_size - 1; return (PyObject *)td; error: Py_XDECREF(tmpkey); Py_XDECREF(combined); Py_XDECREF(dict2); Py_XDECREF(dict1); return NULL; } static PyObject *b_alignof(PyObject *self, PyObject *arg) { int align; if (!CTypeDescr_Check(arg)) { PyErr_SetString(PyExc_TypeError, "expected a 'ctype' object"); return NULL; } align = get_alignment((CTypeDescrObject *)arg); if (align < 0) return NULL; return PyInt_FromLong(align); } static PyObject *b_sizeof(PyObject *self, PyObject *arg) { Py_ssize_t size; if (CData_Check(arg)) { CDataObject *cd = (CDataObject *)arg; if (cd->c_type->ct_flags & CT_ARRAY) size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; else size = cd->c_type->ct_size; } else if (CTypeDescr_Check(arg)) { size = ((CTypeDescrObject *)arg)->ct_size; if (size < 0) { PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown size", ((CTypeDescrObject *)arg)->ct_name); return NULL; } } else { PyErr_SetString(PyExc_TypeError, "expected a 'cdata' or 'ctype' object"); return NULL; } return PyInt_FromSsize_t(size); } static PyObject *b_typeof(PyObject *self, PyObject *arg) { PyObject *res; if (!CData_Check(arg)) { PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); return NULL; } res = (PyObject *)((CDataObject *)arg)->c_type; Py_INCREF(res); return res; } static PyObject *b_typeoffsetof(PyObject *self, PyObject *args) { PyObject *res, *fieldname; CTypeDescrObject *ct; CFieldObject *cf; Py_ssize_t offset; if (!PyArg_ParseTuple(args, "O!O:typeof", &CTypeDescr_Type, &ct, &fieldname)) return NULL; if (fieldname == Py_None) { if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) { PyErr_SetString(PyExc_TypeError, "expected a struct or union ctype"); return NULL; } res = (PyObject *)ct; offset = 0; } else { if (ct->ct_flags & CT_POINTER) ct = ct->ct_itemdescr; if (!(ct->ct_flags & (CT_STRUCT|CT_UNION)) || ct->ct_stuff == NULL) { PyErr_SetString(PyExc_TypeError, "expected an initialized struct or union ctype, " "or a pointer to one"); return NULL; } cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, fieldname); if (cf == NULL) { PyErr_SetObject(PyExc_KeyError, fieldname); return NULL; } if (cf->cf_bitshift >= 0) { PyErr_SetString(PyExc_TypeError, "not supported for bitfields"); return NULL; } res = (PyObject *)cf->cf_type; offset = cf->cf_offset; } return Py_BuildValue("(On)", res, offset); } static PyObject *b_rawaddressof(PyObject *self, PyObject *args) { CTypeDescrObject *ct; CDataObject *cd; Py_ssize_t offset = 0; if (!PyArg_ParseTuple(args, "O!O!|n:rawaddressof", &CTypeDescr_Type, &ct, &CData_Type, &cd, &offset)) return NULL; if ((cd->c_type->ct_flags & (CT_STRUCT|CT_UNION|CT_IS_PTR_TO_OWNED)) == 0) { PyErr_SetString(PyExc_TypeError, "expected a 'cdata struct-or-union' object"); return NULL; } if ((ct->ct_flags & CT_POINTER) == 0) { PyErr_SetString(PyExc_TypeError, "expected a pointer ctype"); return NULL; } return new_simple_cdata(cd->c_data + offset, ct); } static PyObject *b_getcname(PyObject *self, PyObject *args) { CTypeDescrObject *ct; char *replace_with, *p, *s; Py_ssize_t namelen, replacelen; if (!PyArg_ParseTuple(args, "O!s:getcname", &CTypeDescr_Type, &ct, &replace_with)) return NULL; namelen = strlen(ct->ct_name); replacelen = strlen(replace_with); s = p = alloca(namelen + replacelen + 1); memcpy(p, ct->ct_name, ct->ct_name_position); p += ct->ct_name_position; memcpy(p, replace_with, replacelen); p += replacelen; memcpy(p, ct->ct_name + ct->ct_name_position, namelen - ct->ct_name_position); return PyText_FromStringAndSize(s, namelen + replacelen); } static PyObject *b_string(PyObject *self, PyObject *args) { CDataObject *cd; Py_ssize_t maxlen = -1; if (!PyArg_ParseTuple(args, "O!|n:string", &CData_Type, &cd, &maxlen)) return NULL; if (cd->c_type->ct_itemdescr != NULL && cd->c_type->ct_itemdescr->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_UNSIGNED)) { Py_ssize_t length = maxlen; if (cd->c_data == NULL) { PyObject *s = cdata_repr(cd); if (s != NULL) { PyErr_Format(PyExc_RuntimeError, "cannot use string() on %s", PyText_AS_UTF8(s)); Py_DECREF(s); } return NULL; } if (length < 0 && cd->c_type->ct_flags & CT_ARRAY) { length = get_array_length(cd); } if (cd->c_type->ct_itemdescr->ct_size == sizeof(char)) { const char *start = cd->c_data; if (length < 0) { /*READ(start, 1)*/ length = strlen(start); /*READ(start, length)*/ } else { const char *end; /*READ(start, length)*/ end = (const char *)memchr(start, 0, length); if (end != NULL) length = end - start; } return PyBytes_FromStringAndSize(start, length); } #ifdef HAVE_WCHAR_H else if (cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) { const wchar_t *start = (wchar_t *)cd->c_data; assert(cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t)); if (length < 0) { /*READ(start, sizeof(wchar_t))*/ length = 0; while (start[length]) length++; /*READ(start, sizeof(wchar_t) * length)*/ } else { /*READ(start, sizeof(wchar_t) * length)*/ maxlen = length; length = 0; while (length < maxlen && start[length]) length++; } return _my_PyUnicode_FromWideChar(start, length); } #endif } else if (cd->c_type->ct_flags & CT_IS_ENUM) { return convert_cdata_to_enum_string(cd, 0); } else if (cd->c_type->ct_flags & CT_IS_BOOL) { /* fall through to TypeError */ } else if (cd->c_type->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_UNSIGNED)) { /*READ(cd->c_data, cd->c_type->ct_size)*/ if (cd->c_type->ct_size == sizeof(char)) return PyBytes_FromStringAndSize(cd->c_data, 1); #ifdef HAVE_WCHAR_H else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) { assert(cd->c_type->ct_size == sizeof(wchar_t)); return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1); } #endif } PyErr_Format(PyExc_TypeError, "string(): unexpected cdata '%s' argument", cd->c_type->ct_name); return NULL; } static PyObject *b_buffer(PyObject *self, PyObject *args) { CDataObject *cd; Py_ssize_t size = -1; if (!PyArg_ParseTuple(args, "O!|n:buffer", &CData_Type, &cd, &size)) return NULL; if (cd->c_type->ct_flags & CT_POINTER) { if (size < 0) size = cd->c_type->ct_itemdescr->ct_size; } else if (cd->c_type->ct_flags & CT_ARRAY) { if (size < 0) size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; } else { PyErr_Format(PyExc_TypeError, "expected a pointer or array cdata, got '%s'", cd->c_type->ct_name); return NULL; } if (size < 0) { PyErr_Format(PyExc_TypeError, "don't know the size pointed to by '%s'", cd->c_type->ct_name); return NULL; } /*WRITE(cd->c_data, size)*/ return minibuffer_new(cd->c_data, size, (PyObject *)cd); } static PyObject *b_get_errno(PyObject *self, PyObject *noarg) { int err; restore_errno_only(); err = errno; errno = 0; return PyInt_FromLong(err); } static PyObject *b_set_errno(PyObject *self, PyObject *args) { int i; if (!PyArg_ParseTuple(args, "i:set_errno", &i)) return NULL; errno = i; save_errno_only(); errno = 0; Py_INCREF(Py_None); return Py_None; } static PyObject *b_newp_handle(PyObject *self, PyObject *args) { CTypeDescrObject *ct; CDataObject *cd; PyObject *x; if (!PyArg_ParseTuple(args, "O!O", &CTypeDescr_Type, &ct, &x)) return NULL; if (!(ct->ct_flags & CT_IS_VOID_PTR)) { PyErr_Format(PyExc_TypeError, "needs 'void *', got '%s'", ct->ct_name); return NULL; } cd = (CDataObject *)PyObject_GC_New(CDataObject, &CDataOwningGC_Type); if (cd == NULL) return NULL; Py_INCREF(ct); cd->c_type = ct; Py_INCREF(x); cd->c_data = ((char *)x) - 42; cd->c_weakreflist = NULL; PyObject_GC_Track(cd); return (PyObject *)cd; } static PyObject *b_from_handle(PyObject *self, PyObject *arg) { CTypeDescrObject *ct; char *raw; PyObject *x; if (!CData_Check(arg)) { PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); return NULL; } ct = ((CDataObject *)arg)->c_type; raw = ((CDataObject *)arg)->c_data; if (!(ct->ct_flags & CT_CAST_ANYTHING)) { PyErr_Format(PyExc_TypeError, "expected a 'cdata' object with a 'void *' out of " "new_handle(), got '%s'", ct->ct_name); return NULL; } if (!raw) { PyErr_SetString(PyExc_RuntimeError, "cannot use from_handle() on NULL pointer"); return NULL; } x = (PyObject *)(raw + 42); Py_INCREF(x); return x; } static PyObject *b__get_types(PyObject *self, PyObject *noarg) { return PyTuple_Pack(2, (PyObject *)&CData_Type, (PyObject *)&CTypeDescr_Type); } /************************************************************/ static char _testfunc0(char a, char b) { return a + b; } static long _testfunc1(int a, long b) { return (long)a + b; } static PY_LONG_LONG _testfunc2(PY_LONG_LONG a, PY_LONG_LONG b) { return a + b; } static double _testfunc3(float a, double b) { return a + b; } static float _testfunc4(float a, double b) { return (float)(a + b); } static void _testfunc5(void) { errno = errno + 15; } static int *_testfunc6(int *x) { static int y; y = *x - 1000; return &y; } struct _testfunc7_s { unsigned char a1; short a2; }; static short _testfunc7(struct _testfunc7_s inlined) { return inlined.a1 + inlined.a2; } static int _testfunc9(int num, ...) { va_list vargs; int i, total = 0; va_start(vargs, num); for (i=0; ia1 + (int)ptr->a2; } static long double _testfunc19(long double x, int count) { int i; for (i=0; ia1 + ptr->a2; } struct _testfunc21_s { int a, b, c, d, e, f, g, h, i, j; }; static int _testfunc21(struct _testfunc21_s inlined) { return ((inlined.a << 0) + (inlined.b << 1) + (inlined.c << 2) + (inlined.d << 3) + (inlined.e << 4) + (inlined.f << 5) + (inlined.g << 6) + (inlined.h << 7) + (inlined.i << 8) + (inlined.j << 9)); } struct _testfunc22_s { int a[10]; }; static struct _testfunc22_s _testfunc22(struct _testfunc22_s s1, struct _testfunc22_s s2) { struct _testfunc22_s result; int i; for (i=0; i<10; i++) result.a[i] = s1.a[i] - s2.a[i]; return result; } static int _testfunc23(char *p) { if (p) return 1000 * p[0]; return -42; } static PyObject *b__testfunc(PyObject *self, PyObject *args) { /* for testing only */ int i; void *f; if (!PyArg_ParseTuple(args, "i:_testfunc", &i)) return NULL; switch (i) { case 0: f = &_testfunc0; break; case 1: f = &_testfunc1; break; case 2: f = &_testfunc2; break; case 3: f = &_testfunc3; break; case 4: f = &_testfunc4; break; case 5: f = &_testfunc5; break; case 6: f = &_testfunc6; break; case 7: f = &_testfunc7; break; case 8: f = stderr; break; case 9: f = &_testfunc9; break; case 10: f = &_testfunc10; break; case 11: f = &_testfunc11; break; case 12: f = &_testfunc12; break; case 13: f = &_testfunc13; break; case 14: f = &_testfunc14; break; case 15: f = &_testfunc15; break; case 16: f = &_testfunc16; break; case 17: f = &_testfunc17; break; case 18: f = &_testfunc18; break; case 19: f = &_testfunc19; break; case 20: f = &_testfunc20; break; case 21: f = &_testfunc21; break; case 22: f = &_testfunc22; break; case 23: f = &_testfunc23; break; default: PyErr_SetNone(PyExc_ValueError); return NULL; } return PyLong_FromVoidPtr(f); } static PyMethodDef FFIBackendMethods[] = { {"load_library", b_load_library, METH_VARARGS}, {"new_primitive_type", b_new_primitive_type, METH_VARARGS}, {"new_pointer_type", b_new_pointer_type, METH_VARARGS}, {"new_array_type", b_new_array_type, METH_VARARGS}, {"new_void_type", b_new_void_type, METH_NOARGS}, {"new_struct_type", b_new_struct_type, METH_VARARGS}, {"new_union_type", b_new_union_type, METH_VARARGS}, {"complete_struct_or_union", b_complete_struct_or_union, METH_VARARGS}, {"new_function_type", b_new_function_type, METH_VARARGS}, {"new_enum_type", b_new_enum_type, METH_VARARGS}, {"newp", b_newp, METH_VARARGS}, {"cast", b_cast, METH_VARARGS}, {"callback", b_callback, METH_VARARGS}, {"alignof", b_alignof, METH_O}, {"sizeof", b_sizeof, METH_O}, {"typeof", b_typeof, METH_O}, {"typeoffsetof", b_typeoffsetof, METH_VARARGS}, {"rawaddressof", b_rawaddressof, METH_VARARGS}, {"getcname", b_getcname, METH_VARARGS}, {"string", b_string, METH_VARARGS}, {"buffer", b_buffer, METH_VARARGS}, {"get_errno", b_get_errno, METH_NOARGS}, {"set_errno", b_set_errno, METH_VARARGS}, {"newp_handle", b_newp_handle, METH_VARARGS}, {"from_handle", b_from_handle, METH_O}, #ifdef MS_WIN32 {"getwinerror", b_getwinerror, METH_VARARGS}, #endif {"_get_types", b__get_types, METH_NOARGS}, {"_testfunc", b__testfunc, METH_VARARGS}, {NULL, NULL} /* Sentinel */ }; /************************************************************/ /* Functions used by '_cffi_N.so', the generated modules */ #define _cffi_to_c_SIGNED_FN(RETURNTYPE, SIZE) \ static RETURNTYPE _cffi_to_c_i##SIZE(PyObject *obj) { \ PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj); \ if ((tmp > (PY_LONG_LONG)((1ULL<<(SIZE-1)) - 1)) || \ (tmp < (PY_LONG_LONG)(-(1ULL<<(SIZE-1))))) \ if (!PyErr_Occurred()) \ return (RETURNTYPE)_convert_overflow(obj, #SIZE "-bit int"); \ return (RETURNTYPE)tmp; \ } #define _cffi_to_c_UNSIGNED_FN(RETURNTYPE, SIZE) \ static RETURNTYPE _cffi_to_c_u##SIZE(PyObject *obj) { \ unsigned PY_LONG_LONG tmp = _my_PyLong_AsUnsignedLongLong(obj, 1); \ if (tmp > ~(((unsigned PY_LONG_LONG)-2) << (SIZE-1))) \ if (!PyErr_Occurred()) \ return (RETURNTYPE)_convert_overflow(obj, \ #SIZE "-bit unsigned int"); \ return (RETURNTYPE)tmp; \ } _cffi_to_c_SIGNED_FN(int, 8) _cffi_to_c_SIGNED_FN(int, 16) _cffi_to_c_SIGNED_FN(int, 32) _cffi_to_c_SIGNED_FN(PY_LONG_LONG, 64) _cffi_to_c_UNSIGNED_FN(int, 8) _cffi_to_c_UNSIGNED_FN(int, 16) _cffi_to_c_UNSIGNED_FN(unsigned int, 32) _cffi_to_c_UNSIGNED_FN(unsigned PY_LONG_LONG, 64) static PyObject *_cffi_from_c_pointer(char *ptr, CTypeDescrObject *ct) { return convert_to_object((char *)&ptr, ct); } static char *_cffi_to_c_pointer(PyObject *obj, CTypeDescrObject *ct) { char *result; if (convert_from_object((char *)&result, ct, obj) < 0) { if ((ct->ct_flags & CT_POINTER) && (ct->ct_itemdescr->ct_flags & CT_IS_FILE) && PyFile_Check(obj)) { PyErr_Clear(); return (char *)PyFile_AsFile(obj); } return NULL; } return result; } static long double _cffi_to_c_long_double(PyObject *obj) { if (CData_Check(obj) && (((CDataObject *)obj)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { char *data = ((CDataObject *)obj)->c_data; /*READ(data, sizeof(long double))*/ return read_raw_longdouble_data(data); } else return PyFloat_AsDouble(obj); } static _Bool _cffi_to_c__Bool(PyObject *obj) { PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj); if (tmp == 0) return 0; else if (tmp == 1) return 1; else if (PyErr_Occurred()) return (_Bool)-1; else return (_Bool)_convert_overflow(obj, "_Bool"); } static PyObject *_cffi_get_struct_layout(Py_ssize_t nums[]) { PyObject *result; int count = 0; while (nums[count] >= 0) count++; result = PyList_New(count); if (result == NULL) return NULL; while (--count >= 0) { PyObject *o = PyInt_FromSsize_t(nums[count]); if (o == NULL) { Py_DECREF(result); return NULL; } PyList_SET_ITEM(result, count, o); } return result; } static PyObject *_cffi_from_c_char(char x) { return PyBytes_FromStringAndSize(&x, 1); } #ifdef HAVE_WCHAR_H static PyObject *_cffi_from_c_wchar_t(wchar_t x) { return _my_PyUnicode_FromWideChar(&x, 1); } #endif static void *cffi_exports[] = { 0, _cffi_to_c_i8, _cffi_to_c_u8, _cffi_to_c_i16, _cffi_to_c_u16, _cffi_to_c_i32, _cffi_to_c_u32, _cffi_to_c_i64, _cffi_to_c_u64, _convert_to_char, _cffi_from_c_pointer, _cffi_to_c_pointer, _cffi_get_struct_layout, restore_errno, save_errno, _cffi_from_c_char, convert_to_object, convert_from_object, convert_struct_to_owning_object, #ifdef HAVE_WCHAR_H _convert_to_wchar_t, _cffi_from_c_wchar_t, #else 0, 0, #endif _cffi_to_c_long_double, _cffi_to_c__Bool, _prepare_pointer_call_argument, convert_array_from_object, }; /************************************************************/ #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef FFIBackendModuleDef = { PyModuleDef_HEAD_INIT, "_cffi_backend", NULL, -1, FFIBackendMethods, NULL, NULL, NULL, NULL }; #define INITERROR return NULL PyMODINIT_FUNC PyInit__cffi_backend(void) #else #define INITERROR return PyMODINIT_FUNC init_cffi_backend(void) #endif { PyObject *m, *v; v = PySys_GetObject("version"); if (v == NULL || !PyText_Check(v) || strncmp(PyText_AS_UTF8(v), PY_VERSION, 3) != 0) { PyErr_Format(PyExc_ImportError, "this module was compiled for Python %c%c%c", PY_VERSION[0], PY_VERSION[1], PY_VERSION[2]); INITERROR; } #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&FFIBackendModuleDef); #else m = Py_InitModule("_cffi_backend", FFIBackendMethods); #endif if (m == NULL) INITERROR; if (PyType_Ready(&dl_type) < 0) INITERROR; if (PyType_Ready(&CTypeDescr_Type) < 0) INITERROR; if (PyType_Ready(&CField_Type) < 0) INITERROR; if (PyType_Ready(&CData_Type) < 0) INITERROR; if (PyType_Ready(&CDataOwning_Type) < 0) INITERROR; if (PyType_Ready(&CDataOwningGC_Type) < 0) INITERROR; if (PyType_Ready(&CDataIter_Type) < 0) INITERROR; if (PyType_Ready(&MiniBuffer_Type) < 0) INITERROR; v = PyText_FromString("_cffi_backend"); if (v == NULL || PyDict_SetItemString(CData_Type.tp_dict, "__module__", v) < 0) INITERROR; v = PyText_FromString(""); if (v == NULL || PyDict_SetItemString(CData_Type.tp_dict, "__name__", v) < 0) INITERROR; v = PyCapsule_New((void *)cffi_exports, "cffi", NULL); if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0) INITERROR; v = PyText_FromString("0.8.2"); if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0) INITERROR; if (PyModule_AddIntConstant(m, "FFI_DEFAULT_ABI", FFI_DEFAULT_ABI) < 0 || #if defined(MS_WIN32) && !defined(_WIN64) PyModule_AddIntConstant(m, "FFI_STDCALL", FFI_STDCALL) < 0 || #endif #ifdef FFI_CDECL PyModule_AddIntConstant(m, "FFI_CDECL", FFI_CDECL) < 0 || /* win32 */ #else PyModule_AddIntConstant(m, "FFI_CDECL", FFI_DEFAULT_ABI) < 0 || #endif #ifdef MS_WIN32 # ifdef _WIN64 PyModule_AddIntConstant(m, "_WIN", 64) < 0 || /* win64 */ # else PyModule_AddIntConstant(m, "_WIN", 32) < 0 || /* win32 */ # endif #endif PyModule_AddIntConstant(m, "RTLD_LAZY", RTLD_LAZY) < 0 || PyModule_AddIntConstant(m, "RTLD_NOW", RTLD_NOW) < 0 || PyModule_AddIntConstant(m, "RTLD_GLOBAL", RTLD_GLOBAL) < 0 || #ifdef RTLD_LOCAL PyModule_AddIntConstant(m, "RTLD_LOCAL", RTLD_LOCAL) < 0 || #else PyModule_AddIntConstant(m, "RTLD_LOCAL", 0) < 0 || #endif #ifdef RTLD_NODELETE PyModule_AddIntConstant(m, "RTLD_NODELETE", RTLD_NODELETE) < 0 || #endif #ifdef RTLD_NOLOAD PyModule_AddIntConstant(m, "RTLD_NOLOAD", RTLD_NOLOAD) < 0 || #endif #ifdef RTLD_DEEPBIND PyModule_AddIntConstant(m, "RTLD_DEEPBIND", RTLD_DEEPBIND) < 0 || #endif 0) INITERROR; init_errno(); #if PY_MAJOR_VERSION >= 3 if (init_file_emulator() < 0) INITERROR; return m; #endif } cffi-0.8.2/testing/0000755000076400001440000000000012306266401014371 5ustar arigousers00000000000000cffi-0.8.2/testing/test_model.py0000644000076400001440000001046212116052652017105 0ustar arigousers00000000000000from cffi.model import * def test_void_type(): assert void_type.get_c_name() == "void" assert void_type.get_c_name("foo") == "void foo" assert void_type.get_c_name("*foo") == "void *foo" def test_primitive_type(): int_type = PrimitiveType("int") assert int_type.get_c_name() == "int" assert int_type.get_c_name("foo") == "int foo" assert int_type.get_c_name("*foo") == "int *foo" assert int_type.get_c_name("[5]") == "int[5]" def test_raw_function_type(): int_type = PrimitiveType("int") fn_type = RawFunctionType([], int_type, False) assert fn_type.get_c_name() == "int()(void)" assert fn_type.get_c_name("*") == "int( *)(void)" assert fn_type.get_c_name("*foo") == "int( *foo)(void)" fn_type = RawFunctionType([int_type], int_type, False) assert fn_type.get_c_name() == "int()(int)" fn_type = RawFunctionType([int_type] * 2, int_type, False) assert fn_type.get_c_name() == "int()(int, int)" # fn_type = RawFunctionType([int_type], int_type, True) assert fn_type.get_c_name() == "int()(int, ...)" assert fn_type.get_c_name("*foo") == "int( *foo)(int, ...)" # res_type = FunctionPtrType([int_type], int_type, True) fn_type = RawFunctionType([int_type], res_type, True) assert fn_type.get_c_name("x") == "int(*( x)(int, ...))(int, ...)" def test_function_ptr_type(): int_type = PrimitiveType("int") fn_type = FunctionPtrType([], int_type, False) assert fn_type.get_c_name() == "int(*)(void)" assert fn_type.get_c_name("*") == "int(* *)(void)" assert fn_type.get_c_name("*foo") == "int(* *foo)(void)" fn_type = FunctionPtrType([int_type], int_type, False) assert fn_type.get_c_name() == "int(*)(int)" fn_type = FunctionPtrType([int_type] * 2, int_type, False) assert fn_type.get_c_name() == "int(*)(int, int)" # fn_type = FunctionPtrType([int_type], int_type, True) assert fn_type.get_c_name() == "int(*)(int, ...)" def test_pointer_type(): ptr_type = PointerType(PrimitiveType("int")) assert ptr_type.get_c_name("x") == "int * x" def test_const_pointer_type(): ptr_type = ConstPointerType(PrimitiveType("int")) assert ptr_type.get_c_name("x") == "int const * x" ptr_type = ConstPointerType(ArrayType(PrimitiveType("int"), 5)) assert ptr_type.get_c_name("") == "int(const *)[5]" assert ptr_type.get_c_name("*x") == "int(const * *x)[5]" def test_unknown_pointer_type(): ptr_type = unknown_ptr_type("foo_p") assert ptr_type.get_c_name("") == "foo_p" assert ptr_type.get_c_name("x") == "foo_p x" def test_unknown_type(): u_type = unknown_type("foo_t") assert u_type.get_c_name("") == "foo_t" assert u_type.get_c_name("x") == "foo_t x" def test_array_type(): a_type = ArrayType(PrimitiveType("int"), None) assert a_type.get_c_name("") == "int[]" assert a_type.get_c_name("x") == "int x[]" assert a_type.get_c_name("*x") == "int(*x)[]" assert a_type.get_c_name(" *x") == "int(*x)[]" assert a_type.get_c_name("[5]") == "int[5][]" a_type = ArrayType(unknown_type("foo_t"), 5) assert a_type.get_c_name("") == "foo_t[5]" assert a_type.get_c_name("x") == "foo_t x[5]" assert a_type.get_c_name("*x") == "foo_t(*x)[5]" a_type = ArrayType(unknown_ptr_type("foo_p"), None) assert a_type.get_c_name("") == "foo_p[]" assert a_type.get_c_name("x") == "foo_p x[]" assert a_type.get_c_name("*x") == "foo_p(*x)[]" a_type = ArrayType(ConstPointerType(PrimitiveType("int")), None) assert a_type.get_c_name("") == "int const *[]" assert a_type.get_c_name("x") == "int const * x[]" assert a_type.get_c_name("*x") == "int const *(*x)[]" fn_type = FunctionPtrType([], PrimitiveType("int"), False) a_type = ArrayType(fn_type, 5) assert a_type.get_c_name("") == "int(*[5])(void)" assert a_type.get_c_name("x") == "int(* x[5])(void)" assert a_type.get_c_name("*x") == "int(*(*x)[5])(void)" def test_struct_type(): struct_type = StructType("foo_s", None, None, None) assert struct_type.get_c_name() == "struct foo_s" assert struct_type.get_c_name("*x") == "struct foo_s *x" def test_union_type(): union_type = UnionType("foo_s", None, None, None) assert union_type.get_c_name() == "union foo_s" def test_enum_type(): enum_type = EnumType("foo_e", [], []) assert enum_type.get_c_name() == "enum foo_e" cffi-0.8.2/testing/support.py0000644000076400001440000000075512012377165016472 0ustar arigousers00000000000000import sys if sys.version_info < (3,): __all__ = ['u'] class U(object): def __add__(self, other): return eval('u'+repr(other).replace(r'\\u', r'\u') .replace(r'\\U', r'\U')) u = U() assert u+'a\x00b' == eval(r"u'a\x00b'") assert u+'a\u1234b' == eval(r"u'a\u1234b'") assert u+'a\U00012345b' == eval(r"u'a\U00012345b'") else: __all__ = ['u', 'unicode', 'long'] u = "" unicode = str long = int cffi-0.8.2/testing/test_zdistutils.py0000644000076400001440000002634112306266214020230 0ustar arigousers00000000000000import sys, os, imp, math, shutil import py from cffi import FFI, FFIError from cffi.verifier import Verifier, _locate_engine_class, _get_so_suffixes from cffi.ffiplatform import maybe_relative_path from testing.udir import udir class DistUtilsTest(object): def setup_class(self): self.lib_m = "m" if sys.platform == 'win32': #there is a small chance this fails on Mingw via environ $CC import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': self.lib_m = 'msvcrt' def test_locate_engine_class(self): cls = _locate_engine_class(FFI(), self.generic) if self.generic: # asked for the generic engine, which must not generate a # CPython extension module assert not cls._gen_python_module else: # asked for the CPython engine: check that we got it, unless # we are running on top of PyPy, where the generic engine is # always better if '__pypy__' not in sys.builtin_module_names: assert cls._gen_python_module def test_write_source(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!*/\n#include \n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) v.write_source() with open(v.sourcefilename, 'r') as f: data = f.read() assert csrc in data def test_write_source_explicit_filename(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!*/\n#include \n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) v.sourcefilename = filename = str(udir.join('write_source.c')) v.write_source() assert filename == v.sourcefilename with open(filename, 'r') as f: data = f.read() assert csrc in data def test_write_source_to_file_obj(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!*/\n#include \n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) try: from StringIO import StringIO except ImportError: from io import StringIO f = StringIO() v.write_source(file=f) assert csrc in f.getvalue() def test_compile_module(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!*/\n#include \n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) v.compile_module() assert v.get_module_name().startswith('_cffi_') if v.generates_python_module(): mod = imp.load_dynamic(v.get_module_name(), v.modulefilename) assert hasattr(mod, '_cffi_setup') def test_compile_module_explicit_filename(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!2*/\n#include \n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) basename = self.__class__.__name__ + 'test_compile_module' v.modulefilename = filename = str(udir.join(basename + '.so')) v.compile_module() assert filename == v.modulefilename assert v.get_module_name() == basename if v.generates_python_module(): mod = imp.load_dynamic(v.get_module_name(), v.modulefilename) assert hasattr(mod, '_cffi_setup') def test_name_from_checksum_of_cdef(self): names = [] for csrc in ['double', 'double', 'float']: ffi = FFI() ffi.cdef("%s sin(double x);" % csrc) v = Verifier(ffi, "#include ", force_generic_engine=self.generic, libraries=[self.lib_m]) names.append(v.get_module_name()) assert names[0] == names[1] != names[2] def test_name_from_checksum_of_csrc(self): names = [] for csrc in ['123', '123', '1234']: ffi = FFI() ffi.cdef("double sin(double x);") v = Verifier(ffi, csrc, force_generic_engine=self.generic) names.append(v.get_module_name()) assert names[0] == names[1] != names[2] def test_load_library(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!3*/\n#include \n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) library = v.load_library() assert library.sin(12.3) == math.sin(12.3) def test_verifier_args(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!4*/#include "test_verifier_args.h"\n' % self udir.join('test_verifier_args.h').write('#include \n') v = Verifier(ffi, csrc, include_dirs=[str(udir)], force_generic_engine=self.generic, libraries=[self.lib_m]) library = v.load_library() assert library.sin(12.3) == math.sin(12.3) def test_verifier_object_from_ffi(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = "/*6%s*/\n#include " % self lib = ffi.verify(csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) assert lib.sin(12.3) == math.sin(12.3) assert isinstance(ffi.verifier, Verifier) with open(ffi.verifier.sourcefilename, 'r') as f: data = f.read() assert csrc in data def test_extension_object(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*7%s*/' % self + ''' #include #ifndef TEST_EXTENSION_OBJECT # error "define_macros missing" #endif ''' lib = ffi.verify(csrc, define_macros=[('TEST_EXTENSION_OBJECT', '1')], force_generic_engine=self.generic, libraries=[self.lib_m]) assert lib.sin(12.3) == math.sin(12.3) v = ffi.verifier ext = v.get_extension() assert 'distutils.extension.Extension' in str(ext.__class__) assert ext.sources == [maybe_relative_path(v.sourcefilename)] assert ext.name == v.get_module_name() assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')] def test_extension_forces_write_source(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there9!%s*/\n#include \n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) assert not os.path.exists(v.sourcefilename) v.get_extension() assert os.path.exists(v.sourcefilename) def test_extension_object_extra_sources(self): ffi = FFI() ffi.cdef("double test1eoes(double x);") extra_source = str(udir.join('extension_extra_sources.c')) with open(extra_source, 'w') as f: f.write('double test1eoes(double x) { return x * 6.0; }\n') csrc = '/*9%s*/' % self + ''' double test1eoes(double x); /* or #include "extra_sources.h" */ ''' lib = ffi.verify(csrc, sources=[extra_source], force_generic_engine=self.generic) assert lib.test1eoes(7.0) == 42.0 v = ffi.verifier ext = v.get_extension() assert 'distutils.extension.Extension' in str(ext.__class__) assert ext.sources == [maybe_relative_path(v.sourcefilename), extra_source] assert ext.name == v.get_module_name() def test_install_and_reload_module(self, targetpackage='', ext_package=''): KEY = repr(self) if not hasattr(os, 'fork'): py.test.skip("test requires os.fork()") if targetpackage: udir.ensure(targetpackage, dir=1).ensure('__init__.py') sys.path.insert(0, str(udir)) def make_ffi(**verifier_args): ffi = FFI() ffi.cdef("/* %s, %s, %s */" % (KEY, targetpackage, ext_package)) ffi.cdef("double test1iarm(double x);") csrc = "double test1iarm(double x) { return x * 42.0; }" lib = ffi.verify(csrc, force_generic_engine=self.generic, ext_package=ext_package, **verifier_args) return ffi, lib childpid = os.fork() if childpid == 0: # in the child ffi, lib = make_ffi() assert lib.test1iarm(1.5) == 63.0 # "install" the module by moving it into udir (/targetpackage) if targetpackage: target = udir.join(targetpackage) else: target = udir shutil.move(ffi.verifier.modulefilename, str(target)) os._exit(0) # in the parent _, status = os.waitpid(childpid, 0) if not (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0): raise AssertionError # see error above in subprocess from cffi import ffiplatform prev_compile = ffiplatform.compile try: if targetpackage == ext_package: ffiplatform.compile = lambda *args: dont_call_me_any_more # won't find it in tmpdir, but should find it correctly # installed in udir ffi, lib = make_ffi() assert lib.test1iarm(0.5) == 21.0 finally: ffiplatform.compile = prev_compile def test_install_and_reload_module_package(self): self.test_install_and_reload_module(targetpackage='foo_iarmp', ext_package='foo_iarmp') def test_install_and_reload_module_ext_package_not_found(self): self.test_install_and_reload_module(targetpackage='foo_epnf', ext_package='not_found') def test_tag(self): ffi = FFI() ffi.cdef("/* %s test_tag */ double test1tag(double x);" % self) csrc = "double test1tag(double x) { return x - 42.0; }" lib = ffi.verify(csrc, force_generic_engine=self.generic, tag='xxtest_tagxx') assert lib.test1tag(143) == 101.0 assert '_cffi_xxtest_tagxx_' in ffi.verifier.modulefilename def test_modulename(self): ffi = FFI() ffi.cdef("/* %s test_modulename */ double test1foo(double x);" % self) csrc = "double test1foo(double x) { return x - 63.0; }" modname = 'xxtest_modulenamexx%d' % (self.generic,) lib = ffi.verify(csrc, force_generic_engine=self.generic, modulename=modname) assert lib.test1foo(143) == 80.0 suffix = _get_so_suffixes()[0] fn1 = os.path.join(ffi.verifier.tmpdir, modname + '.c') fn2 = os.path.join(ffi.verifier.tmpdir, modname + suffix) assert ffi.verifier.sourcefilename == fn1 assert ffi.verifier.modulefilename == fn2 class TestDistUtilsCPython(DistUtilsTest): generic = False class TestDistUtilsGeneric(DistUtilsTest): generic = True cffi-0.8.2/testing/backend_tests.py0000644000076400001440000016001312306266214017557 0ustar arigousers00000000000000import py import platform import sys, ctypes from cffi import FFI, CDefError from testing.support import * SIZE_OF_INT = ctypes.sizeof(ctypes.c_int) SIZE_OF_LONG = ctypes.sizeof(ctypes.c_long) SIZE_OF_SHORT = ctypes.sizeof(ctypes.c_short) SIZE_OF_PTR = ctypes.sizeof(ctypes.c_void_p) SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar) class BackendTests: def test_integer_ranges(self): ffi = FFI(backend=self.Backend()) for (c_type, size) in [('char', 1), ('short', 2), ('short int', 2), ('', 4), ('int', 4), ('long', SIZE_OF_LONG), ('long int', SIZE_OF_LONG), ('long long', 8), ('long long int', 8), ]: for unsigned in [None, False, True]: c_decl = {None: '', False: 'signed ', True: 'unsigned '}[unsigned] + c_type if c_decl == 'char' or c_decl == '': continue self._test_int_type(ffi, c_decl, size, unsigned) def test_fixedsize_int(self): ffi = FFI(backend=self.Backend()) for size in [1, 2, 4, 8]: self._test_int_type(ffi, 'int%d_t' % (8*size), size, False) self._test_int_type(ffi, 'uint%d_t' % (8*size), size, True) self._test_int_type(ffi, 'intptr_t', SIZE_OF_PTR, False) self._test_int_type(ffi, 'uintptr_t', SIZE_OF_PTR, True) self._test_int_type(ffi, 'ptrdiff_t', SIZE_OF_PTR, False) self._test_int_type(ffi, 'size_t', SIZE_OF_PTR, True) self._test_int_type(ffi, 'ssize_t', SIZE_OF_PTR, False) def _test_int_type(self, ffi, c_decl, size, unsigned): if unsigned: min = 0 max = (1 << (8*size)) - 1 else: min = -(1 << (8*size-1)) max = (1 << (8*size-1)) - 1 min = int(min) max = int(max) p = ffi.cast(c_decl, min) assert p != min # no __eq__(int) assert bool(p) is True assert int(p) == min p = ffi.cast(c_decl, max) assert int(p) == max p = ffi.cast(c_decl, long(max)) assert int(p) == max q = ffi.cast(c_decl, min - 1) assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max q = ffi.cast(c_decl, long(min - 1)) assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max assert q != p assert int(q) == int(p) assert hash(q) != hash(p) # unlikely c_decl_ptr = '%s *' % c_decl py.test.raises(OverflowError, ffi.new, c_decl_ptr, min - 1) py.test.raises(OverflowError, ffi.new, c_decl_ptr, max + 1) py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1)) py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(max + 1)) assert ffi.new(c_decl_ptr, min)[0] == min assert ffi.new(c_decl_ptr, max)[0] == max assert ffi.new(c_decl_ptr, long(min))[0] == min assert ffi.new(c_decl_ptr, long(max))[0] == max def test_new_unsupported_type(self): ffi = FFI(backend=self.Backend()) e = py.test.raises(TypeError, ffi.new, "int") assert str(e.value) == "expected a pointer or array ctype, got 'int'" def test_new_single_integer(self): ffi = FFI(backend=self.Backend()) p = ffi.new("int *") # similar to ffi.new("int[1]") assert p[0] == 0 p[0] = -123 assert p[0] == -123 p = ffi.new("int *", -42) assert p[0] == -42 assert repr(p) == "" % SIZE_OF_INT def test_new_array_no_arg(self): ffi = FFI(backend=self.Backend()) p = ffi.new("int[10]") # the object was zero-initialized: for i in range(10): assert p[i] == 0 def test_array_indexing(self): ffi = FFI(backend=self.Backend()) p = ffi.new("int[10]") p[0] = 42 p[9] = 43 assert p[0] == 42 assert p[9] == 43 py.test.raises(IndexError, "p[10]") py.test.raises(IndexError, "p[10] = 44") py.test.raises(IndexError, "p[-1]") py.test.raises(IndexError, "p[-1] = 44") def test_new_array_args(self): ffi = FFI(backend=self.Backend()) # this tries to be closer to C: where we say "int x[5] = {10, 20, ..}" # then here we must enclose the items in a list p = ffi.new("int[5]", [10, 20, 30, 40, 50]) assert p[0] == 10 assert p[1] == 20 assert p[2] == 30 assert p[3] == 40 assert p[4] == 50 p = ffi.new("int[4]", [25]) assert p[0] == 25 assert p[1] == 0 # follow C convention rather than LuaJIT's assert p[2] == 0 assert p[3] == 0 p = ffi.new("int[4]", [ffi.cast("int", -5)]) assert p[0] == -5 assert repr(p) == "" % (4*SIZE_OF_INT) def test_new_array_varsize(self): ffi = FFI(backend=self.Backend()) p = ffi.new("int[]", 10) # a single integer is the length assert p[9] == 0 py.test.raises(IndexError, "p[10]") # py.test.raises(TypeError, ffi.new, "int[]") # p = ffi.new("int[]", [-6, -7]) # a list is all the items, like C assert p[0] == -6 assert p[1] == -7 py.test.raises(IndexError, "p[2]") assert repr(p) == "" % (2*SIZE_OF_INT) # p = ffi.new("int[]", 0) py.test.raises(IndexError, "p[0]") py.test.raises(ValueError, ffi.new, "int[]", -1) assert repr(p) == "" def test_pointer_init(self): ffi = FFI(backend=self.Backend()) n = ffi.new("int *", 24) a = ffi.new("int *[10]", [ffi.NULL, ffi.NULL, n, n, ffi.NULL]) for i in range(10): if i not in (2, 3): assert a[i] == ffi.NULL assert a[2] == a[3] == n def test_cannot_cast(self): ffi = FFI(backend=self.Backend()) a = ffi.new("short int[10]") e = py.test.raises(TypeError, ffi.new, "long int **", a) msg = str(e.value) assert "'short[10]'" in msg and "'long *'" in msg def test_new_pointer_to_array(self): ffi = FFI(backend=self.Backend()) a = ffi.new("int[4]", [100, 102, 104, 106]) p = ffi.new("int **", a) assert p[0] == ffi.cast("int *", a) assert p[0][2] == 104 p = ffi.cast("int *", a) assert p[0] == 100 assert p[1] == 102 assert p[2] == 104 assert p[3] == 106 # keepalive: a def test_pointer_direct(self): ffi = FFI(backend=self.Backend()) p = ffi.cast("int*", 0) assert p is not None assert bool(p) is False assert p == ffi.cast("int*", 0) assert p != None assert repr(p) == "" a = ffi.new("int[]", [123, 456]) p = ffi.cast("int*", a) assert bool(p) is True assert p == ffi.cast("int*", a) assert p != ffi.cast("int*", 0) assert p[0] == 123 assert p[1] == 456 def test_repr(self): typerepr = self.TypeRepr ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { short a, b, c; };") p = ffi.cast("short unsigned int", 0) assert repr(p) == "" assert repr(ffi.typeof(p)) == typerepr % "unsigned short" p = ffi.cast("unsigned short int", 0) assert repr(p) == "" assert repr(ffi.typeof(p)) == typerepr % "unsigned short" p = ffi.cast("int*", 0) assert repr(p) == "" assert repr(ffi.typeof(p)) == typerepr % "int *" # p = ffi.new("int*") assert repr(p) == "" % SIZE_OF_INT assert repr(ffi.typeof(p)) == typerepr % "int *" p = ffi.new("int**") assert repr(p) == "" % SIZE_OF_PTR assert repr(ffi.typeof(p)) == typerepr % "int * *" p = ffi.new("int [2]") assert repr(p) == "" % (2*SIZE_OF_INT) assert repr(ffi.typeof(p)) == typerepr % "int[2]" p = ffi.new("int*[2][3]") assert repr(p) == "" % ( 6*SIZE_OF_PTR) assert repr(ffi.typeof(p)) == typerepr % "int *[2][3]" p = ffi.new("struct foo *") assert repr(p) == "" % ( 3*SIZE_OF_SHORT) assert repr(ffi.typeof(p)) == typerepr % "struct foo *" # q = ffi.cast("short", -123) assert repr(q) == "" assert repr(ffi.typeof(q)) == typerepr % "short" p = ffi.new("int*") q = ffi.cast("short*", p) assert repr(q).startswith(" 2: assert ffi.new("wchar_t*", u+'\U00012345')[0] == u+'\U00012345' else: py.test.raises(TypeError, ffi.new, "wchar_t*", u+'\U00012345') assert ffi.new("wchar_t*")[0] == u+'\x00' assert int(ffi.cast("wchar_t", 300)) == 300 assert bool(ffi.cast("wchar_t", 0)) py.test.raises(TypeError, ffi.new, "wchar_t*", 32) py.test.raises(TypeError, ffi.new, "wchar_t*", "foo") # p = ffi.new("wchar_t[]", [u+'a', u+'b', u+'\u1234']) assert len(p) == 3 assert p[0] == u+'a' assert p[1] == u+'b' and type(p[1]) is unicode assert p[2] == u+'\u1234' p[0] = u+'x' assert p[0] == u+'x' and type(p[0]) is unicode p[1] = u+'\u1357' assert p[1] == u+'\u1357' p = ffi.new("wchar_t[]", u+"abcd") assert len(p) == 5 assert p[4] == u+'\x00' p = ffi.new("wchar_t[]", u+"a\u1234b") assert len(p) == 4 assert p[1] == u+'\u1234' # p = ffi.new("wchar_t[]", u+'\U00023456') if SIZE_OF_WCHAR == 2: assert sys.maxunicode == 0xffff assert len(p) == 3 assert p[0] == u+'\ud84d' assert p[1] == u+'\udc56' assert p[2] == u+'\x00' else: assert len(p) == 2 assert p[0] == u+'\U00023456' assert p[1] == u+'\x00' # p = ffi.new("wchar_t[4]", u+"ab") assert len(p) == 4 assert [p[i] for i in range(4)] == [u+'a', u+'b', u+'\x00', u+'\x00'] p = ffi.new("wchar_t[2]", u+"ab") assert len(p) == 2 assert [p[i] for i in range(2)] == [u+'a', u+'b'] py.test.raises(IndexError, ffi.new, "wchar_t[2]", u+"abc") def test_none_as_null_doesnt_work(self): ffi = FFI(backend=self.Backend()) p = ffi.new("int*[1]") assert p[0] is not None assert p[0] != None assert p[0] == ffi.NULL assert repr(p[0]) == "" # n = ffi.new("int*", 99) p = ffi.new("int*[]", [n]) assert p[0][0] == 99 py.test.raises(TypeError, "p[0] = None") p[0] = ffi.NULL assert p[0] == ffi.NULL def test_float(self): ffi = FFI(backend=self.Backend()) p = ffi.new("float[]", [-2, -2.5]) assert p[0] == -2.0 assert p[1] == -2.5 p[1] += 17.75 assert p[1] == 15.25 # p = ffi.new("float*", 15.75) assert p[0] == 15.75 py.test.raises(TypeError, int, p) py.test.raises(TypeError, float, p) p[0] = 0.0 assert bool(p) is True # p = ffi.new("float*", 1.1) f = p[0] assert f != 1.1 # because of rounding effect assert abs(f - 1.1) < 1E-7 # INF = 1E200 * 1E200 assert 1E200 != INF p[0] = 1E200 assert p[0] == INF # infinite, not enough precision def test_struct_simple(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a; short b, c; };") s = ffi.new("struct foo*") assert s.a == s.b == s.c == 0 s.b = -23 assert s.b == -23 py.test.raises(OverflowError, "s.b = 32768") # s = ffi.new("struct foo*", [-2, -3]) assert s.a == -2 assert s.b == -3 assert s.c == 0 py.test.raises((AttributeError, TypeError), "del s.a") assert repr(s) == "" % ( SIZE_OF_INT + 2 * SIZE_OF_SHORT) # py.test.raises(ValueError, ffi.new, "struct foo*", [1, 2, 3, 4]) def test_constructor_struct_from_dict(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a; short b, c; };") s = ffi.new("struct foo*", {'b': 123, 'c': 456}) assert s.a == 0 assert s.b == 123 assert s.c == 456 py.test.raises(KeyError, ffi.new, "struct foo*", {'d': 456}) def test_struct_pointer(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a; short b, c; };") s = ffi.new("struct foo*") assert s[0].a == s[0].b == s[0].c == 0 s[0].b = -23 assert s[0].b == s.b == -23 py.test.raises(OverflowError, "s[0].b = -32769") py.test.raises(IndexError, "s[1]") def test_struct_opaque(self): ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.new, "struct baz*") p = ffi.new("struct baz **") # this works assert p[0] == ffi.NULL def test_pointer_to_struct(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a; short b, c; };") s = ffi.new("struct foo *") s.a = -42 assert s[0].a == -42 p = ffi.new("struct foo **", s) assert p[0].a == -42 assert p[0][0].a == -42 p[0].a = -43 assert s.a == -43 assert s[0].a == -43 p[0][0].a = -44 assert s.a == -44 assert s[0].a == -44 s.a = -45 assert p[0].a == -45 assert p[0][0].a == -45 s[0].a = -46 assert p[0].a == -46 assert p[0][0].a == -46 def test_constructor_struct_of_array(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a[2]; char b[3]; };") s = ffi.new("struct foo *", [[10, 11], [b'a', b'b', b'c']]) assert s.a[1] == 11 assert s.b[2] == b'c' s.b[1] = b'X' assert s.b[0] == b'a' assert s.b[1] == b'X' assert s.b[2] == b'c' def test_recursive_struct(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int value; struct foo *next; };") s = ffi.new("struct foo*") t = ffi.new("struct foo*") s.value = 123 s.next = t t.value = 456 assert s.value == 123 assert s.next.value == 456 def test_union_simple(self): ffi = FFI(backend=self.Backend()) ffi.cdef("union foo { int a; short b, c; };") u = ffi.new("union foo*") assert u.a == u.b == u.c == 0 u.b = -23 assert u.b == -23 assert u.a != 0 py.test.raises(OverflowError, "u.b = 32768") # u = ffi.new("union foo*", [-2]) assert u.a == -2 py.test.raises((AttributeError, TypeError), "del u.a") assert repr(u) == "" % SIZE_OF_INT def test_union_opaque(self): ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.new, "union baz *") u = ffi.new("union baz **") # this works assert u[0] == ffi.NULL def test_union_initializer(self): ffi = FFI(backend=self.Backend()) ffi.cdef("union foo { char a; int b; };") py.test.raises(TypeError, ffi.new, "union foo*", b'A') py.test.raises(TypeError, ffi.new, "union foo*", 5) py.test.raises(ValueError, ffi.new, "union foo*", [b'A', 5]) u = ffi.new("union foo*", [b'A']) assert u.a == b'A' py.test.raises(TypeError, ffi.new, "union foo*", [1005]) u = ffi.new("union foo*", {'b': 12345}) assert u.b == 12345 u = ffi.new("union foo*", []) assert u.a == b'\x00' assert u.b == 0 def test_sizeof_type(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" struct foo { int a; short b, c, d; }; union foo { int a; short b, c, d; }; """) for c_type, expected_size in [ ('char', 1), ('unsigned int', 4), ('char *', SIZE_OF_PTR), ('int[5]', 20), ('struct foo', 12), ('union foo', 4), ]: size = ffi.sizeof(c_type) assert size == expected_size, (size, expected_size, ctype) def test_sizeof_cdata(self): ffi = FFI(backend=self.Backend()) assert ffi.sizeof(ffi.new("short*")) == SIZE_OF_PTR assert ffi.sizeof(ffi.cast("short", 123)) == SIZE_OF_SHORT # a = ffi.new("int[]", [10, 11, 12, 13, 14]) assert len(a) == 5 assert ffi.sizeof(a) == 5 * SIZE_OF_INT def test_string_from_char_pointer(self): ffi = FFI(backend=self.Backend()) x = ffi.new("char*", b"x") assert str(x) == repr(x) assert ffi.string(x) == b"x" assert ffi.string(ffi.new("char*", b"\x00")) == b"" py.test.raises(TypeError, ffi.new, "char*", unicode("foo")) def test_unicode_from_wchar_pointer(self): ffi = FFI(backend=self.Backend()) self.check_wchar_t(ffi) x = ffi.new("wchar_t*", u+"x") assert unicode(x) == unicode(repr(x)) assert ffi.string(x) == u+"x" assert ffi.string(ffi.new("wchar_t*", u+"\x00")) == u+"" def test_string_from_char_array(self): ffi = FFI(backend=self.Backend()) p = ffi.new("char[]", b"hello.") p[5] = b'!' assert ffi.string(p) == b"hello!" p[6] = b'?' assert ffi.string(p) == b"hello!?" p[3] = b'\x00' assert ffi.string(p) == b"hel" assert ffi.string(p, 2) == b"he" py.test.raises(IndexError, "p[7] = b'X'") # a = ffi.new("char[]", b"hello\x00world") assert len(a) == 12 p = ffi.cast("char *", a) assert ffi.string(p) == b'hello' def test_string_from_wchar_array(self): ffi = FFI(backend=self.Backend()) self.check_wchar_t(ffi) assert ffi.string(ffi.cast("wchar_t", "x")) == u+"x" assert ffi.string(ffi.cast("wchar_t", u+"x")) == u+"x" x = ffi.cast("wchar_t", "x") assert str(x) == repr(x) assert ffi.string(x) == u+"x" # p = ffi.new("wchar_t[]", u+"hello.") p[5] = u+'!' assert ffi.string(p) == u+"hello!" p[6] = u+'\u04d2' assert ffi.string(p) == u+"hello!\u04d2" p[3] = u+'\x00' assert ffi.string(p) == u+"hel" assert ffi.string(p, 123) == u+"hel" py.test.raises(IndexError, "p[7] = u+'X'") # a = ffi.new("wchar_t[]", u+"hello\x00world") assert len(a) == 12 p = ffi.cast("wchar_t *", a) assert ffi.string(p) == u+'hello' assert ffi.string(p, 123) == u+'hello' assert ffi.string(p, 5) == u+'hello' assert ffi.string(p, 2) == u+'he' def test_fetch_const_char_p_field(self): # 'const' is ignored so far ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { const char *name; };") t = ffi.new("const char[]", b"testing") s = ffi.new("struct foo*", [t]) assert type(s.name) not in (bytes, str, unicode) assert ffi.string(s.name) == b"testing" py.test.raises(TypeError, "s.name = None") s.name = ffi.NULL assert s.name == ffi.NULL def test_fetch_const_wchar_p_field(self): # 'const' is ignored so far ffi = FFI(backend=self.Backend()) self.check_wchar_t(ffi) ffi.cdef("struct foo { const wchar_t *name; };") t = ffi.new("const wchar_t[]", u+"testing") s = ffi.new("struct foo*", [t]) assert type(s.name) not in (bytes, str, unicode) assert ffi.string(s.name) == u+"testing" s.name = ffi.NULL assert s.name == ffi.NULL def test_voidp(self): ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.new, "void*") p = ffi.new("void **") assert p[0] == ffi.NULL a = ffi.new("int[]", [10, 11, 12]) p = ffi.new("void **", a) vp = p[0] py.test.raises(TypeError, "vp[0]") py.test.raises(TypeError, ffi.new, "short **", a) # ffi.cdef("struct foo { void *p; int *q; short *r; };") s = ffi.new("struct foo *") s.p = a # works s.q = a # works py.test.raises(TypeError, "s.r = a") # fails b = ffi.cast("int *", a) s.p = b # works s.q = b # works py.test.raises(TypeError, "s.r = b") # fails def test_functionptr_simple(self): ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.callback, "int(*)(int)", 0) def cb(n): return n + 1 cb.__qualname__ = 'cb' p = ffi.callback("int(*)(int)", cb) res = p(41) # calling an 'int(*)(int)', i.e. a function pointer assert res == 42 and type(res) is int res = p(ffi.cast("int", -41)) assert res == -40 and type(res) is int assert repr(p).startswith( "" % ( SIZE_OF_PTR) py.test.raises(TypeError, "q(43)") res = q[0](43) assert res == 44 q = ffi.cast("int(*)(int)", p) assert repr(q).startswith("" assert repr(ffi.cast("enum foo", -1)) == ( # enums are unsigned, if "") # they contain no neg value ffi.cdef("enum baz { A=0x1000, B=0x2000 };") assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A" assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B" def test_enum_in_struct(self): ffi = FFI(backend=self.Backend()) ffi.cdef("enum foo { A, B, C, D }; struct bar { enum foo e; };") s = ffi.new("struct bar *") s.e = 0 assert s.e == 0 s.e = 3 assert s.e == 3 assert s[0].e == 3 s[0].e = 2 assert s.e == 2 assert s[0].e == 2 s.e = ffi.cast("enum foo", -1) assert s.e == 4294967295 assert s[0].e == 4294967295 s.e = s.e py.test.raises(TypeError, "s.e = 'B'") py.test.raises(TypeError, "s.e = '2'") py.test.raises(TypeError, "s.e = '#2'") py.test.raises(TypeError, "s.e = '#7'") def test_enum_non_contiguous(self): ffi = FFI(backend=self.Backend()) ffi.cdef("enum foo { A, B=42, C };") assert ffi.string(ffi.cast("enum foo", 0)) == "A" assert ffi.string(ffi.cast("enum foo", 42)) == "B" assert ffi.string(ffi.cast("enum foo", 43)) == "C" invalid_value = ffi.cast("enum foo", 2) assert int(invalid_value) == 2 assert ffi.string(invalid_value) == "2" def test_array_of_struct(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a, b; };") s = ffi.new("struct foo[1]") py.test.raises(AttributeError, 's.b') py.test.raises(AttributeError, 's.b = 412') s[0].b = 412 assert s[0].b == 412 py.test.raises(IndexError, 's[1]') def test_pointer_to_array(self): ffi = FFI(backend=self.Backend()) p = ffi.new("int(**)[5]") assert repr(p) == "" % SIZE_OF_PTR def test_iterate_array(self): ffi = FFI(backend=self.Backend()) a = ffi.new("char[]", b"hello") assert list(a) == [b"h", b"e", b"l", b"l", b"o", b"\0"] assert list(iter(a)) == [b"h", b"e", b"l", b"l", b"o", b"\0"] # py.test.raises(TypeError, iter, ffi.cast("char *", a)) py.test.raises(TypeError, list, ffi.cast("char *", a)) py.test.raises(TypeError, iter, ffi.new("int *")) py.test.raises(TypeError, list, ffi.new("int *")) def test_offsetof(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a, b, c; };") assert ffi.offsetof("struct foo", "a") == 0 assert ffi.offsetof("struct foo", "b") == 4 assert ffi.offsetof("struct foo", "c") == 8 def test_alignof(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { char a; short b; char c; };") assert ffi.alignof("int") == 4 assert ffi.alignof("double") in (4, 8) assert ffi.alignof("struct foo") == 2 def test_bitfield(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a:10, b:20, c:3; };") assert ffi.sizeof("struct foo") == 8 s = ffi.new("struct foo *") s.a = 511 py.test.raises(OverflowError, "s.a = 512") py.test.raises(OverflowError, "s[0].a = 512") assert s.a == 511 s.a = -512 py.test.raises(OverflowError, "s.a = -513") py.test.raises(OverflowError, "s[0].a = -513") assert s.a == -512 s.c = 3 assert s.c == 3 py.test.raises(OverflowError, "s.c = 4") py.test.raises(OverflowError, "s[0].c = 4") s.c = -4 assert s.c == -4 def test_bitfield_enum(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef enum { AA, BB, CC } foo_e; typedef struct { foo_e f:2; } foo_s; """) s = ffi.new("foo_s *") s.f = 2 assert s.f == 2 def test_anonymous_struct(self): ffi = FFI(backend=self.Backend()) ffi.cdef("typedef struct { int a; } foo_t;") ffi.cdef("typedef struct { char b, c; } bar_t;") f = ffi.new("foo_t *", [12345]) b = ffi.new("bar_t *", [b"B", b"C"]) assert f.a == 12345 assert b.b == b"B" assert b.c == b"C" assert repr(b).startswith(" s) is False assert (p >= s) is True assert (s < p) is False assert (s <= p) is True assert (s == p) is True assert (s != p) is False assert (s > p) is False assert (s >= p) is True q = p + 1 assert (q < s) is False assert (q <= s) is False assert (q == s) is False assert (q != s) is True assert (q > s) is True assert (q >= s) is True assert (s < q) is True assert (s <= q) is True assert (s == q) is False assert (s != q) is True assert (s > q) is False assert (s >= q) is False assert (q < p) is False assert (q <= p) is False assert (q == p) is False assert (q != p) is True assert (q > p) is True assert (q >= p) is True assert (p < q) is True assert (p <= q) is True assert (p == q) is False assert (p != q) is True assert (p > q) is False assert (p >= q) is False # assert (None == s) is False assert (None != s) is True assert (s == None) is False assert (s != None) is True assert (None == q) is False assert (None != q) is True assert (q == None) is False assert (q != None) is True def test_no_integer_comparison(self): ffi = FFI(backend=self.Backend()) x = ffi.cast("int", 123) y = ffi.cast("int", 456) py.test.raises(TypeError, "x < y") # z = ffi.cast("double", 78.9) py.test.raises(TypeError, "x < z") py.test.raises(TypeError, "z < y") def test_ffi_buffer_ptr(self): ffi = FFI(backend=self.Backend()) a = ffi.new("short *", 100) try: b = ffi.buffer(a) except NotImplementedError as e: py.test.skip(str(e)) content = b[:] assert len(content) == len(b) == 2 if sys.byteorder == 'little': assert content == b'\x64\x00' assert b[0] == b'\x64' b[0] = b'\x65' else: assert content == b'\x00\x64' assert b[1] == b'\x64' b[1] = b'\x65' assert a[0] == 101 def test_ffi_buffer_array(self): ffi = FFI(backend=self.Backend()) a = ffi.new("int[]", list(range(100, 110))) try: b = ffi.buffer(a) except NotImplementedError as e: py.test.skip(str(e)) content = b[:] if sys.byteorder == 'little': assert content.startswith(b'\x64\x00\x00\x00\x65\x00\x00\x00') b[4] = b'\x45' else: assert content.startswith(b'\x00\x00\x00\x64\x00\x00\x00\x65') b[7] = b'\x45' assert len(content) == 4 * 10 assert a[1] == 0x45 def test_ffi_buffer_ptr_size(self): ffi = FFI(backend=self.Backend()) a = ffi.new("short *", 0x4243) try: b = ffi.buffer(a, 1) except NotImplementedError as e: py.test.skip(str(e)) content = b[:] assert len(content) == 1 if sys.byteorder == 'little': assert content == b'\x43' b[0] = b'\x62' assert a[0] == 0x4262 else: assert content == b'\x42' b[0] = b'\x63' assert a[0] == 0x6343 def test_ffi_buffer_array_size(self): ffi = FFI(backend=self.Backend()) a1 = ffi.new("int[]", list(range(100, 110))) a2 = ffi.new("int[]", list(range(100, 115))) try: ffi.buffer(a1) except NotImplementedError as e: py.test.skip(str(e)) assert ffi.buffer(a1)[:] == ffi.buffer(a2, 4*10)[:] def test_ffi_buffer_with_file(self): ffi = FFI(backend=self.Backend()) import tempfile, os, array fd, filename = tempfile.mkstemp() f = os.fdopen(fd, 'r+b') a = ffi.new("int[]", list(range(1005))) try: ffi.buffer(a, 512) except NotImplementedError as e: py.test.skip(str(e)) f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) f.seek(0) assert f.read() == array.array('i', range(1000)).tostring() f.seek(0) b = ffi.new("int[]", 1005) f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) assert list(a)[:1000] + [0] * (len(a)-1000) == list(b) f.close() os.unlink(filename) def test_ffi_buffer_with_io(self): ffi = FFI(backend=self.Backend()) import io, array f = io.BytesIO() a = ffi.new("int[]", list(range(1005))) try: ffi.buffer(a, 512) except NotImplementedError as e: py.test.skip(str(e)) f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) f.seek(0) assert f.read() == array.array('i', range(1000)).tostring() f.seek(0) b = ffi.new("int[]", 1005) f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) assert list(a)[:1000] + [0] * (len(a)-1000) == list(b) f.close() def test_array_in_struct(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int len; short data[5]; };") p = ffi.new("struct foo_s *") p.data[3] = 5 assert p.data[3] == 5 assert repr(p.data).startswith(" typedef int (*mycallback_func_t)(int, int); void *my_wait_function(void *ptr) { mycallback_func_t cbfunc = (mycallback_func_t)ptr; cbfunc(10, 10); cbfunc(12, 15); return NULL; } int threaded_ballback_test(mycallback_func_t mycb) { pthread_t thread; pthread_create(&thread, NULL, my_wait_function, (void*)mycb); return 0; } """, extra_compile_args=['-pthread']) seen = [] @ffi.callback('int(*)(int,int)') def mycallback(x, y): time.sleep(0.022) seen.append((x, y)) return 0 lib.threaded_ballback_test(mycallback) count = 300 while len(seen) != 2: time.sleep(0.01) count -= 1 assert count > 0, "timeout" assert seen == [(10, 10), (12, 15)] print('STARTING') _run_callback_in_thread() print('DONE') cffi-0.8.2/testing/test_ffi_backend.py0000644000076400001440000002227612240417427020231 0ustar arigousers00000000000000import py, sys, platform import pytest from testing import backend_tests, test_function, test_ownlib from cffi import FFI import _cffi_backend class TestFFI(backend_tests.BackendTests, test_function.TestFunction, test_ownlib.TestOwnLib): TypeRepr = "" @staticmethod def Backend(): return _cffi_backend def test_not_supported_bitfield_in_result(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo_s { int a,b,c,d,e; int x:1; };") e = py.test.raises(NotImplementedError, ffi.callback, "struct foo_s foo(void)", lambda: 42) assert str(e.value) == (": " "cannot pass as argument or return value a struct with bit fields") def test_inspecttype(self): ffi = FFI(backend=self.Backend()) assert ffi.typeof("long").kind == "primitive" assert ffi.typeof("long(*)(long, long**, ...)").cname == ( "long(*)(long, long * *, ...)") assert ffi.typeof("long(*)(long, long**, ...)").ellipsis is True def test_new_handle(self): ffi = FFI(backend=self.Backend()) o = [2, 3, 4] p = ffi.new_handle(o) assert ffi.typeof(p) == ffi.typeof("void *") assert ffi.from_handle(p) is o assert ffi.from_handle(ffi.cast("char *", p)) is o py.test.raises(RuntimeError, ffi.from_handle, ffi.NULL) class TestBitfield: def check(self, source, expected_ofs_y, expected_align, expected_size): # NOTE: 'expected_*' is the numbers expected from GCC. # The numbers expected from MSVC are not explicitly written # in this file, and will just be taken from the compiler. ffi = FFI() ffi.cdef("struct s1 { %s };" % source) ctype = ffi.typeof("struct s1") # verify the information with gcc ffi1 = FFI() ffi1.cdef(""" static const int Gofs_y, Galign, Gsize; struct s1 *try_with_value(int fieldnum, long long value); """) fnames = [name for name, cfield in ctype.fields if name and cfield.bitsize > 0] setters = ['case %d: s.%s = value; break;' % iname for iname in enumerate(fnames)] lib = ffi1.verify(""" struct s1 { %s }; struct sa { char a; struct s1 b; }; #define Gofs_y offsetof(struct s1, y) #define Galign offsetof(struct sa, b) #define Gsize sizeof(struct s1) struct s1 *try_with_value(int fieldnum, long long value) { static struct s1 s; memset(&s, 0, sizeof(s)); switch (fieldnum) { %s } return &s; } """ % (source, ' '.join(setters))) if sys.platform == 'win32': expected_ofs_y = lib.Gofs_y expected_align = lib.Galign expected_size = lib.Gsize else: assert (lib.Gofs_y, lib.Galign, lib.Gsize) == ( expected_ofs_y, expected_align, expected_size) # the real test follows assert ffi.offsetof("struct s1", "y") == expected_ofs_y assert ffi.alignof("struct s1") == expected_align assert ffi.sizeof("struct s1") == expected_size # compare the actual storage of the two for name, cfield in ctype.fields: if cfield.bitsize < 0 or not name: continue if int(ffi.cast(cfield.type, -1)) == -1: # signed min_value = -(1 << (cfield.bitsize-1)) max_value = (1 << (cfield.bitsize-1)) - 1 else: min_value = 0 max_value = (1 << cfield.bitsize) - 1 for t in [1, 2, 4, 8, 16, 128, 2813, 89728, 981729, -1,-2,-4,-8,-16,-128,-2813,-89728,-981729]: if min_value <= t <= max_value: self._fieldcheck(ffi, lib, fnames, name, t) def _fieldcheck(self, ffi, lib, fnames, name, value): s = ffi.new("struct s1 *") setattr(s, name, value) assert getattr(s, name) == value raw1 = ffi.buffer(s)[:] t = lib.try_with_value(fnames.index(name), value) raw2 = ffi.buffer(t, len(raw1))[:] assert raw1 == raw2 def test_bitfield_basic(self): self.check("int a; int b:9; int c:20; int y;", 8, 4, 12) self.check("int a; short b:9; short c:7; int y;", 8, 4, 12) self.check("int a; short b:9; short c:9; int y;", 8, 4, 12) def test_bitfield_reuse_if_enough_space(self): self.check("int a:2; char y;", 1, 4, 4) self.check("int a:1; char b ; int c:1; char y;", 3, 4, 4) self.check("int a:1; char b:8; int c:1; char y;", 3, 4, 4) self.check("char a; int b:9; char y;", 3, 4, 4) self.check("char a; short b:9; char y;", 4, 2, 6) self.check("int a:2; char b:6; char y;", 1, 4, 4) self.check("int a:2; char b:7; char y;", 2, 4, 4) self.check("int a:2; short b:15; char c:2; char y;", 5, 4, 8) self.check("int a:2; char b:1; char c:1; char y;", 1, 4, 4) @pytest.mark.skipif("platform.machine().startswith('arm')") def test_bitfield_anonymous_no_align(self): L = FFI().alignof("long long") self.check("char y; int :1;", 0, 1, 2) self.check("char x; int z:1; char y;", 2, 4, 4) self.check("char x; int :1; char y;", 2, 1, 3) self.check("char x; long long z:48; char y;", 7, L, 8) self.check("char x; long long :48; char y;", 7, 1, 8) self.check("char x; long long z:56; char y;", 8, L, 8 + L) self.check("char x; long long :56; char y;", 8, 1, 9) self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) self.check("char x; long long :57; char y;", L + 8, 1, L + 9) @pytest.mark.skipif("not platform.machine().startswith('arm')") def test_bitfield_anonymous_align_arm(self): L = FFI().alignof("long long") self.check("char y; int :1;", 0, 4, 4) self.check("char x; int z:1; char y;", 2, 4, 4) self.check("char x; int :1; char y;", 2, 4, 4) self.check("char x; long long z:48; char y;", 7, L, 8) self.check("char x; long long :48; char y;", 7, 8, 8) self.check("char x; long long z:56; char y;", 8, L, 8 + L) self.check("char x; long long :56; char y;", 8, L, 8 + L) self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) self.check("char x; long long :57; char y;", L + 8, L, L + 8 + L) @pytest.mark.skipif("platform.machine().startswith('arm')") def test_bitfield_zero(self): L = FFI().alignof("long long") self.check("char y; int :0;", 0, 1, 4) self.check("char x; int :0; char y;", 4, 1, 5) self.check("char x; int :0; int :0; char y;", 4, 1, 5) self.check("char x; long long :0; char y;", L, 1, L + 1) self.check("short x, y; int :0; int :0;", 2, 2, 4) self.check("char x; int :0; short b:1; char y;", 5, 2, 6) self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8) @pytest.mark.skipif("not platform.machine().startswith('arm')") def test_bitfield_zero_arm(self): L = FFI().alignof("long long") self.check("char y; int :0;", 0, 4, 4) self.check("char x; int :0; char y;", 4, 4, 8) self.check("char x; int :0; int :0; char y;", 4, 4, 8) self.check("char x; long long :0; char y;", L, 8, L + 8) self.check("short x, y; int :0; int :0;", 2, 4, 4) self.check("char x; int :0; short b:1; char y;", 5, 4, 8) self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8) def test_error_cases(self): ffi = FFI() py.test.raises(TypeError, 'ffi.cdef("struct s1 { float x:1; };"); ffi.new("struct s1 *")') py.test.raises(TypeError, 'ffi.cdef("struct s2 { char x:0; };"); ffi.new("struct s2 *")') py.test.raises(TypeError, 'ffi.cdef("struct s3 { char x:9; };"); ffi.new("struct s3 *")') def test_struct_with_typedef(self): ffi = FFI() ffi.cdef("typedef struct { float x; } foo_t;") p = ffi.new("foo_t *", [5.2]) assert repr(p).startswith("= [11, 0, 0]): # special things for clang extra_compile_args = [ '-Werror', '-Qunused-arguments', '-Wno-error=shorten-64-to-32'] else: # assume a standard gcc extra_compile_args = ['-Werror'] class FFI(FFI): def verify(self, *args, **kwds): return super(FFI, self).verify( *args, extra_compile_args=extra_compile_args, **kwds) def setup_module(): import cffi.verifier cffi.verifier.cleanup_tmpdir() def test_module_type(): import cffi.verifier ffi = FFI() lib = ffi.verify() if hasattr(lib, '_cffi_python_module'): print('verify got a PYTHON module') if hasattr(lib, '_cffi_generic_module'): print('verify got a GENERIC module') expected_generic = (cffi.verifier._FORCE_GENERIC_ENGINE or '__pypy__' in sys.builtin_module_names) assert hasattr(lib, '_cffi_python_module') == (not expected_generic) assert hasattr(lib, '_cffi_generic_module') == expected_generic def test_missing_function(ffi=None): # uses the FFI hacked above with '-Werror' if ffi is None: ffi = FFI() ffi.cdef("void some_completely_unknown_function();") try: lib = ffi.verify() except (VerificationError, OSError): pass # expected case: we get a VerificationError else: # but depending on compiler and loader details, maybe # 'lib' could actually be imported but will fail if we # actually try to call the unknown function... Hard # to test anything more. pass def test_missing_function_import_error(): # uses the original FFI that just gives a warning during compilation import cffi test_missing_function(ffi=cffi.FFI()) def test_simple_case(): ffi = FFI() ffi.cdef("double sin(double x);") lib = ffi.verify('#include ', libraries=lib_m) assert lib.sin(1.23) == math.sin(1.23) def test_rounding_1(): ffi = FFI() ffi.cdef("float sin(double x);") lib = ffi.verify('#include ', libraries=lib_m) res = lib.sin(1.23) assert res != math.sin(1.23) # not exact, because of double->float assert abs(res - math.sin(1.23)) < 1E-5 def test_rounding_2(): ffi = FFI() ffi.cdef("double sin(float x);") lib = ffi.verify('#include ', libraries=lib_m) res = lib.sin(1.23) assert res != math.sin(1.23) # not exact, because of double->float assert abs(res - math.sin(1.23)) < 1E-5 def test_strlen_exact(): ffi = FFI() ffi.cdef("size_t strlen(const char *s);") lib = ffi.verify("#include ") assert lib.strlen(b"hi there!") == 9 def test_strlen_approximate(): ffi = FFI() ffi.cdef("int strlen(char *s);") lib = ffi.verify("#include ") assert lib.strlen(b"hi there!") == 9 def test_strlen_array_of_char(): ffi = FFI() ffi.cdef("int strlen(char[]);") lib = ffi.verify("#include ") assert lib.strlen(b"hello") == 5 def test_longdouble(): ffi = FFI() ffi.cdef("long double sinl(long double x);") lib = ffi.verify('#include ', libraries=lib_m) for input in [1.23, ffi.cast("double", 1.23), ffi.cast("long double", 1.23)]: x = lib.sinl(input) assert repr(x).startswith(" 0.1 # Check the particular results on Intel import platform if (platform.machine().startswith('i386') or platform.machine().startswith('x86')): assert abs(more_precise - 0.656769) < 0.001 assert abs(less_precise - 3.99091) < 0.001 else: py.test.skip("don't know the very exact precision of 'long double'") all_primitive_types = model.PrimitiveType.ALL_PRIMITIVE_TYPES all_integer_types = sorted(tp for tp in all_primitive_types if all_primitive_types[tp] == 'i') all_float_types = sorted(tp for tp in all_primitive_types if all_primitive_types[tp] == 'f') def all_signed_integer_types(ffi): return [x for x in all_integer_types if int(ffi.cast(x, -1)) < 0] def all_unsigned_integer_types(ffi): return [x for x in all_integer_types if int(ffi.cast(x, -1)) > 0] def test_primitive_category(): for typename in all_primitive_types: tp = model.PrimitiveType(typename) C = tp.is_char_type() F = tp.is_float_type() I = tp.is_integer_type() assert C == (typename in ('char', 'wchar_t')) assert F == (typename in ('float', 'double', 'long double')) assert I + F + C == 1 # one and only one of them is true def test_all_integer_and_float_types(): typenames = [] for typename in all_primitive_types: if (all_primitive_types[typename] == 'c' or typename == '_Bool' or typename == 'long double'): pass else: typenames.append(typename) # ffi = FFI() ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp) for tp in typenames])) lib = ffi.verify('\n'.join(["%s foo_%s(%s x) { return x+1; }" % (tp, tp.replace(' ', '_'), tp) for tp in typenames])) for typename in typenames: foo = getattr(lib, 'foo_%s' % typename.replace(' ', '_')) assert foo(42) == 43 if sys.version < '3': assert foo(long(44)) == 45 assert foo(ffi.cast(typename, 46)) == 47 py.test.raises(TypeError, foo, ffi.NULL) # # check for overflow cases if all_primitive_types[typename] == 'f': continue for value in [-2**80, -2**40, -2**20, -2**10, -2**5, -1, 2**5, 2**10, 2**20, 2**40, 2**80]: overflows = int(ffi.cast(typename, value)) != value if overflows: py.test.raises(OverflowError, foo, value) else: assert foo(value) == value + 1 def test_var_signed_integer_types(): ffi = FFI() lst = all_signed_integer_types(ffi) csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) lib = ffi.verify(csource) for tp in lst: varname = 'somevar_%s' % tp.replace(' ', '_') sz = ffi.sizeof(tp) max = (1 << (8*sz-1)) - 1 min = -(1 << (8*sz-1)) setattr(lib, varname, max) assert getattr(lib, varname) == max setattr(lib, varname, min) assert getattr(lib, varname) == min py.test.raises(OverflowError, setattr, lib, varname, max+1) py.test.raises(OverflowError, setattr, lib, varname, min-1) def test_var_unsigned_integer_types(): ffi = FFI() lst = all_unsigned_integer_types(ffi) csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) lib = ffi.verify(csource) for tp in lst: varname = 'somevar_%s' % tp.replace(' ', '_') sz = ffi.sizeof(tp) if tp != '_Bool': max = (1 << (8*sz)) - 1 else: max = 1 setattr(lib, varname, max) assert getattr(lib, varname) == max setattr(lib, varname, 0) assert getattr(lib, varname) == 0 py.test.raises(OverflowError, setattr, lib, varname, max+1) py.test.raises(OverflowError, setattr, lib, varname, -1) def test_fn_signed_integer_types(): ffi = FFI() lst = all_signed_integer_types(ffi) cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) for tp in lst]) ffi.cdef(cdefsrc) verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % (tp, tp.replace(' ', '_'), tp) for tp in lst]) lib = ffi.verify(verifysrc) for tp in lst: fnname = 'somefn_%s' % tp.replace(' ', '_') sz = ffi.sizeof(tp) max = (1 << (8*sz-1)) - 1 min = -(1 << (8*sz-1)) fn = getattr(lib, fnname) assert fn(max) == max assert fn(min) == min py.test.raises(OverflowError, fn, max + 1) py.test.raises(OverflowError, fn, min - 1) def test_fn_unsigned_integer_types(): ffi = FFI() lst = all_unsigned_integer_types(ffi) cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) for tp in lst]) ffi.cdef(cdefsrc) verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % (tp, tp.replace(' ', '_'), tp) for tp in lst]) lib = ffi.verify(verifysrc) for tp in lst: fnname = 'somefn_%s' % tp.replace(' ', '_') sz = ffi.sizeof(tp) if tp != '_Bool': max = (1 << (8*sz)) - 1 else: max = 1 fn = getattr(lib, fnname) assert fn(max) == max assert fn(0) == 0 py.test.raises(OverflowError, fn, max + 1) py.test.raises(OverflowError, fn, -1) def test_char_type(): ffi = FFI() ffi.cdef("char foo(char);") lib = ffi.verify("char foo(char x) { return x+1; }") assert lib.foo(b"A") == b"B" py.test.raises(TypeError, lib.foo, b"bar") py.test.raises(TypeError, lib.foo, "bar") def test_wchar_type(): ffi = FFI() if ffi.sizeof('wchar_t') == 2: uniexample1 = u+'\u1234' uniexample2 = u+'\u1235' else: uniexample1 = u+'\U00012345' uniexample2 = u+'\U00012346' # ffi.cdef("wchar_t foo(wchar_t);") lib = ffi.verify("wchar_t foo(wchar_t x) { return x+1; }") assert lib.foo(uniexample1) == uniexample2 def test_no_argument(): ffi = FFI() ffi.cdef("int foo(void);") lib = ffi.verify("int foo(void) { return 42; }") assert lib.foo() == 42 def test_two_arguments(): ffi = FFI() ffi.cdef("int foo(int, int);") lib = ffi.verify("int foo(int a, int b) { return a - b; }") assert lib.foo(40, -2) == 42 def test_macro(): ffi = FFI() ffi.cdef("int foo(int, int);") lib = ffi.verify("#define foo(a, b) ((a) * (b))") assert lib.foo(-6, -7) == 42 def test_ptr(): ffi = FFI() ffi.cdef("int *foo(int *);") lib = ffi.verify("int *foo(int *a) { return a; }") assert lib.foo(ffi.NULL) == ffi.NULL p = ffi.new("int *", 42) q = ffi.new("int *", 42) assert lib.foo(p) == p assert lib.foo(q) != p def test_bogus_ptr(): ffi = FFI() ffi.cdef("int *foo(int *);") lib = ffi.verify("int *foo(int *a) { return a; }") py.test.raises(TypeError, lib.foo, ffi.new("short *", 42)) def test_verify_typedefs(): py.test.skip("ignored so far") types = ['signed char', 'unsigned char', 'int', 'long'] for cdefed in types: for real in types: ffi = FFI() ffi.cdef("typedef %s foo_t;" % cdefed) if cdefed == real: ffi.verify("typedef %s foo_t;" % real) else: py.test.raises(VerificationError, ffi.verify, "typedef %s foo_t;" % real) def test_nondecl_struct(): ffi = FFI() ffi.cdef("typedef struct foo_s foo_t; int bar(foo_t *);") lib = ffi.verify("typedef struct foo_s foo_t;\n" "int bar(foo_t *f) { return 42; }\n") assert lib.bar(ffi.NULL) == 42 def test_ffi_full_struct(): ffi = FFI() ffi.cdef("struct foo_s { char x; int y; long *z; };") ffi.verify("struct foo_s { char x; int y; long *z; };") # if sys.platform != 'win32': # XXX fixme: only gives warnings py.test.raises(VerificationError, ffi.verify, "struct foo_s { char x; int y; int *z; };") # py.test.raises(VerificationError, ffi.verify, "struct foo_s { int y; long *z; };") # e = py.test.raises(VerificationError, ffi.verify, "struct foo_s { int y; char x; long *z; };") assert str(e.value) == ( "struct foo_s: wrong offset for field 'x'" " (we have 0, but C compiler says 4)") # e = py.test.raises(VerificationError, ffi.verify, "struct foo_s { char x; int y; long *z; char extra; };") assert str(e.value) == ( "struct foo_s: wrong total size" " (we have %d, but C compiler says %d)" % ( ffi.sizeof("struct foo_s"), ffi.sizeof("struct foo_s") + ffi.sizeof("long*"))) # # a corner case that we cannot really detect, but where it has no # bad consequences: the size is the same, but there is an extra field # that replaces what is just padding in our declaration above ffi.verify("struct foo_s { char x, extra; int y; long *z; };") # e = py.test.raises(VerificationError, ffi.verify, "struct foo_s { char x; short pad; short y; long *z; };") assert str(e.value) == ( "struct foo_s: wrong size for field 'y'" " (we have 4, but C compiler says 2)") def test_ffi_nonfull_struct(): ffi = FFI() ffi.cdef(""" struct foo_s { int x; ...; }; """) py.test.raises(VerificationMissing, ffi.sizeof, 'struct foo_s') py.test.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x') py.test.raises(VerificationMissing, ffi.new, 'struct foo_s *') ffi.verify(""" struct foo_s { int a, b, x, c, d, e; }; """) assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int') assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int') def test_ffi_nonfull_alignment(): ffi = FFI() ffi.cdef("struct foo_s { char x; ...; };") ffi.verify("struct foo_s { int a, b; char x; };") assert ffi.sizeof('struct foo_s') == 3 * ffi.sizeof('int') assert ffi.alignof('struct foo_s') == ffi.sizeof('int') def _check_field_match(typename, real, expect_mismatch): ffi = FFI() testing_by_size = (expect_mismatch == 'by_size') if testing_by_size: expect_mismatch = ffi.sizeof(typename) != ffi.sizeof(real) ffi.cdef("struct foo_s { %s x; ...; };" % typename) try: ffi.verify("struct foo_s { %s x; };" % real) except VerificationError: if not expect_mismatch: if testing_by_size and typename != real: print("ignoring mismatch between %s* and %s* even though " "they have the same size" % (typename, real)) return raise AssertionError("unexpected mismatch: %s should be accepted " "as equal to %s" % (typename, real)) else: if expect_mismatch: raise AssertionError("mismatch not detected: " "%s != %s" % (typename, real)) def test_struct_bad_sized_integer(): for typename in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: for real in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: _check_field_match(typename, real, "by_size") def test_struct_bad_sized_float(): for typename in all_float_types: for real in all_float_types: _check_field_match(typename, real, "by_size") def test_struct_signedness_ignored(): _check_field_match("int", "unsigned int", expect_mismatch=False) _check_field_match("unsigned short", "signed short", expect_mismatch=False) def test_struct_float_vs_int(): if sys.platform == 'win32': py.test.skip("XXX fixme: only gives warnings") ffi = FFI() for typename in all_signed_integer_types(ffi): for real in all_float_types: _check_field_match(typename, real, expect_mismatch=True) for typename in all_float_types: for real in all_signed_integer_types(ffi): _check_field_match(typename, real, expect_mismatch=True) def test_struct_array_field(): ffi = FFI() ffi.cdef("struct foo_s { int a[17]; ...; };") ffi.verify("struct foo_s { int x; int a[17]; int y; };") assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') s = ffi.new("struct foo_s *") assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') def test_struct_array_no_length(): ffi = FFI() ffi.cdef("struct foo_s { int a[]; int y; ...; };\n" "int bar(struct foo_s *);\n") lib = ffi.verify("struct foo_s { int x; int a[17]; int y; };\n" "int bar(struct foo_s *f) { return f->a[14]; }\n") assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') s = ffi.new("struct foo_s *") assert ffi.typeof(s.a) is ffi.typeof('int *') # because no length s.a[14] = 4242 assert lib.bar(s) == 4242 # with no declared length, out-of-bound accesses are not detected s.a[17] = -521 assert s.y == s.a[17] == -521 # s = ffi.new("struct foo_s *", {'a': list(range(17))}) assert s.a[16] == 16 # overflows at construction time not detected either s = ffi.new("struct foo_s *", {'a': list(range(18))}) assert s.y == s.a[17] == 17 def test_struct_array_guess_length(): ffi = FFI() ffi.cdef("struct foo_s { int a[...]; };") ffi.verify("struct foo_s { int x; int a[17]; int y; };") assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') s = ffi.new("struct foo_s *") assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') py.test.raises(IndexError, 's.a[17]') def test_struct_array_c99_1(): if sys.platform == 'win32': py.test.skip("requires C99") ffi = FFI() ffi.cdef("struct foo_s { int x; int a[]; };") ffi.verify("struct foo_s { int x; int a[]; };") assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int') s = ffi.new("struct foo_s *", [424242, 4]) assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') # the same in C assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') assert s.a[3] == -10 s = ffi.new("struct foo_s *") assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') s = ffi.new("struct foo_s *", [424242]) assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') def test_struct_array_c99_2(): if sys.platform == 'win32': py.test.skip("requires C99") ffi = FFI() ffi.cdef("struct foo_s { int x; int a[]; ...; };") ffi.verify("struct foo_s { int x, y; int a[]; };") assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int') s = ffi.new("struct foo_s *", [424242, 4]) assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') assert s.a[3] == -10 s = ffi.new("struct foo_s *") assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') s = ffi.new("struct foo_s *", [424242]) assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') def test_struct_ptr_to_array_field(): ffi = FFI() ffi.cdef("struct foo_s { int (*a)[17]; ...; }; struct bar_s { ...; };") ffi.verify("struct foo_s { int x; int (*a)[17]; int y; };\n" "struct bar_s { int x; int *a; int y; };") assert ffi.sizeof('struct foo_s') == ffi.sizeof("struct bar_s") s = ffi.new("struct foo_s *") assert ffi.sizeof(s.a) == ffi.sizeof('int(*)[17]') == ffi.sizeof("int *") def test_struct_with_bitfield_exact(): ffi = FFI() ffi.cdef("struct foo_s { int a:2, b:3; };") ffi.verify("struct foo_s { int a:2, b:3; };") s = ffi.new("struct foo_s *") s.b = 3 py.test.raises(OverflowError, "s.b = 4") assert s.b == 3 def test_struct_with_bitfield_enum(): ffi = FFI() code = """ typedef enum { AA, BB, CC } foo_e; typedef struct { foo_e f:2; } foo_s; """ ffi.cdef(code) ffi.verify(code) s = ffi.new("foo_s *") s.f = 2 assert s.f == 2 def test_unsupported_struct_with_bitfield_ellipsis(): ffi = FFI() py.test.raises(NotImplementedError, ffi.cdef, "struct foo_s { int a:2, b:3; ...; };") def test_global_constants(): ffi = FFI() # use 'static const int', as generally documented, although in this # case the 'static' is completely ignored. ffi.cdef("static const int AA, BB, CC, DD;") lib = ffi.verify("#define AA 42\n" "#define BB (-43)\n" "#define CC (22*2)\n" "#define DD ((unsigned int)142)\n") assert lib.AA == 42 assert lib.BB == -43 assert lib.CC == 44 assert lib.DD == 142 def test_global_const_int_size(): # integer constants: ignore the declared type, always just use the value for value in [-2**63, -2**31, -2**15, 2**15-1, 2**15, 2**31-1, 2**31, 2**32-1, 2**32, 2**63-1, 2**63, 2**64-1]: ffi = FFI() if value == int(ffi.cast("long long", value)): if value < 0: vstr = '(-%dLL-1)' % (~value,) else: vstr = '%dLL' % value elif value == int(ffi.cast("unsigned long long", value)): vstr = '%dULL' % value else: raise AssertionError(value) ffi.cdef("static const unsigned short AA;") lib = ffi.verify("#define AA %s\n" % vstr) assert lib.AA == value assert type(lib.AA) is type(int(lib.AA)) def test_global_constants_non_int(): ffi = FFI() ffi.cdef("static char *const PP;") lib = ffi.verify('static char *const PP = "testing!";\n') assert ffi.typeof(lib.PP) == ffi.typeof("char *") assert ffi.string(lib.PP) == b"testing!" def test_nonfull_enum(): ffi = FFI() ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };") py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2') ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") assert ffi.string(ffi.cast('enum ee', 11)) == "EE2" assert ffi.string(ffi.cast('enum ee', -10)) == "EE3" # # try again ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") assert ffi.string(ffi.cast('enum ee', 11)) == "EE2" def test_full_enum(): ffi = FFI() ffi.cdef("enum ee { EE1, EE2, EE3 };") ffi.verify("enum ee { EE1, EE2, EE3 };") py.test.raises(VerificationError, ffi.verify, "enum ee { EE1, EE2 };") e = py.test.raises(VerificationError, ffi.verify, "enum ee { EE1, EE3, EE2 };") assert str(e.value) == 'enum ee: EE2 has the real value 2, not 1' # extra items cannot be seen and have no bad consequence anyway lib = ffi.verify("enum ee { EE1, EE2, EE3, EE4 };") assert lib.EE3 == 2 def test_enum_usage(): ffi = FFI() ffi.cdef("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") lib = ffi.verify("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") assert lib.EE2 == 1 s = ffi.new("sp", [lib.EE2]) assert s.x == 1 s.x = 17 assert s.x == 17 def test_anonymous_enum(): ffi = FFI() ffi.cdef("enum { EE1 }; enum { EE2, EE3 };") lib = ffi.verify("enum { EE1 }; enum { EE2, EE3 };") assert lib.EE1 == 0 assert lib.EE2 == 0 assert lib.EE3 == 1 def test_nonfull_anonymous_enum(): ffi = FFI() ffi.cdef("enum { EE1, ... }; enum { EE3, ... };") lib = ffi.verify("enum { EE2, EE1 }; enum { EE3 };") assert lib.EE1 == 1 assert lib.EE3 == 0 def test_nonfull_enum_syntax2(): ffi = FFI() ffi.cdef("enum ee { EE1, EE2=\t..., EE3 };") py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' assert ffi.string(ffi.cast('enum ee', -10)) == 'EE3' # ffi = FFI() ffi.cdef("enum ee { EE1, EE2=\t... };") py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' # ffi = FFI() ffi.cdef("enum ee2 { EE4=..., EE5=..., ... };") ffi.verify("enum ee2 { EE4=-1234-5, EE5 }; ") assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4' assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5' def test_get_set_errno(): ffi = FFI() ffi.cdef("int foo(int);") lib = ffi.verify(""" static int foo(int x) { errno += 1; return x * 7; } """) ffi.errno = 15 assert lib.foo(6) == 42 assert ffi.errno == 16 def test_define_int(): ffi = FFI() ffi.cdef("#define FOO ...\n" "\t#\tdefine\tBAR\t...\t\n" "#define BAZ ...\n") lib = ffi.verify("#define FOO 42\n" "#define BAR (-44)\n" "#define BAZ 0xffffffffffffffffULL\n") assert lib.FOO == 42 assert lib.BAR == -44 assert lib.BAZ == 0xffffffffffffffff def test_access_variable(): ffi = FFI() ffi.cdef("int foo(void);\n" "int somenumber;") lib = ffi.verify(""" static int somenumber = 2; static int foo(void) { return somenumber * 7; } """) assert lib.somenumber == 2 assert lib.foo() == 14 lib.somenumber = -6 assert lib.foo() == -42 assert lib.somenumber == -6 lib.somenumber = 2 # reset for the next run, if any def test_access_address_of_variable(): # access the address of 'somenumber': need a trick ffi = FFI() ffi.cdef("int somenumber; static int *const somenumberptr;") lib = ffi.verify(""" static int somenumber = 2; #define somenumberptr (&somenumber) """) assert lib.somenumber == 2 lib.somenumberptr[0] = 42 assert lib.somenumber == 42 lib.somenumber = 2 # reset for the next run, if any def test_access_array_variable(length=5): ffi = FFI() ffi.cdef("int foo(int);\n" "int somenumber[%s];" % (length,)) lib = ffi.verify(""" static int somenumber[] = {2, 2, 3, 4, 5}; static int foo(int i) { return somenumber[i] * 7; } """) if length == '': # a global variable of an unknown array length is implicitly # transformed into a global pointer variable, because we can only # work with array instances whose length we know. using a pointer # instead of an array gives the correct effects. assert repr(lib.somenumber).startswith("x * 7; } """) f = ffi.new("foo_t *") f.x = 6 assert lib.foo(f) == 42 def test_unknown_type(): ffi = FFI() ffi.cdef(""" typedef ... token_t; int foo(token_t *); #define TOKEN_SIZE ... """) lib = ffi.verify(""" typedef float token_t; static int foo(token_t *tk) { if (!tk) return -42; *tk += 1.601; return (int)*tk; } #define TOKEN_SIZE sizeof(token_t) """) # we cannot let ffi.new("token_t *") work, because we don't know ahead of # time if it's ok to ask 'sizeof(token_t)' in the C code or not. # See test_unknown_type_2. Workaround. tkmem = ffi.new("char[]", lib.TOKEN_SIZE) # zero-initialized tk = ffi.cast("token_t *", tkmem) results = [lib.foo(tk) for i in range(6)] assert results == [1, 3, 4, 6, 8, 9] assert lib.foo(ffi.NULL) == -42 def test_unknown_type_2(): ffi = FFI() ffi.cdef("typedef ... token_t;") lib = ffi.verify("typedef struct token_s token_t;") # assert did not crash, even though 'sizeof(token_t)' is not valid in C. def test_unknown_type_3(): ffi = FFI() ffi.cdef(""" typedef ... *token_p; token_p foo(token_p); """) lib = ffi.verify(""" typedef struct _token_s *token_p; token_p foo(token_p arg) { if (arg) return (token_p)0x12347; else return (token_p)0x12345; } """) p = lib.foo(ffi.NULL) assert int(ffi.cast("intptr_t", p)) == 0x12345 q = lib.foo(p) assert int(ffi.cast("intptr_t", q)) == 0x12347 def test_varargs(): ffi = FFI() ffi.cdef("int foo(int x, ...);") lib = ffi.verify(""" int foo(int x, ...) { va_list vargs; va_start(vargs, x); x -= va_arg(vargs, int); x -= va_arg(vargs, int); va_end(vargs); return x; } """) assert lib.foo(50, ffi.cast("int", 5), ffi.cast("int", 3)) == 42 def test_varargs_exact(): if sys.platform == 'win32': py.test.skip("XXX fixme: only gives warnings") ffi = FFI() ffi.cdef("int foo(int x, ...);") py.test.raises(VerificationError, ffi.verify, """ int foo(long long x, ...) { return x; } """) def test_varargs_struct(): ffi = FFI() ffi.cdef("struct foo_s { char a; int b; }; int foo(int x, ...);") lib = ffi.verify(""" struct foo_s { char a; int b; }; int foo(int x, ...) { va_list vargs; struct foo_s s; va_start(vargs, x); s = va_arg(vargs, struct foo_s); va_end(vargs); return s.a - s.b; } """) s = ffi.new("struct foo_s *", [b'B', 1]) assert lib.foo(50, s[0]) == ord('A') def test_autofilled_struct_as_argument(): ffi = FFI() ffi.cdef("struct foo_s { long a; double b; ...; };\n" "int foo(struct foo_s);") lib = ffi.verify(""" struct foo_s { double b; long a; }; int foo(struct foo_s s) { return s.a - (int)s.b; } """) s = ffi.new("struct foo_s *", [100, 1]) assert lib.foo(s[0]) == 99 assert lib.foo([100, 1]) == 99 def test_autofilled_struct_as_argument_dynamic(): ffi = FFI() ffi.cdef("struct foo_s { long a; ...; };\n" "int (*foo)(struct foo_s);") e = py.test.raises(TypeError, ffi.verify, """ struct foo_s { double b; long a; }; int foo1(struct foo_s s) { return s.a - (int)s.b; } int (*foo)(struct foo_s s) = &foo1; """) msg ='cannot pass as an argument a struct that was completed with verify()' assert msg in str(e.value) def test_func_returns_struct(): ffi = FFI() ffi.cdef(""" struct foo_s { int aa, bb; }; struct foo_s foo(int a, int b); """) lib = ffi.verify(""" struct foo_s { int aa, bb; }; struct foo_s foo(int a, int b) { struct foo_s r; r.aa = a*a; r.bb = b*b; return r; } """) s = lib.foo(6, 7) assert repr(s) == "" assert s.aa == 36 assert s.bb == 49 def test_func_as_funcptr(): ffi = FFI() ffi.cdef("int *(*const fooptr)(void);") lib = ffi.verify(""" int *foo(void) { return (int*)"foobar"; } int *(*fooptr)(void) = foo; """) foochar = ffi.cast("char *(*)(void)", lib.fooptr) s = foochar() assert ffi.string(s) == b"foobar" def test_funcptr_as_argument(): ffi = FFI() ffi.cdef(""" void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *)); """) ffi.verify("#include ") def test_func_as_argument(): ffi = FFI() ffi.cdef(""" void qsort(void *base, size_t nel, size_t width, int compar(const void *, const void *)); """) ffi.verify("#include ") def test_array_as_argument(): ffi = FFI() ffi.cdef(""" int strlen(char string[]); """) ffi.verify("#include ") def test_enum_as_argument(): ffi = FFI() ffi.cdef(""" enum foo_e { AA, BB, ... }; int foo_func(enum foo_e); """) lib = ffi.verify(""" enum foo_e { AA, CC, BB }; int foo_func(enum foo_e e) { return e; } """) assert lib.foo_func(lib.BB) == 2 py.test.raises(TypeError, lib.foo_func, "BB") def test_enum_as_function_result(): ffi = FFI() ffi.cdef(""" enum foo_e { AA, BB, ... }; enum foo_e foo_func(int x); """) lib = ffi.verify(""" enum foo_e { AA, CC, BB }; enum foo_e foo_func(int x) { return x; } """) assert lib.foo_func(lib.BB) == lib.BB == 2 def test_enum_values(): ffi = FFI() ffi.cdef("enum enum1_e { AA, BB };") lib = ffi.verify("enum enum1_e { AA, BB };") assert lib.AA == 0 assert lib.BB == 1 assert ffi.string(ffi.cast("enum enum1_e", 1)) == 'BB' def test_typedef_complete_enum(): ffi = FFI() ffi.cdef("typedef enum { AA, BB } enum1_t;") lib = ffi.verify("typedef enum { AA, BB } enum1_t;") assert ffi.string(ffi.cast("enum1_t", 1)) == 'BB' assert lib.AA == 0 assert lib.BB == 1 def test_typedef_broken_complete_enum(): ffi = FFI() ffi.cdef("typedef enum { AA, BB } enum1_t;") py.test.raises(VerificationError, ffi.verify, "typedef enum { AA, CC, BB } enum1_t;") def test_typedef_incomplete_enum(): 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("enum1_t", 1)) == '1' assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB' assert lib.AA == 0 assert lib.BB == 2 def test_typedef_enum_as_function_result(): ffi = FFI() ffi.cdef(""" typedef enum { AA, BB, ... } foo_t; foo_t foo_func(int x); """) lib = ffi.verify(""" typedef enum { AA, CC, BB } foo_t; foo_t foo_func(int x) { return x; } """) assert lib.foo_func(lib.BB) == lib.BB == 2 def test_callback_calling_convention(): py.test.skip("later") if sys.platform != 'win32': py.test.skip("Windows only") ffi = FFI() ffi.cdef(""" int call1(int(*__cdecl cb)(int)); int call2(int(*__stdcall cb)(int)); """) lib = ffi.verify(""" int call1(int(*__cdecl cb)(int)) { return cb(42) + 1; } int call2(int(*__stdcall cb)(int)) { return cb(-42) - 6; } """) xxx def test_opaque_integer_as_function_result(): import platform if platform.machine().startswith('sparc'): py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') # XXX bad abuse of "struct { ...; }". It only works a bit by chance # anyway. XXX think about something better :-( ffi = FFI() ffi.cdef(""" typedef struct { ...; } myhandle_t; myhandle_t foo(void); """) lib = ffi.verify(""" typedef short myhandle_t; myhandle_t foo(void) { return 42; } """) h = lib.foo() assert ffi.sizeof(h) == ffi.sizeof("short") def test_cannot_name_struct_type(): ffi = FFI() ffi.cdef("typedef struct { int x; } *sp; void foo(sp);") e = py.test.raises(VerificationError, ffi.verify, "typedef struct { int x; } *sp; void foo(sp);") assert 'in argument of foo: unknown type name' in str(e.value) def test_dont_check_unnamable_fields(): ffi = FFI() ffi.cdef("struct foo_s { struct { int x; } someone; };") ffi.verify("struct foo_s { struct { int x; } someone; };") # assert did not crash def test_nested_anonymous_struct_exact(): if sys.platform == 'win32': py.test.skip("nested anonymous struct/union") ffi = FFI() ffi.cdef(""" struct foo_s { struct { int a; char b; }; union { char c, d; }; }; """) ffi.verify(""" struct foo_s { struct { int a; char b; }; union { char c, d; }; }; """) p = ffi.new("struct foo_s *") assert ffi.sizeof(p[0]) == 3 * ffi.sizeof("int") # with alignment p.a = 1234567 p.b = b'X' p.c = b'Y' assert p.a == 1234567 assert p.b == b'X' assert p.c == b'Y' assert p.d == b'Y' def test_nested_anonymous_struct_exact_error(): if sys.platform == 'win32': py.test.skip("nested anonymous struct/union") ffi = FFI() ffi.cdef(""" struct foo_s { struct { int a; char b; }; union { char c, d; }; }; """) py.test.raises(VerificationError, ffi.verify, """ struct foo_s { struct { int a; short b; }; union { char c, d; }; }; """) py.test.raises(VerificationError, ffi.verify, """ struct foo_s { struct { int a; char e, b; }; union { char c, d; }; }; """) def test_nested_anonymous_struct_inexact_1(): ffi = FFI() ffi.cdef(""" struct foo_s { struct { char b; ...; }; union { char c, d; }; }; """) ffi.verify(""" struct foo_s { int a, padding; char c, d, b; }; """) assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") def test_nested_anonymous_struct_inexact_2(): ffi = FFI() ffi.cdef(""" struct foo_s { union { char c, d; }; struct { int a; char b; }; ...; }; """) ffi.verify(""" struct foo_s { int a, padding; char c, d, b; }; """) assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") def test_ffi_union(): ffi = FFI() ffi.cdef("union foo_u { char x; long *z; };") ffi.verify("union foo_u { char x; int y; long *z; };") def test_ffi_union_partial(): ffi = FFI() ffi.cdef("union foo_u { char x; ...; };") ffi.verify("union foo_u { char x; int y; };") assert ffi.sizeof("union foo_u") == 4 def test_ffi_union_with_partial_struct(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; union foo_u { struct foo_s s; };") ffi.verify("struct foo_s { int a; int x; }; " "union foo_u { char b[32]; struct foo_s s; };") assert ffi.sizeof("struct foo_s") == 8 assert ffi.sizeof("union foo_u") == 32 def test_ffi_union_partial_2(): ffi = FFI() ffi.cdef("typedef union { char x; ...; } u1;") ffi.verify("typedef union { char x; int y; } u1;") assert ffi.sizeof("u1") == 4 def test_ffi_union_with_partial_struct_2(): ffi = FFI() ffi.cdef("typedef struct { int x; ...; } s1;" "typedef union { s1 s; } u1;") ffi.verify("typedef struct { int a; int x; } s1; " "typedef union { char b[32]; s1 s; } u1;") assert ffi.sizeof("s1") == 8 assert ffi.sizeof("u1") == 32 assert ffi.offsetof("u1", "s") == 0 def test_ffi_struct_packed(): if sys.platform == 'win32': py.test.skip("needs a GCC extension") ffi = FFI() ffi.cdef("struct foo_s { int b; ...; };") ffi.verify(""" struct foo_s { char a; int b; } __attribute__((packed)); """) def test_tmpdir(): import tempfile, os, shutil from testing.udir import udir tmpdir = tempfile.mkdtemp(dir=str(udir)) ffi = FFI() ffi.cdef("int foo(int);") lib = ffi.verify("int foo(int a) { return a + 42; }", tmpdir=tmpdir) assert os.listdir(tmpdir) assert lib.foo(100) == 142 def test_bug1(): ffi = FFI() ffi.cdef(""" typedef struct tdlhandle_s { ...; } *tdl_handle_t; typedef struct my_error_code_ { tdl_handle_t *rh; } my_error_code_t; """) ffi.verify(""" typedef struct tdlhandle_s { int foo; } *tdl_handle_t; typedef struct my_error_code_ { tdl_handle_t *rh; } my_error_code_t; """) def test_bool(): if sys.platform == 'win32': py.test.skip("_Bool not in MSVC") ffi = FFI() ffi.cdef("struct foo_s { _Bool x; };" "_Bool foo(_Bool);") lib = ffi.verify(""" struct foo_s { _Bool x; }; int foo(int arg) { return !arg; } """) p = ffi.new("struct foo_s *") p.x = 1 assert p.x == 1 py.test.raises(OverflowError, "p.x = -1") py.test.raises(TypeError, "p.x = 0.0") assert lib.foo(1) == 0 assert lib.foo(0) == 1 py.test.raises(OverflowError, lib.foo, 42) py.test.raises(TypeError, lib.foo, 0.0) assert int(ffi.cast("_Bool", long(1))) == 1 assert int(ffi.cast("_Bool", long(0))) == 0 assert int(ffi.cast("_Bool", long(-1))) == 1 assert int(ffi.cast("_Bool", 10**200)) == 1 assert int(ffi.cast("_Bool", 10**40000)) == 1 # class Foo(object): def __int__(self): self.seen = 1 return result f = Foo() f.seen = 0 result = 42 assert int(ffi.cast("_Bool", f)) == 1 assert f.seen f.seen = 0 result = 0 assert int(ffi.cast("_Bool", f)) == 0 assert f.seen # py.test.raises(TypeError, ffi.cast, "_Bool", []) def test_bool_on_long_double(): if sys.platform == 'win32': py.test.skip("_Bool not in MSVC") f = 1E-250 if f == 0.0 or f*f != 0.0: py.test.skip("unexpected precision") ffi = FFI() ffi.cdef("long double square(long double f); _Bool opposite(_Bool);") lib = ffi.verify("long double square(long double f) { return f*f; }\n" "_Bool opposite(_Bool x) { return !x; }") f0 = lib.square(0.0) f2 = lib.square(f) f3 = lib.square(f * 2.0) if repr(f2) == repr(f3): py.test.skip("long double doesn't have enough precision") assert float(f0) == float(f2) == float(f3) == 0.0 # too tiny for 'double' assert int(ffi.cast("_Bool", f2)) == 1 assert int(ffi.cast("_Bool", f3)) == 1 assert int(ffi.cast("_Bool", f0)) == 0 py.test.raises(TypeError, lib.opposite, f2) def test_cannot_pass_float(): for basetype in ['char', 'short', 'int', 'long', 'long long']: for sign in ['signed', 'unsigned']: type = '%s %s' % (sign, basetype) ffi = FFI() ffi.cdef("struct foo_s { %s x; };\n" "int foo(%s);" % (type, type)) lib = ffi.verify(""" struct foo_s { %s x; }; int foo(%s arg) { return !arg; } """ % (type, type)) p = ffi.new("struct foo_s *") py.test.raises(TypeError, "p.x = 0.0") assert lib.foo(42) == 0 assert lib.foo(0) == 1 py.test.raises(TypeError, lib.foo, 0.0) def test_cast_from_int_type_to_bool(): ffi = FFI() for basetype in ['char', 'short', 'int', 'long', 'long long']: 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(): ffi = FFI() ffi.cdef(""" struct point_s { int x, y; }; struct foo_s { int z; struct point_s point; }; struct point_s sum_coord(struct point_s *); """) lib = ffi.verify(""" struct point_s { int x, y; }; struct foo_s { int z; struct point_s point; }; struct point_s sum_coord(struct point_s *point) { struct point_s r; r.x = point->x + point->y; r.y = point->x - point->y; return r; } """) p = ffi.new("struct foo_s *") p.point.x = 16 p.point.y = 9 py.test.raises(TypeError, lib.sum_coord, p.point) res = lib.sum_coord(ffi.addressof(p.point)) assert res.x == 25 assert res.y == 7 res2 = lib.sum_coord(ffi.addressof(res)) assert res2.x == 32 assert res2.y == 18 py.test.raises(TypeError, lib.sum_coord, res2) def test_callback_in_thread(): if sys.platform == 'win32': py.test.skip("pthread only") import os, subprocess, imp arg = os.path.join(os.path.dirname(__file__), 'callback_in_thread.py') g = subprocess.Popen([sys.executable, arg, os.path.dirname(imp.find_module('cffi')[1])]) result = g.wait() assert result == 0 def test_keepalive_lib(): ffi = FFI() ffi.cdef("int foobar(void);") lib = ffi.verify("int foobar(void) { return 42; }") func = lib.foobar ffi_r = weakref.ref(ffi) lib_r = weakref.ref(lib) del ffi import gc; gc.collect() # lib stays alive assert lib_r() is not None assert ffi_r() is not None assert func() == 42 def test_keepalive_ffi(): ffi = FFI() ffi.cdef("int foobar(void);") lib = ffi.verify("int foobar(void) { return 42; }") func = lib.foobar ffi_r = weakref.ref(ffi) lib_r = weakref.ref(lib) del lib import gc; gc.collect() # ffi stays alive assert ffi_r() is not None assert lib_r() is not None assert func() == 42 def test_FILE_stored_in_stdout(): if sys.platform == 'win32': py.test.skip("MSVC: cannot assign to stdout") ffi = FFI() ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);") lib = ffi.verify(""" #include FILE *setstdout(FILE *f) { FILE *result = stdout; stdout = f; return result; } """) import os fdr, fdw = os.pipe() fw1 = os.fdopen(fdw, 'wb', 256) old_stdout = lib.setstdout(fw1) try: # fw1.write(b"X") r = lib.printf(b"hello, %d!\n", ffi.cast("int", 42)) fw1.close() assert r == len("hello, 42!\n") # finally: lib.setstdout(old_stdout) # result = os.read(fdr, 256) os.close(fdr) # the 'X' might remain in the user-level buffer of 'fw1' and # end up showing up after the 'hello, 42!\n' assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" def test_FILE_stored_explicitly(): ffi = FFI() ffi.cdef("int myprintf(const char *, int); FILE *myfile;") lib = ffi.verify(""" #include FILE *myfile; int myprintf(const char *out, int value) { return fprintf(myfile, out, value); } """) import os fdr, fdw = os.pipe() fw1 = os.fdopen(fdw, 'wb', 256) lib.myfile = ffi.cast("FILE *", fw1) # fw1.write(b"X") r = lib.myprintf(b"hello, %d!\n", ffi.cast("int", 42)) fw1.close() assert r == len("hello, 42!\n") # result = os.read(fdr, 256) os.close(fdr) # the 'X' might remain in the user-level buffer of 'fw1' and # end up showing up after the 'hello, 42!\n' assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" def test_global_array_with_missing_length(): ffi = FFI() ffi.cdef("int fooarray[];") lib = ffi.verify("int fooarray[50];") assert repr(lib.fooarray).startswith("x; }") res = lib.myfunc(ffi2.new("foo_t *", {'x': 10})) assert res == 420 res = lib.myfunc(ffi1.new("foo_t *", {'x': -10})) assert res == -420 def test_include_enum(): ffi1 = FFI() ffi1.cdef("enum foo_e { AA, ... };") lib1 = ffi1.verify("enum foo_e { CC, BB, AA };") ffi2 = FFI() ffi2.include(ffi1) ffi2.cdef("int myfunc(enum foo_e);") lib2 = ffi2.verify("enum foo_e { CC, BB, AA };" "int myfunc(enum foo_e x) { return (int)x; }") res = lib2.myfunc(lib2.AA) assert res == 2 def test_enum_size(): cases = [('123', 4, 4294967295), ('4294967295U', 4, 4294967295), ('-123', 4, -1), ('-2147483647-1', 4, -1), ] if FFI().sizeof("long") == 8: cases += [('4294967296L', 8, 2**64-1), ('%dUL' % (2**64-1), 8, 2**64-1), ('-2147483649L', 8, -1), ('%dL-1L' % (1-2**63), 8, -1)] for hidden_value, expected_size, expected_minus1 in cases: if sys.platform == 'win32' and 'U' in hidden_value: continue # skipped on Windows ffi = FFI() ffi.cdef("enum foo_e { AA, BB, ... };") lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) assert lib.AA == 0 assert lib.BB == eval(hidden_value.replace('U', '').replace('L', '')) assert ffi.sizeof("enum foo_e") == expected_size assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 # test with the large value hidden: # disabled so far, doesn't work ## for hidden_value, expected_size, expected_minus1 in cases: ## ffi = FFI() ## ffi.cdef("enum foo_e { AA, BB, ... };") ## lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) ## assert lib.AA == 0 ## assert ffi.sizeof("enum foo_e") == expected_size ## assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 def test_enum_bug118(): maxulong = 256 ** FFI().sizeof("unsigned long") - 1 for c1, c2, c2c in [(0xffffffff, -1, ''), (maxulong, -1, ''), (-1, 0xffffffff, 'U'), (-1, maxulong, 'UL')]: if c2c and sys.platform == 'win32': continue # enums may always be signed with MSVC ffi = FFI() ffi.cdef("enum foo_e { AA=%s };" % c1) e = py.test.raises(VerificationError, ffi.verify, "enum foo_e { AA=%s%s };" % (c2, c2c)) assert str(e.value) == ('enum foo_e: AA has the real value %d, not %d' % (c2, c1)) def test_string_to_voidp_arg(): ffi = FFI() ffi.cdef("int myfunc(void *);") lib = ffi.verify("int myfunc(void *p) { return ((signed char *)p)[0]; }") res = lib.myfunc(b"hi!") assert res == ord(b"h") p = ffi.new("char[]", b"gah") res = lib.myfunc(p) assert res == ord(b"g") res = lib.myfunc(ffi.cast("void *", p)) assert res == ord(b"g") res = lib.myfunc(ffi.cast("int *", p)) assert res == ord(b"g") def test_callback_indirection(): ffi = FFI() ffi.cdef(""" int (*python_callback)(int how_many, int *values); void *const c_callback; /* pass this ptr to C routines */ int some_c_function(void *cb); """) lib = ffi.verify(""" #include #ifdef _WIN32 #include #define alloca _alloca #else # ifdef __FreeBSD__ # include # else # include # endif #endif static int (*python_callback)(int how_many, int *values); static int c_callback(int how_many, ...) { va_list ap; /* collect the "..." arguments into the values[] array */ int i, *values = alloca(how_many * sizeof(int)); va_start(ap, how_many); for (i=0; i" def test_bug_const_char_ptr_array_1(): ffi = FFI() ffi.cdef("""const char *a[...];""") lib = ffi.verify("""const char *a[5];""") assert repr(ffi.typeof(lib.a)) == "" def test_bug_const_char_ptr_array_2(): from cffi import FFI # ignore warnings ffi = FFI() ffi.cdef("""const int a[];""") lib = ffi.verify("""const int a[5];""") assert repr(ffi.typeof(lib.a)) == "" def _test_various_calls(force_libffi): cdef_source = """ int xvalue; long long ivalue, rvalue; float fvalue; double dvalue; long double Dvalue; signed char tf_bb(signed char x, signed char c); unsigned char tf_bB(signed char x, unsigned char c); short tf_bh(signed char x, short c); unsigned short tf_bH(signed char x, unsigned short c); int tf_bi(signed char x, int c); unsigned int tf_bI(signed char x, unsigned int c); long tf_bl(signed char x, long c); unsigned long tf_bL(signed char x, unsigned long c); long long tf_bq(signed char x, long long c); float tf_bf(signed char x, float c); double tf_bd(signed char x, double c); long double tf_bD(signed char x, long double c); """ if force_libffi: cdef_source = (cdef_source .replace('tf_', '(*const tf_') .replace('(signed char x', ')(signed char x')) ffi = FFI() ffi.cdef(cdef_source) lib = ffi.verify(""" int xvalue; long long ivalue, rvalue; float fvalue; double dvalue; long double Dvalue; #define S(letter) xvalue = x; letter##value = c; return rvalue; signed char tf_bb(signed char x, signed char c) { S(i) } unsigned char tf_bB(signed char x, unsigned char c) { S(i) } short tf_bh(signed char x, short c) { S(i) } unsigned short tf_bH(signed char x, unsigned short c) { S(i) } int tf_bi(signed char x, int c) { S(i) } unsigned int tf_bI(signed char x, unsigned int c) { S(i) } long tf_bl(signed char x, long c) { S(i) } unsigned long tf_bL(signed char x, unsigned long c) { S(i) } long long tf_bq(signed char x, long long c) { S(i) } float tf_bf(signed char x, float c) { S(f) } double tf_bd(signed char x, double c) { S(d) } long double tf_bD(signed char x, long double c) { S(D) } """) lib.rvalue = 0x7182838485868788 for kind, cname in [('b', 'signed char'), ('B', 'unsigned char'), ('h', 'short'), ('H', 'unsigned short'), ('i', 'int'), ('I', 'unsigned int'), ('l', 'long'), ('L', 'unsigned long'), ('q', 'long long'), ('f', 'float'), ('d', 'double'), ('D', 'long double')]: sign = +1 if 'unsigned' in cname else -1 lib.xvalue = 0 lib.ivalue = 0 lib.fvalue = 0 lib.dvalue = 0 lib.Dvalue = 0 fun = getattr(lib, 'tf_b' + kind) res = fun(-42, sign * 99) if kind == 'D': res = float(res) assert res == int(ffi.cast(cname, 0x7182838485868788)) assert lib.xvalue == -42 if kind in 'fdD': assert float(getattr(lib, kind + 'value')) == -99.0 else: assert lib.ivalue == sign * 99 def test_various_calls_direct(): _test_various_calls(force_libffi=False) def test_various_calls_libffi(): _test_various_calls(force_libffi=True) def test_ptr_to_opaque(): ffi = FFI() ffi.cdef("typedef ... foo_t; int f1(foo_t*); foo_t *f2(int);") lib = ffi.verify(""" #include typedef struct { int x; } foo_t; int f1(foo_t* p) { int x = p->x; free(p); return x; } foo_t *f2(int x) { foo_t *p = malloc(sizeof(foo_t)); p->x = x; return p; } """) p = lib.f2(42) x = lib.f1(p) assert x == 42 cffi-0.8.2/testing/test_ownlib.py0000644000076400001440000001057012034764754017313 0ustar arigousers00000000000000import py, sys import subprocess, weakref from cffi import FFI from cffi.backend_ctypes import CTypesBackend SOURCE = """\ #include int test_getting_errno(void) { errno = 123; return -1; } int test_setting_errno(void) { return errno; } int my_array[7] = {0, 1, 2, 3, 4, 5, 6}; """ class TestOwnLib(object): Backend = CTypesBackend def setup_class(cls): if sys.platform == 'win32': return from testing.udir import udir udir.join('testownlib.c').write(SOURCE) subprocess.check_call( 'gcc testownlib.c -shared -fPIC -o testownlib.so', cwd=str(udir), shell=True) cls.module = str(udir.join('testownlib.so')) def test_getting_errno(self): if sys.platform == 'win32': py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int test_getting_errno(void); """) ownlib = ffi.dlopen(self.module) res = ownlib.test_getting_errno() assert res == -1 assert ffi.errno == 123 def test_setting_errno(self): if sys.platform == 'win32': py.test.skip("fix the auto-generation of the tiny test lib") if self.Backend is CTypesBackend and '__pypy__' in sys.modules: py.test.skip("XXX errno issue with ctypes on pypy?") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int test_setting_errno(void); """) ownlib = ffi.dlopen(self.module) ffi.errno = 42 res = ownlib.test_setting_errno() assert res == 42 assert ffi.errno == 42 def test_my_array_7(self): if sys.platform == 'win32': py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int my_array[7]; """) ownlib = ffi.dlopen(self.module) for i in range(7): assert ownlib.my_array[i] == i assert len(ownlib.my_array) == 7 if self.Backend is CTypesBackend: py.test.skip("not supported by the ctypes backend") ownlib.my_array = list(range(10, 17)) for i in range(7): assert ownlib.my_array[i] == 10 + i ownlib.my_array = list(range(7)) for i in range(7): assert ownlib.my_array[i] == i def test_my_array_no_length(self): if sys.platform == 'win32': py.test.skip("fix the auto-generation of the tiny test lib") if self.Backend is CTypesBackend: py.test.skip("not supported by the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int my_array[]; """) ownlib = ffi.dlopen(self.module) for i in range(7): assert ownlib.my_array[i] == i py.test.raises(TypeError, len, ownlib.my_array) ownlib.my_array = list(range(10, 17)) for i in range(7): assert ownlib.my_array[i] == 10 + i ownlib.my_array = list(range(7)) for i in range(7): assert ownlib.my_array[i] == i def test_keepalive_lib(self): if sys.platform == 'win32': py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int test_getting_errno(void); """) ownlib = ffi.dlopen(self.module) ffi_r = weakref.ref(ffi) ownlib_r = weakref.ref(ownlib) func = ownlib.test_getting_errno del ffi import gc; gc.collect() # ownlib stays alive assert ownlib_r() is not None assert ffi_r() is not None # kept alive by ownlib res = func() assert res == -1 def test_keepalive_ffi(self): if sys.platform == 'win32': py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int test_getting_errno(void); """) ownlib = ffi.dlopen(self.module) ffi_r = weakref.ref(ffi) ownlib_r = weakref.ref(ownlib) func = ownlib.test_getting_errno del ownlib import gc; gc.collect() # ffi stays alive assert ffi_r() is not None assert ownlib_r() is not None # kept alive by ffi res = func() assert res == -1 assert ffi.errno == 123 cffi-0.8.2/testing/test_verify2.py0000644000076400001440000000047412000741367017375 0ustar arigousers00000000000000from .test_verify import * # This test file runs normally after test_verify. We only clean up the .c # sources, to check that it also works when we have only the .so. The # tests should run much faster than test_verify. def setup_module(): import cffi.verifier cffi.verifier.cleanup_tmpdir(keep_so=True) cffi-0.8.2/testing/test_platform.py0000644000076400001440000000153212104773620017631 0ustar arigousers00000000000000import os from cffi.ffiplatform import maybe_relative_path, flatten def test_not_absolute(): assert maybe_relative_path('foo/bar') == 'foo/bar' assert maybe_relative_path('test_platform.py') == 'test_platform.py' def test_different_absolute(): p = os.path.join('..', 'baz.py') assert maybe_relative_path(p) == p def test_absolute_mapping(): p = os.path.abspath('baz.py') assert maybe_relative_path(p) == 'baz.py' foobaz = os.path.join('foo', 'baz.py') assert maybe_relative_path(os.path.abspath(foobaz)) == foobaz def test_flatten(): assert flatten("foo") == "3sfoo" assert flatten(-10000000000000000000000000000) == \ "-10000000000000000000000000000i" assert flatten([4, 5]) == "2l4i5i" assert flatten({4: 5}) == "1d4i5i" assert flatten({"foo": ("bar", "baaz")}) == "1d3sfoo2l3sbar4sbaaz" cffi-0.8.2/testing/test_version.py0000644000076400001440000000314212306266214017471 0ustar arigousers00000000000000import py, os, sys import cffi, _cffi_backend def setup_module(mod): if '_cffi_backend' in sys.builtin_module_names: py.test.skip("this is embedded version") BACKEND_VERSIONS = { '0.4.2': '0.4', # did not change '0.7.1': '0.7', # did not change '0.7.2': '0.7', # did not change '0.8.1': '0.8', # did not change (essentially) } def test_version(): v = cffi.__version__ version_info = '.'.join(str(i) for i in cffi.__version_info__) assert v == version_info assert BACKEND_VERSIONS.get(v, v) == _cffi_backend.__version__ def test_doc_version(): parent = os.path.dirname(os.path.dirname(__file__)) p = os.path.join(parent, 'doc', 'source', 'conf.py') content = open(p).read() # v = cffi.__version__ assert ("version = '%s'\n" % v[:3]) in content assert ("release = '%s'\n" % v) in content def test_doc_version_file(): parent = os.path.dirname(os.path.dirname(__file__)) v = cffi.__version__ p = os.path.join(parent, 'doc', 'source', 'index.rst') content = open(p).read() assert ("cffi/cffi-%s.tar.gz" % v) in content def test_setup_version(): parent = os.path.dirname(os.path.dirname(__file__)) p = os.path.join(parent, 'setup.py') content = open(p).read() # v = cffi.__version__ assert ("version='%s'" % v) in content def test_c_version(): parent = os.path.dirname(os.path.dirname(__file__)) v = cffi.__version__ p = os.path.join(parent, 'c', 'test_c.py') content = open(p).read() assert (('assert __version__ == "%s"' % BACKEND_VERSIONS.get(v, v)) in content) cffi-0.8.2/testing/test_function.py0000644000076400001440000003267512306266214017646 0ustar arigousers00000000000000import py from cffi import FFI import math, os, sys import ctypes.util from cffi.backend_ctypes import CTypesBackend from testing.udir import udir try: from StringIO import StringIO except ImportError: from io import StringIO class FdWriteCapture(object): """xxx limited to capture at most 512 bytes of output, according to the Posix manual.""" def __init__(self, capture_fd): self.capture_fd = capture_fd def __enter__(self): self.read_fd, self.write_fd = os.pipe() self.copy_fd = os.dup(self.capture_fd) os.dup2(self.write_fd, self.capture_fd) return self def __exit__(self, *args): os.dup2(self.copy_fd, self.capture_fd) os.close(self.copy_fd) os.close(self.write_fd) self._value = os.read(self.read_fd, 512) os.close(self.read_fd) def getvalue(self): return self._value lib_m = 'm' if sys.platform == 'win32': #there is a small chance this fails on Mingw via environ $CC import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': lib_m = 'msvcrt' class TestFunction(object): Backend = CTypesBackend def test_sin(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" double sin(double x); """) m = ffi.dlopen(lib_m) x = m.sin(1.23) assert x == math.sin(1.23) def test_sinf(self): if sys.platform == 'win32': py.test.skip("no sinf found in the Windows stdlib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" float sinf(float x); """) m = ffi.dlopen(lib_m) x = m.sinf(1.23) assert type(x) is float assert x != math.sin(1.23) # rounding effects assert abs(x - math.sin(1.23)) < 1E-6 def test_sin_no_return_value(self): # check that 'void'-returning functions work too ffi = FFI(backend=self.Backend()) ffi.cdef(""" void sin(double x); """) m = ffi.dlopen(lib_m) x = m.sin(1.23) assert x is None def test_dlopen_filename(self): path = ctypes.util.find_library(lib_m) if not path: py.test.skip("%s not found" % lib_m) ffi = FFI(backend=self.Backend()) ffi.cdef(""" double cos(double x); """) m = ffi.dlopen(path) x = m.cos(1.23) assert x == math.cos(1.23) m = ffi.dlopen(os.path.basename(path)) x = m.cos(1.23) assert x == math.cos(1.23) def test_dlopen_flags(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" double cos(double x); """) m = ffi.dlopen(lib_m, ffi.RTLD_LAZY | ffi.RTLD_LOCAL) x = m.cos(1.23) assert x == math.cos(1.23) def test_tlsalloc(self): if sys.platform != 'win32': py.test.skip("win32 only") if self.Backend is CTypesBackend: py.test.skip("ctypes complains on wrong calling conv") ffi = FFI(backend=self.Backend()) ffi.cdef("long TlsAlloc(void); int TlsFree(long);") lib = ffi.dlopen('KERNEL32.DLL') x = lib.TlsAlloc() assert x != 0 y = lib.TlsFree(x) assert y != 0 def test_fputs(self): if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stderr' in the lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fputs(const char *, void *); void *stderr; """) ffi.C = ffi.dlopen(None) ffi.C.fputs # fetch before capturing, for easier debugging with FdWriteCapture(2) as fd: ffi.C.fputs(b"hello\n", ffi.C.stderr) ffi.C.fputs(b" world\n", ffi.C.stderr) res = fd.getvalue() assert res == b'hello\n world\n' def test_fputs_without_const(self): if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stderr' in the lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fputs(char *, void *); void *stderr; """) ffi.C = ffi.dlopen(None) ffi.C.fputs # fetch before capturing, for easier debugging with FdWriteCapture(2) as fd: ffi.C.fputs(b"hello\n", ffi.C.stderr) ffi.C.fputs(b" world\n", ffi.C.stderr) res = fd.getvalue() assert res == b'hello\n world\n' def test_vararg(self): if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stderr' in the lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fprintf(void *, const char *format, ...); void *stderr; """) ffi.C = ffi.dlopen(None) with FdWriteCapture(2) as fd: ffi.C.fprintf(ffi.C.stderr, b"hello with no arguments\n") ffi.C.fprintf(ffi.C.stderr, b"hello, %s!\n", ffi.new("char[]", b"world")) ffi.C.fprintf(ffi.C.stderr, ffi.new("char[]", b"hello, %s!\n"), ffi.new("char[]", b"world2")) ffi.C.fprintf(ffi.C.stderr, b"hello int %d long %ld long long %lld\n", ffi.cast("int", 42), ffi.cast("long", 84), ffi.cast("long long", 168)) ffi.C.fprintf(ffi.C.stderr, b"hello %p\n", ffi.NULL) res = fd.getvalue() assert res == (b"hello with no arguments\n" b"hello, world!\n" b"hello, world2!\n" b"hello int 42 long 84 long long 168\n" b"hello (nil)\n") def test_must_specify_type_of_vararg(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" int printf(const char *format, ...); """) ffi.C = ffi.dlopen(None) e = py.test.raises(TypeError, ffi.C.printf, b"hello %d\n", 42) assert str(e.value) == ("argument 2 passed in the variadic part " "needs to be a cdata object (got int)") def test_function_has_a_c_type(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" int puts(const char *); """) ffi.C = ffi.dlopen(None) fptr = ffi.C.puts assert ffi.typeof(fptr) == ffi.typeof("int(*)(const char*)") if self.Backend is CTypesBackend: assert repr(fptr).startswith("" % (cb,) res = fptr(b"Hello") assert res == 42 # if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stderr' in the lib") ffi.cdef(""" int fputs(const char *, void *); void *stderr; """) ffi.C = ffi.dlopen(None) fptr = ffi.cast("int(*)(const char *txt, void *)", ffi.C.fputs) assert fptr == ffi.C.fputs assert repr(fptr).startswith(" lambda (closure) -> container -> callback return callback class Data(object): pass ffi = FFI(backend=self.Backend()) data = Data() callback = make_callback(data) wr = weakref.ref(data) del callback, data for i in range(3): if wr() is not None: import gc; gc.collect() assert wr() is None # 'data' does not leak cffi-0.8.2/testing/test_parsing.py0000644000076400001440000002246112306266214017454 0ustar arigousers00000000000000import py, sys, re from cffi import FFI, FFIError, CDefError, VerificationError class FakeBackend(object): def nonstandard_integer_types(self): return {} def sizeof(self, name): return 1 def load_library(self, name, flags): if sys.platform == 'win32': assert name is None or "msvcr" in name else: assert name is None or "libc" in name or "libm" in name return FakeLibrary() def new_function_type(self, args, result, has_varargs): args = [arg.cdecl for arg in args] result = result.cdecl return FakeType( '' % (', '.join(args), result, has_varargs)) def new_primitive_type(self, name): assert name == name.lower() return FakeType('<%s>' % name) def new_pointer_type(self, itemtype): return FakeType('' % (itemtype,)) def new_struct_type(self, name): return FakeStruct(name) def complete_struct_or_union(self, s, fields, tp=None, totalsize=-1, totalalignment=-1, sflags=0): assert isinstance(s, FakeStruct) s.fields = fields def new_array_type(self, ptrtype, length): return FakeType('' % (ptrtype, length)) def new_void_type(self): return FakeType("") def cast(self, x, y): return 'casted!' def _get_types(self): return "CData", "CType" class FakeType(object): def __init__(self, cdecl): self.cdecl = cdecl def __str__(self): return self.cdecl class FakeStruct(object): def __init__(self, name): self.name = name def __str__(self): return ', '.join([str(y) + str(x) for x, y, z in self.fields]) class FakeLibrary(object): def load_function(self, BType, name): return FakeFunction(BType, name) class FakeFunction(object): def __init__(self, BType, name): self.BType = str(BType) self.name = name lib_m = "m" if sys.platform == 'win32': #there is a small chance this fails on Mingw via environ $CC import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': lib_m = 'msvcrt' def test_simple(): ffi = FFI(backend=FakeBackend()) ffi.cdef("double sin(double x);") m = ffi.dlopen(lib_m) func = m.sin # should be a callable on real backends assert func.name == 'sin' assert func.BType == '), , False>' def test_pipe(): ffi = FFI(backend=FakeBackend()) ffi.cdef("int pipe(int pipefd[2]);") C = ffi.dlopen(None) func = C.pipe assert func.name == 'pipe' assert func.BType == '>), , False>' def test_vararg(): ffi = FFI(backend=FakeBackend()) ffi.cdef("short foo(int, ...);") C = ffi.dlopen(None) func = C.foo assert func.name == 'foo' assert func.BType == '), , True>' def test_no_args(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" int foo(void); """) C = ffi.dlopen(None) assert C.foo.BType == ', False>' def test_typedef(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" typedef unsigned int UInt; typedef UInt UIntReally; UInt foo(void); """) C = ffi.dlopen(None) assert str(ffi.typeof("UIntReally")) == '' assert C.foo.BType == ', False>' def test_typedef_more_complex(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" typedef struct { int a, b; } foo_t, *foo_p; int foo(foo_p[]); """) C = ffi.dlopen(None) assert str(ffi.typeof("foo_t")) == 'a, b' assert str(ffi.typeof("foo_p")) == 'a, b>' assert C.foo.BType == ('a, b>>), , False>') def test_typedef_array_convert_array_to_pointer(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" typedef int (*fn_t)(int[5]); """) with ffi._lock: type = ffi._parser.parse_type("fn_t") BType = ffi._get_cached_btype(type) assert str(BType) == '>), , False>' def test_remove_comments(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" double /*comment here*/ sin // blah blah /* multi- line- //comment */ ( // foo double // bar /* <- ignored, because it's in a comment itself x, double/*several*//*comment*/y) /*on the same line*/ ; """) m = ffi.dlopen(lib_m) func = m.sin assert func.name == 'sin' assert func.BType == ', ), , False>' def test_define_not_supported_for_now(): ffi = FFI(backend=FakeBackend()) e = py.test.raises(CDefError, ffi.cdef, "#define FOO 42") assert str(e.value) == \ 'only supports the syntax "#define FOO ..." for now (literally)' def test_unnamed_struct(): ffi = FFI(backend=FakeBackend()) ffi.cdef("typedef struct { int x; } foo_t;\n" "typedef struct { int y; } *bar_p;\n") assert 'typedef foo_t' in ffi._parser._declarations assert 'typedef bar_p' in ffi._parser._declarations assert 'anonymous foo_t' in ffi._parser._declarations type_foo = ffi._parser.parse_type("foo_t") type_bar = ffi._parser.parse_type("bar_p").totype assert repr(type_foo) == "" assert repr(type_bar) == "" py.test.raises(VerificationError, type_bar.get_c_name) assert type_foo.get_c_name() == "foo_t" def test_override(): ffi = FFI(backend=FakeBackend()) C = ffi.dlopen(None) ffi.cdef("int foo(void);") py.test.raises(FFIError, ffi.cdef, "long foo(void);") assert C.foo.BType == ', False>' ffi.cdef("long foo(void);", override=True) assert C.foo.BType == ', False>' def test_cannot_have_only_variadic_part(): # this checks that we get a sensible error if we try "int foo(...);" ffi = FFI() e = py.test.raises(CDefError, ffi.cdef, "int foo(...);") assert str(e.value) == \ "foo: a function with only '(...)' as argument is not correct C" def test_parse_error(): 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") def test_unknown_name(): ffi = FFI() e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown", 0) assert str(e.value) == "unknown identifier 'foobarbazunknown'" e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown*", 0) 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(): prefix = "" if sys.version_info < (3,) else "b" ffi = FFI() ffi.cdef("typedef char FILE;") assert repr(ffi.cast("FILE", 123)) == "" % prefix ffi.cdef("typedef char int32_t;") assert repr(ffi.cast("int32_t", 123)) == "" % prefix 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);") def test__is_constant_globalvar(): from cffi.cparser import Parser, _get_parser for input, expected_output in [ ("int a;", False), ("const int a;", True), ("int *a;", False), ("const int *a;", False), ("int const *a;", False), ("int *const a;", True), ("int a[5];", False), ("const int a[5];", False), ("int *a[5];", False), ("const int *a[5];", False), ("int const *a[5];", False), ("int *const a[5];", False), ("int a[5][6];", False), ("const int a[5][6];", False), ]: p = Parser() ast = _get_parser().parse(input) decl = ast.children()[0][1] node = decl.type assert p._is_constant_globalvar(node) == expected_output cffi-0.8.2/testing/test_vgen2.py0000644000076400001440000000065112007242111017013 0ustar arigousers00000000000000import cffi.verifier from .test_vgen import * # This test file runs normally after test_vgen. We only clean up the .c # sources, to check that it also works when we have only the .so. The # tests should run much faster than test_vgen. def setup_module(): cffi.verifier.cleanup_tmpdir(keep_so=True) cffi.verifier._FORCE_GENERIC_ENGINE = True def teardown_module(): cffi.verifier._FORCE_GENERIC_ENGINE = False cffi-0.8.2/testing/__init__.py0000644000076400001440000000000011764325060016474 0ustar arigousers00000000000000cffi-0.8.2/testing/test_cdata.py0000644000076400001440000000150012034761544017060 0ustar arigousers00000000000000import py from cffi import FFI class FakeBackend(object): def nonstandard_integer_types(self): return {} def sizeof(self, name): return 1 def load_library(self, path): return "fake library" def new_primitive_type(self, name): return FakeType("primitive " + name) def new_void_type(self): return FakeType("void") def new_pointer_type(self, x): return FakeType('ptr-to-%r' % (x,)) def cast(self, x, y): return 'casted!' def _get_types(self): return "CData", "CType" class FakeType(object): def __init__(self, cdecl): self.cdecl = cdecl def test_typeof(): ffi = FFI(backend=FakeBackend()) clong = ffi.typeof("signed long int") assert isinstance(clong, FakeType) assert clong.cdecl == 'primitive long' cffi-0.8.2/testing/snippets/0000755000076400001440000000000012306266401016236 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_package_1/0000755000076400001440000000000012306266401022352 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_package_1/build/0000755000076400001440000000000012306266401023451 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_package_1/build/lib.linux-x86_64-3.2/0000755000076400001440000000000012306266401026611 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_package_1/build/lib.linux-x86_64-3.2/snip_setuptools_verify1/0000755000076400001440000000000012306266401033530 5ustar arigousers00000000000000././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000cffi-0.8.2/testing/snippets/setuptools_package_1/build/lib.linux-x86_64-3.2/snip_setuptools_verify1/__init__.pycffi-0.8.2/testing/snippets/setuptools_package_1/build/lib.linux-x86_64-3.2/snip_setuptools_verify1/0000644000076400001440000000065412050227574033543 0ustar arigousers00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/setuptools_package_1/setup.py0000644000076400017500000000031312050227574024031 0ustar arigoarigo00000000000000 from setuptools import setup import snip_setuptools_verify1 setup( zip_safe=False, packages=['snip_setuptools_verify1'], ext_modules=[snip_setuptools_verify1.ffi.verifier.get_extension()]) cffi-0.8.2/testing/snippets/setuptools_package_1/snip_setuptools_verify1/0000755000076400001440000000000012306266401027271 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py0000644000076400017500000000065412050227574031357 0ustar arigoarigo00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/setuptools_module/0000755000076400001440000000000012306266401022024 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_module/build/0000755000076400001440000000000012306266401023123 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_module/build/lib.linux-x86_64-3.2/0000755000076400001440000000000012306266401026263 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_module/build/lib.linux-x86_64-3.2/snip_setuptools_verify.py0000644000076400001440000000065412050227574033504 0ustar arigousers00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/setuptools_module/setup.py0000644000076400017500000000031212050227574023502 0ustar arigoarigo00000000000000 from setuptools import setup import snip_setuptools_verify setup( zip_safe=False, py_modules=['snip_setuptools_verify'], ext_modules=[snip_setuptools_verify.ffi.verifier.get_extension()]) cffi-0.8.2/testing/snippets/setuptools_module/snip_setuptools_verify.py0000644000076400017500000000065412050227574027211 0ustar arigoarigo00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/setuptools_package_2/0000755000076400001440000000000012306266401022353 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_package_2/build/0000755000076400001440000000000012306266401023452 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_package_2/build/lib.linux-x86_64-3.2/0000755000076400001440000000000012306266401026612 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_package_2/build/lib.linux-x86_64-3.2/snip_setuptools_verify2/0000755000076400001440000000000012306266401033532 5ustar arigousers00000000000000././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000cffi-0.8.2/testing/snippets/setuptools_package_2/build/lib.linux-x86_64-3.2/snip_setuptools_verify2/__init__.pycffi-0.8.2/testing/snippets/setuptools_package_2/build/lib.linux-x86_64-3.2/snip_setuptools_verify2/0000644000076400001440000000073012050227574033540 0ustar arigousers00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with ext_package='snip_setuptools_verify2', force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/setuptools_package_2/setup.py0000644000076400017500000000036612050227574024042 0ustar arigoarigo00000000000000 from setuptools import setup import snip_setuptools_verify2 setup( zip_safe=False, packages=['snip_setuptools_verify2'], ext_package='snip_setuptools_verify2', ext_modules=[snip_setuptools_verify2.ffi.verifier.get_extension()]) cffi-0.8.2/testing/snippets/setuptools_package_2/snip_setuptools_verify2/0000755000076400001440000000000012306266401027273 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py0000644000076400017500000000073012050227574031354 0ustar arigoarigo00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with ext_package='snip_setuptools_verify2', force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/distutils_package_1/0000755000076400001440000000000012306266401022155 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_package_1/build/0000755000076400001440000000000012306266401023254 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_package_1/build/lib.linux-x86_64-3.2/0000755000076400001440000000000012306266401026414 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_package_1/build/lib.linux-x86_64-3.2/snip_basic_verify1/0000755000076400001440000000000012306266401032173 5ustar arigousers00000000000000././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000cffi-0.8.2/testing/snippets/distutils_package_1/build/lib.linux-x86_64-3.2/snip_basic_verify1/__init__.pycffi-0.8.2/testing/snippets/distutils_package_1/build/lib.linux-x86_64-3.2/snip_basic_verify1/__init0000644000076400001440000000065412050227574033370 0ustar arigousers00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/distutils_package_1/setup.py0000644000076400017500000000025412050227574023640 0ustar arigoarigo00000000000000 from distutils.core import setup import snip_basic_verify1 setup( packages=['snip_basic_verify1'], ext_modules=[snip_basic_verify1.ffi.verifier.get_extension()]) cffi-0.8.2/testing/snippets/distutils_package_1/snip_basic_verify1/0000755000076400001440000000000012306266401025734 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_package_1/snip_basic_verify1/__init__.py0000644000076400017500000000065412050227574030022 0ustar arigoarigo00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/distutils_package_2/0000755000076400001440000000000012306266401022156 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_package_2/build/0000755000076400001440000000000012306266401023255 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_package_2/build/lib.linux-x86_64-3.2/0000755000076400001440000000000012306266401026415 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_package_2/build/lib.linux-x86_64-3.2/snip_basic_verify2/0000755000076400001440000000000012306266401032175 5ustar arigousers00000000000000././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000cffi-0.8.2/testing/snippets/distutils_package_2/build/lib.linux-x86_64-3.2/snip_basic_verify2/__init__.pycffi-0.8.2/testing/snippets/distutils_package_2/build/lib.linux-x86_64-3.2/snip_basic_verify2/__init0000644000076400001440000000072312050227574033367 0ustar arigousers00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with ext_package='snip_basic_verify2', force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/distutils_package_2/snip_basic_verify2/0000755000076400001440000000000012306266401025736 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_package_2/snip_basic_verify2/__init__.py0000644000076400017500000000072312050227574030021 0ustar arigoarigo00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with ext_package='snip_basic_verify2', force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/distutils_package_2/setup.py0000644000076400017500000000032212050227574023635 0ustar arigoarigo00000000000000 from distutils.core import setup import snip_basic_verify2 setup( packages=['snip_basic_verify2'], ext_package='snip_basic_verify2', ext_modules=[snip_basic_verify2.ffi.verifier.get_extension()]) cffi-0.8.2/testing/snippets/distutils_module/0000755000076400001440000000000012306266401021627 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_module/snip_basic_verify.py0000644000076400017500000000065412050227574025654 0ustar arigoarigo00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/distutils_module/build/0000755000076400001440000000000012306266401022726 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_module/build/lib.linux-x86_64-3.2/0000755000076400001440000000000012306266401026066 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/distutils_module/build/lib.linux-x86_64-3.2/snip_basic_verify.py0000644000076400001440000000065412050227574032147 0ustar arigousers00000000000000 from cffi import FFI import sys ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """, libraries=[], # or a list of libraries to link with force_generic_engine=hasattr(sys, '_force_generic_engine_')) cffi-0.8.2/testing/snippets/distutils_module/setup.py0000644000076400017500000000025312050227574023311 0ustar arigoarigo00000000000000 from distutils.core import setup import snip_basic_verify setup( py_modules=['snip_basic_verify'], ext_modules=[snip_basic_verify.ffi.verifier.get_extension()]) cffi-0.8.2/testing/snippets/infrastructure/0000755000076400001440000000000012306266401021316 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/infrastructure/snip_infrastructure/0000755000076400001440000000000012306266401025427 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/infrastructure/snip_infrastructure/__init__.py0000644000076400017500000000003312050227574027504 0ustar arigoarigo00000000000000 def func(): return 42 cffi-0.8.2/testing/snippets/infrastructure/build/0000755000076400001440000000000012306266401022415 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/infrastructure/build/lib/0000755000076400001440000000000012306266401023163 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/infrastructure/build/lib/snip_infrastructure/0000755000076400001440000000000012306266401027274 5ustar arigousers00000000000000cffi-0.8.2/testing/snippets/infrastructure/build/lib/snip_infrastructure/__init__.py0000644000076400001440000000003312050227574031405 0ustar arigousers00000000000000 def func(): return 42 cffi-0.8.2/testing/snippets/infrastructure/setup.py0000644000076400017500000000014412050227574022777 0ustar arigoarigo00000000000000 from distutils.core import setup setup(packages=['snip_infrastructure'], requires=['cffi']) cffi-0.8.2/testing/test_ctypes.py0000644000076400001440000000244312043307301017305 0ustar arigousers00000000000000import py, sys from testing import backend_tests from cffi.backend_ctypes import CTypesBackend class TestCTypes(backend_tests.BackendTests): # for individual tests see # ====> backend_tests.py Backend = CTypesBackend TypeRepr = "'>" def test_array_of_func_ptr(self): py.test.skip("ctypes backend: not supported: " "initializers for function pointers") def test_structptr_argument(self): py.test.skip("ctypes backend: not supported: passing a list " "for a pointer argument") def test_array_argument_as_list(self): py.test.skip("ctypes backend: not supported: passing a list " "for a pointer argument") def test_cast_to_array_type(self): py.test.skip("ctypes backend: not supported: casting to array") def test_nested_anonymous_struct(self): py.test.skip("ctypes backend: not supported: nested anonymous struct") def test_nested_anonymous_union(self): py.test.skip("ctypes backend: not supported: nested anonymous union") def test_CData_CType_2(self): if sys.version_info >= (3,): py.test.skip("ctypes backend: not supported in Python 3: CType") backend_tests.BackendTests.test_CData_CType_2(self) cffi-0.8.2/testing/udir.py0000644000076400001440000000010311764325060015704 0ustar arigousers00000000000000import py udir = py.path.local.make_numbered_dir(prefix = 'ffi-') cffi-0.8.2/testing/test_zintegration.py0000644000076400001440000001031012176750317020523 0ustar arigousers00000000000000import py, os, sys, shutil import imp import subprocess from testing.udir import udir def create_venv(name): tmpdir = udir.join(name) try: subprocess.check_call(['virtualenv', '--distribute', '-p', os.path.abspath(sys.executable), str(tmpdir)]) except OSError as e: py.test.skip("Cannot execute virtualenv: %s" % (e,)) site_packages = None for dirpath, dirnames, filenames in os.walk(str(tmpdir)): if os.path.basename(dirpath) == 'site-packages': site_packages = dirpath break if site_packages: try: from cffi import _pycparser modules = ('cffi', '_cffi_backend') except ImportError: modules = ('cffi', '_cffi_backend', 'pycparser') try: import ply except ImportError: pass else: modules += ('ply',) # needed for older versions of pycparser for module in modules: target = imp.find_module(module)[1] os.symlink(target, os.path.join(site_packages, os.path.basename(target))) return tmpdir SNIPPET_DIR = py.path.local(__file__).join('..', 'snippets') def really_run_setup_and_program(dirname, venv_dir, python_snippet): def remove(dir): dir = str(SNIPPET_DIR.join(dirname, dir)) shutil.rmtree(dir, ignore_errors=True) remove('build') remove('__pycache__') for basedir in os.listdir(str(SNIPPET_DIR.join(dirname))): remove(os.path.join(basedir, '__pycache__')) olddir = os.getcwd() python_f = udir.join('x.py') python_f.write(py.code.Source(python_snippet)) try: os.chdir(str(SNIPPET_DIR.join(dirname))) vp = str(venv_dir.join('bin/python')) subprocess.check_call((vp, 'setup.py', 'clean')) subprocess.check_call((vp, 'setup.py', 'install')) subprocess.check_call((vp, str(python_f))) finally: os.chdir(olddir) def run_setup_and_program(dirname, python_snippet): venv_dir = create_venv(dirname + '-cpy') really_run_setup_and_program(dirname, venv_dir, python_snippet) # sys._force_generic_engine_ = True try: venv_dir = create_venv(dirname + '-gen') really_run_setup_and_program(dirname, venv_dir, python_snippet) finally: del sys._force_generic_engine_ # the two files lextab.py and yacctab.py are created by not-correctly- # installed versions of pycparser. assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'lextab.py'))) assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'yacctab.py'))) def test_infrastructure(): run_setup_and_program('infrastructure', ''' import snip_infrastructure assert snip_infrastructure.func() == 42 ''') def test_distutils_module(): run_setup_and_program("distutils_module", ''' import snip_basic_verify p = snip_basic_verify.C.getpwuid(0) assert snip_basic_verify.ffi.string(p.pw_name) == b"root" ''') def test_distutils_package_1(): run_setup_and_program("distutils_package_1", ''' import snip_basic_verify1 p = snip_basic_verify1.C.getpwuid(0) assert snip_basic_verify1.ffi.string(p.pw_name) == b"root" ''') def test_distutils_package_2(): run_setup_and_program("distutils_package_2", ''' import snip_basic_verify2 p = snip_basic_verify2.C.getpwuid(0) assert snip_basic_verify2.ffi.string(p.pw_name) == b"root" ''') def test_setuptools_module(): run_setup_and_program("setuptools_module", ''' import snip_setuptools_verify p = snip_setuptools_verify.C.getpwuid(0) assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root" ''') def test_setuptools_package_1(): run_setup_and_program("setuptools_package_1", ''' import snip_setuptools_verify1 p = snip_setuptools_verify1.C.getpwuid(0) assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root" ''') def test_setuptools_package_2(): run_setup_and_program("setuptools_package_2", ''' import snip_setuptools_verify2 p = snip_setuptools_verify2.C.getpwuid(0) assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root" ''') cffi-0.8.2/testing/test_unicode_literals.py0000644000076400001440000000420212306266214021327 0ustar arigousers00000000000000# # ---------------------------------------------- # WARNING, ALL LITERALS IN THIS FILE ARE UNICODE # ---------------------------------------------- # from __future__ import unicode_literals # # # import sys, math from cffi import FFI lib_m = "m" if sys.platform == 'win32': #there is a small chance this fails on Mingw via environ $CC import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': lib_m = 'msvcrt' def test_cast(): ffi = FFI() assert int(ffi.cast("int", 3.14)) == 3 # unicode literal def test_new(): ffi = FFI() assert ffi.new("int[]", [3, 4, 5])[2] == 5 # unicode literal def test_typeof(): ffi = FFI() tp = ffi.typeof("int[51]") # unicode literal assert tp.length == 51 def test_sizeof(): ffi = FFI() assert ffi.sizeof("int[51]") == 51 * 4 # unicode literal def test_alignof(): ffi = FFI() assert ffi.alignof("int[51]") == 4 # unicode literal def test_getctype(): ffi = FFI() assert ffi.getctype("int**") == "int * *" # unicode literal assert type(ffi.getctype("int**")) is str def test_cdef(): ffi = FFI() ffi.cdef("typedef int foo_t[50];") # unicode literal def test_offsetof(): ffi = FFI() ffi.cdef("typedef struct { int x, y; } foo_t;") assert ffi.offsetof("foo_t", "y") == 4 # unicode literal def test_enum(): ffi = FFI() ffi.cdef("enum foo_e { AA, BB, CC };") # unicode literal x = ffi.cast("enum foo_e", 1) assert int(ffi.cast("int", x)) == 1 def test_dlopen(): ffi = FFI() ffi.cdef("double sin(double x);") m = ffi.dlopen(lib_m) # unicode literal x = m.sin(1.23) assert x == math.sin(1.23) def test_verify(): ffi = FFI() ffi.cdef("double test_verify_1(double x);") # unicode literal lib = ffi.verify("double test_verify_1(double x) { return x * 42.0; }") assert lib.test_verify_1(-1.5) == -63.0 def test_callback(): ffi = FFI() cb = ffi.callback("int(int)", # unicode literal lambda x: x + 42) assert cb(5) == 47 cffi-0.8.2/demo/0000755000076400001440000000000012306266401013640 5ustar arigousers00000000000000cffi-0.8.2/demo/readdir2.py0000644000076400001440000000304612006757604015720 0ustar arigousers00000000000000# A Linux-only demo, using verify() instead of hard-coding the exact layouts # import sys from cffi import FFI if not sys.platform.startswith('linux'): raise Exception("Linux-only demo") ffi = FFI() ffi.cdef(""" typedef ... DIR; struct dirent { unsigned char d_type; /* type of file; not supported by all file system types */ char d_name[...]; /* filename */ ...; }; int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); int openat(int dirfd, const char *pathname, int flags); DIR *fdopendir(int fd); int closedir(DIR *dirp); static const int DT_DIR; """) ffi.C = ffi.verify(""" #ifndef _ATFILE_SOURCE # define _ATFILE_SOURCE #endif #ifndef _BSD_SOURCE # define _BSD_SOURCE #endif #include #include #include """) def walk(basefd, path): print '{', path dirfd = ffi.C.openat(basefd, path, 0) if dirfd < 0: # error in openat() return dir = ffi.C.fdopendir(dirfd) dirent = ffi.new("struct dirent *") result = ffi.new("struct dirent **") while True: if ffi.C.readdir_r(dir, dirent, result): # error in readdir_r() break if result[0] == ffi.NULL: break name = ffi.string(dirent.d_name) print '%3d %s' % (dirent.d_type, name) if dirent.d_type == ffi.C.DT_DIR and name != '.' and name != '..': walk(dirfd, name) ffi.C.closedir(dir) print '}' walk(-1, "/tmp") cffi-0.8.2/demo/pyobj.py0000644000076400001440000000650712020145763015345 0ustar arigousers00000000000000 referents = [] # list "object descriptor -> python object" freelist = None def store(x): "Store the object 'x' and returns a new object descriptor for it." global freelist p = freelist if p is None: p = len(referents) referents.append(x) else: freelist = referents[p] referents[p] = x return p def discard(p): """Discard (i.e. close) the object descriptor 'p'. Return the original object that was attached to 'p'.""" global freelist x = referents[p] referents[p] = freelist freelist = p return x class Ref(object): """For use in 'with Ref(x) as ob': open an object descriptor and returns it in 'ob', and close it automatically when the 'with' statement finishes.""" def __init__(self, x): self.x = x def __enter__(self): self.p = p = store(self.x) return p def __exit__(self, *args): discard(self.p) def count_pyobj_alive(): result = len(referents) p = freelist while p is not None: assert result > 0 result -= 1 p = referents[p] return result # ------------------------------------------------------------ if __name__ == '__main__': import api ffi = api.PythonFFI() ffi.cdef(""" typedef int pyobj_t; int sum_integers(pyobj_t p_list); pyobj_t sum_objects(pyobj_t p_list, pyobj_t p_initial); """) @ffi.pyexport("int(pyobj_t)") def length(p_list): list = referents[p_list] return len(list) @ffi.pyexport("int(pyobj_t, int)") def getitem(p_list, index): list = referents[p_list] return list[index] @ffi.pyexport("pyobj_t(pyobj_t)") def pyobj_dup(p): return store(referents[p]) @ffi.pyexport("void(pyobj_t)") def pyobj_close(p): discard(p) @ffi.pyexport("pyobj_t(pyobj_t, int)") def pyobj_getitem(p_list, index): list = referents[p_list] return store(list[index]) @ffi.pyexport("pyobj_t(pyobj_t, pyobj_t)") def pyobj_add(p1, p2): return store(referents[p1] + referents[p2]) lib = ffi.verify(""" typedef int pyobj_t; /* an "object descriptor" number */ int sum_integers(pyobj_t p_list) { /* this a demo function written in C, using the API defined above: length() and getitem(). */ int i, result = 0; int count = length(p_list); for (i=0; i> sys.stderr, "skipping", path return for filename, smode in content: if stat.S_ISDIR(smode): clean(filename) if filename.endswith('/__pycache__'): try: os.rmdir(filename) except OSError: pass elif (filename.endswith('.pyc') or filename.endswith('.pyo') or filename.endswith('.pyc~') or filename.endswith('.pyo~')): os.unlink(filename) count += 1 count = 0 for arg in sys.argv[1:] or ['.']: print "cleaning path", arg, "of .pyc/.pyo/__pycache__ files" clean(arg) print "%d files removed" % (count,) cffi-0.8.2/demo/api.py0000644000076400001440000000315312020132771014760 0ustar arigousers00000000000000import cffi from cffi import FFI class PythonFFI(FFI): def __init__(self, backend=None): FFI.__init__(self, backend=backend) self._pyexports = {} def pyexport(self, signature): tp = self._typeof(signature, consider_function_as_funcptr=True) def decorator(func): name = func.__name__ if name in self._pyexports: raise cffi.CDefError("duplicate pyexport'ed function %r" % (name,)) callback_var = self.getctype(tp, name) self.cdef("%s;" % callback_var) self._pyexports[name] = _PyExport(tp, func) return decorator def verify(self, source='', **kwargs): extras = [] pyexports = sorted(self._pyexports.items()) for name, export in pyexports: callback_var = self.getctype(export.tp, name) extras.append("%s;" % callback_var) extras.append(source) source = '\n'.join(extras) lib = FFI.verify(self, source, **kwargs) for name, export in pyexports: cb = self.callback(export.tp, export.func) export.cb = cb setattr(lib, name, cb) return lib class _PyExport(object): def __init__(self, tp, func): self.tp = tp self.func = func if __name__ == '__main__': ffi = PythonFFI() @ffi.pyexport("int(int)") def add1(n): print n return n + 1 ffi.cdef(""" int f(int); """) lib = ffi.verify(""" int f(int x) { return add1(add1(x)); } """) assert lib.f(5) == 7 cffi-0.8.2/demo/readdir_ctypes.py0000644000076400001440000000337512005523376017226 0ustar arigousers00000000000000# A Linux-only demo # # For comparison purposes, this is a ctypes version of readdir.py. import sys import ctypes if not sys.platform.startswith('linux'): raise Exception("Linux-only demo") DIR_p = ctypes.c_void_p ino_t = ctypes.c_long off_t = ctypes.c_long class DIRENT(ctypes.Structure): _fields_ = [ ('d_ino', ino_t), # inode number ('d_off', off_t), # offset to the next dirent ('d_reclen', ctypes.c_ushort), # length of this record ('d_type', ctypes.c_ubyte), # type of file; not supported # by all file system types ('d_name', ctypes.c_char * 256), # filename ] DIRENT_p = ctypes.POINTER(DIRENT) DIRENT_pp = ctypes.POINTER(DIRENT_p) C = ctypes.CDLL(None) readdir_r = C.readdir_r readdir_r.argtypes = [DIR_p, DIRENT_p, DIRENT_pp] readdir_r.restype = ctypes.c_int openat = C.openat openat.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int] openat.restype = ctypes.c_int fdopendir = C.fdopendir fdopendir.argtypes = [ctypes.c_int] fdopendir.restype = DIR_p closedir = C.closedir closedir.argtypes = [DIR_p] closedir.restype = ctypes.c_int def walk(basefd, path): print '{', path dirfd = openat(basefd, path, 0) if dirfd < 0: # error in openat() return dir = fdopendir(dirfd) dirent = DIRENT() result = DIRENT_p() while True: if readdir_r(dir, dirent, result): # error in readdir_r() break if not result: break name = dirent.d_name print '%3d %s' % (dirent.d_type, name) if dirent.d_type == 4 and name != '.' and name != '..': walk(dirfd, name) closedir(dir) print '}' walk(-1, "/tmp") cffi-0.8.2/demo/btrfs-snap.py0000644000076400001440000000173712021350603016271 0ustar arigousers00000000000000""" btrfs-snap.py: source target newname creates a exactly named snapshots and bails out if they exist """ import argparse import fcntl import os import sys import cffi ffi = cffi.FFI() ffi.cdef(""" #define BTRFS_IOC_SNAP_CREATE_V2 ... struct btrfs_ioctl_vol_args_v2 { int64_t fd; char name[]; ...; }; """) v = ffi.verify("#include ") parser = argparse.ArgumentParser(usage=__doc__.strip()) parser.add_argument('source', help='source subvolume') parser.add_argument('target', help='target directory') parser.add_argument('newname', help='name of the new snapshot') opts = parser.parse_args() source = os.open(opts.source, os.O_DIRECTORY) target = os.open(opts.target, os.O_DIRECTORY) args = ffi.new('struct btrfs_ioctl_vol_args_v2 *') args.name = opts.newname args.fd = source args_buffer = ffi.buffer(args) try: fcntl.ioctl(target, v.BTRFS_IOC_SNAP_CREATE_V2, args_buffer) except IOError as e: print e sys.exit(1) cffi-0.8.2/demo/gmp.py0000644000076400001440000000164112176750270015006 0ustar arigousers00000000000000import sys import cffi # # This is only a demo based on the GMP library. # There is a rather more complete version available at: # http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files # ffi = cffi.FFI() ffi.cdef(""" typedef struct { ...; } MP_INT; typedef MP_INT mpz_t[1]; int mpz_init_set_str (MP_INT *dest_integer, char *src_cstring, int base); void mpz_add (MP_INT *sum, MP_INT *addend1, MP_INT *addend2); char * mpz_get_str (char *string, int base, MP_INT *integer); """) lib = ffi.verify("#include ", libraries=['gmp', 'm']) # ____________________________________________________________ a = ffi.new("mpz_t") b = ffi.new("mpz_t") lib.mpz_init_set_str(a, sys.argv[1], 10) # Assume decimal integers lib.mpz_init_set_str(b, sys.argv[2], 10) # Assume decimal integers lib.mpz_add(a, a, b) # a=a+b s = lib.mpz_get_str(ffi.NULL, 10, a) print ffi.string(s) cffi-0.8.2/demo/readdir.py0000644000076400001440000000277312027116223015631 0ustar arigousers00000000000000# A Linux-only demo # import sys from cffi import FFI if not sys.platform.startswith('linux'): raise Exception("Linux-only demo") ffi = FFI() ffi.cdef(""" typedef void DIR; typedef long ino_t; typedef long off_t; struct dirent { ino_t d_ino; /* inode number */ off_t d_off; /* offset to the next dirent */ unsigned short d_reclen; /* length of this record */ unsigned char d_type; /* type of file; not supported by all file system types */ char d_name[256]; /* filename */ }; int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); int openat(int dirfd, const char *pathname, int flags); DIR *fdopendir(int fd); int closedir(DIR *dirp); """) ffi.C = ffi.dlopen(None) def walk(basefd, path): print '{', path dirfd = ffi.C.openat(basefd, path, 0) if dirfd < 0: # error in openat() return dir = ffi.C.fdopendir(dirfd) dirent = ffi.new("struct dirent *") result = ffi.new("struct dirent **") while True: if ffi.C.readdir_r(dir, dirent, result): # error in readdir_r() break if result[0] == ffi.NULL: break name = ffi.string(dirent.d_name) print '%3d %s' % (dirent.d_type, name) if dirent.d_type == 4 and name != '.' and name != '..': walk(dirfd, name) ffi.C.closedir(dir) print '}' walk(-1, "/tmp") cffi-0.8.2/demo/setup.py0000644000076400001440000000054612012221446015351 0ustar arigousers00000000000000# # A minimal example of setup.py to install a Python module # together with the custom C extension module generated by CFFI. # from distutils.core import setup from distutils.extension import Extension import bsdopendirtype setup(name='bsdopendirtype', py_modules=['bsdopendirtype'], ext_modules=[bsdopendirtype.ffi.verifier.get_extension()]) cffi-0.8.2/demo/image.py0000644000076400001440000000031711766413124015302 0ustar arigousers00000000000000from cffi import FFI ffi = FFI() ffi.cdef(""" typedef struct { unsigned char r, g, b; } pixel_t; """) image = ffi.new("pixel_t[]", 800*600) image[0].r = 255 image[0].g = 192 image[0].b = 128 cffi-0.8.2/demo/sarvi.py0000644000076400001440000000353612023577643015356 0ustar arigousers00000000000000import cffi ffi = cffi.FFI() ffi.cdef(""" typedef ... *tdl_handle_t; typedef tdl_handle_t my_local_handle; typedef tdl_handle_t my_remote_handle; typedef struct my_error_code_ { int error; my_remote_handle *rh; } my_error_code_t; typedef int (*my_app_error_member_handler_t)( my_local_handle, my_remote_handle, my_remote_handle, int, void *); my_local_handle make_handle(void); // Just a dummy function that prints "Hello World and all the parameters" int my_iterate_error_code(my_local_handle lh, my_remote_handle rh, my_error_code_t *context, my_app_error_member_handler_t app_callback, void *app_context); """) lib = ffi.verify(''' typedef struct tdlhandle_s *tdl_handle_t; struct tdlhandle_s { int hello; char world[255]; void *ctx; }; typedef tdl_handle_t my_local_handle; typedef tdl_handle_t my_remote_handle; typedef struct my_error_code_ { int error; my_remote_handle *rh; } my_error_code_t; typedef int (*my_app_error_member_handler_t)( my_local_handle, my_remote_handle, my_remote_handle, int, void *); my_local_handle make_handle(void) { return (my_local_handle)malloc(sizeof(struct tdlhandle_s)); } // Just a dummy function that prints "Hello World and all the parameters" int my_iterate_error_code(my_local_handle lh, my_remote_handle rh, my_error_code_t *context, my_app_error_member_handler_t app_callback, void *app_context) { return 42; } ''') p1 = lib.make_handle() p2 = lib.make_handle() print p1 print lib.my_iterate_error_code(p1, p2, ffi.NULL, ffi.NULL, ffi.NULL) cffi-0.8.2/demo/pwuid.py0000644000076400001440000000051712006757534015356 0ustar arigousers00000000000000from cffi import FFI ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) C = ffi.verify(""" // passed to the real C compiler #include #include """) print ffi.string(C.getpwuid(0).pw_name) cffi-0.8.2/demo/syslog.py0000644000076400001440000000027612130515377015543 0ustar arigousers00000000000000import cffi XXXXXXXXXX ffi = cffi.FFI() ffi.cdef(""" """) __all__ = ALL_CONSTANTS + ( 'openlog', 'syslog', 'closelog', 'setlogmask', 'LOG_MASK', 'LOG_UPTO') del ALL_CONSTANTS cffi-0.8.2/demo/bsdopendirtype.py0000644000076400001440000000304012012004462017231 0ustar arigousers00000000000000from cffi import FFI ffi = FFI() ffi.cdef(""" typedef ... DIR; struct dirent { unsigned char d_type; /* type of file */ char d_name[]; /* filename */ ...; }; DIR *opendir(const char *name); int closedir(DIR *dirp); struct dirent *readdir(DIR *dirp); static const int DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK; """) lib = ffi.verify(""" #include #include """) def _posix_error(): raise OSError(ffi.errno, os.strerror(ffi.errno)) _dtype_to_smode = { lib.DT_BLK: 0o060000, lib.DT_CHR: 0o020000, lib.DT_DIR: 0o040000, lib.DT_FIFO: 0o010000, lib.DT_LNK: 0o120000, lib.DT_REG: 0o100000, lib.DT_SOCK: 0o140000, } def opendir(dir): if len(dir) == 0: dir = '.' dirname = dir if not dirname.endswith('/'): dirname += '/' dirp = lib.opendir(dir) if dirp == ffi.NULL: raise _posix_error() try: while True: ffi.errno = 0 dirent = lib.readdir(dirp) if dirent == ffi.NULL: if ffi.errno != 0: raise _posix_error() return name = ffi.string(dirent.d_name) if name == '.' or name == '..': continue name = dirname + name try: smode = _dtype_to_smode[dirent.d_type] except KeyError: smode = os.lstat(name).st_mode yield name, smode finally: lib.closedir(dirp) cffi-0.8.2/demo/_curses.py0000644000076400001440000001472212176750270015672 0ustar arigousers00000000000000"""Very partial replacement of the standard extension module '_curses'. Just contains the minimal amount of stuff to make one of my curses programs run. XXX should also check for and report errors. """ from cffi import FFI ffi = FFI() ffi.cdef(""" typedef ... WINDOW; typedef unsigned char bool; typedef unsigned long chtype; static const int ERR, OK; WINDOW *initscr(void); int endwin(void); bool isendwin(void); const char *keyname(int c); static const int KEY_MIN, KEY_MAX; int setupterm(char *term, int fildes, int *errret); int tigetflag(char *); int tigetnum(char *); char *tigetstr(char *); char *tparm (const char *, ...); int cbreak(void); int nocbreak(void); int echo(void); int noecho(void); int keypad(WINDOW *win, bool bf); int notimeout(WINDOW *win, bool bf); void wtimeout(WINDOW *win, int delay); int def_prog_mode(void); int def_shell_mode(void); int reset_prog_mode(void); int reset_shell_mode(void); int resetty(void); int savetty(void); void getsyx(int y, int x); void setsyx(int y, int x); //int ripoffline(int line, int (*init)(WINDOW *, int)); int curs_set(int visibility); int napms(int ms); int start_color(void); int init_pair(short pair, short f, short b); int init_color(short color, short r, short g, short b); bool has_colors(void); bool can_change_color(void); int color_content(short color, short *r, short *g, short *b); int pair_content(short pair, short *f, short *b); int use_default_colors(void); static const int COLOR_BLACK; static const int COLOR_RED; static const int COLOR_GREEN; static const int COLOR_YELLOW; static const int COLOR_BLUE; static const int COLOR_MAGENTA; static const int COLOR_CYAN; static const int COLOR_WHITE; static const int A_ATTRIBUTES; static const int A_NORMAL; static const int A_STANDOUT; static const int A_UNDERLINE; static const int A_REVERSE; static const int A_BLINK; static const int A_DIM; static const int A_BOLD; static const int A_ALTCHARSET; static const int A_INVIS; static const int A_PROTECT; static const int A_CHARTEXT; static const int A_COLOR; int COLORS, COLOR_PAIRS; void _m_getyx(WINDOW *win, int yx[2]); void _m_getparyx(WINDOW *win, int yx[2]); void _m_getbegyx(WINDOW *win, int yx[2]); void _m_getmaxyx(WINDOW *win, int yx[2]); int wclear(WINDOW *win); int wclrtoeol(WINDOW *win); int wmove(WINDOW *win, int y, int x); int waddstr(WINDOW *win, const char *str); int mvwaddstr(WINDOW *win, int y, int x, const char *str); void wbkgdset(WINDOW *win, chtype ch); int wrefresh(WINDOW *win); int wgetch(WINDOW *win); int getattrs(WINDOW *win); int wattrset(WINDOW *win, int attrs); """) lib = ffi.verify(""" #include #include void _m_getyx(WINDOW *win, int yx[2]) { getyx(win, yx[0], yx[1]); } void _m_getparyx(WINDOW *win, int yx[2]) { getparyx(win, yx[0], yx[1]); } void _m_getbegyx(WINDOW *win, int yx[2]) { getbegyx(win, yx[0], yx[1]); } void _m_getmaxyx(WINDOW *win, int yx[2]) { getmaxyx(win, yx[0], yx[1]); } """, libraries=['ncurses']) def _setup(): globals().update(lib.__dict__) for key in range(KEY_MIN, KEY_MAX): key_n = keyname(key) if key_n == ffi.NULL or ffi.string(key_n) == "UNKNOWN KEY": continue key_n = ffi.string(key_n).replace('(', '').replace(')', '') globals()[key_n] = key _setup() # ____________________________________________________________ class error(Exception): pass class Window(object): def __init__(self): self._window = lib.initscr() def getyx(self): yx = ffi.new("int[2]") lib._m_getyx(self._window, yx) return tuple(yx) def getparyx(self): yx = ffi.new("int[2]") lib._m_getparyx(self._window, yx) return tuple(yx) def getbegyx(self): yx = ffi.new("int[2]") lib._m_getbegyx(self._window, yx) return tuple(yx) def getmaxyx(self): yx = ffi.new("int[2]") lib._m_getmaxyx(self._window, yx) return tuple(yx) def addstr(self, *args): y = None attr = None if len(args) == 1: text, = args elif len(args) == 2: text, attr = args elif len(args) == 3: y, x, text = args elif len(args) == 4: y, x, text, attr = args else: raise TypeError("addstr requires 1 to 4 arguments") if attr is not None: attr_old = getattrs(self._window) wattrset(self._window, attr) if y is not None: mvwaddstr(self._window, y, x, text) else: waddstr(self._window, text) if attr is not None: wattrset(self._window, attr_old) def bkgdset(self, bkgd, attr=A_NORMAL): if isinstance(bkgd, str): bkgd = ord(bkgd) wbkgdset(self._window, bkgd | attr) def _make_method(cname): method = getattr(lib, cname) def _execute(self, *args): return method(self._window, *args) return _execute keypad = _make_method('keypad') clear = _make_method('wclear') clrtoeol = _make_method('wclrtoeol') move = _make_method('wmove') refresh = _make_method('wrefresh') getch = _make_method('wgetch') notimeout = _make_method('notimeout') timeout = _make_method('wtimeout') del _make_method initscr = Window _setupterm_called = False def _ensure_setupterm_called(): if not _setupterm_called: raise error("must call (at least) setupterm() first") def setupterm(term=None, fd=-1): if term is None: term = ffi.NULL if fd < 0: import sys fd = sys.stdout.fileno() err = ffi.new("int *") if lib.setupterm(term, fd, err) == ERR: if err[0] == 0: s = "setupterm: could not find terminal" elif err[0] == 1: s = "setupterm: could not find terminfo database" else: s = "setupterm: unknown error %d" % err[0] raise error(s) global _setupterm_called _setupterm_called = True def tigetflag(capname): _ensure_setupterm_called() return lib.tigetflag(capname) def tigetnum(capname): _ensure_setupterm_called() return lib.tigetnum(capname) def tigetstr(capname): _ensure_setupterm_called() out = lib.tigetstr(capname) if out == ffi.NULL: return None return ffi.string(out) def tparm(name, *args): _ensure_setupterm_called() cargs = [ffi.cast("long", arg) for arg in args] return ffi.string(lib.tparm(name, *cargs)) def color_pair(n): return n << 8 cffi-0.8.2/demo/fastcsv.py0000644000076400001440000002006712027413115015664 0ustar arigousers00000000000000import csv import cffi # IN-PROGRESS. See the demo at the end of the file dialect2ffi = {} def _make_ffi_from_dialect(dialect): ffi = cffi.FFI() ffi.cdef(""" long parse_line(char *rawline, long inputlength); """) d = {'quotechar': ord(dialect.quotechar), 'quoting': int(dialect.quoting), 'skipinitialspace': int(dialect.skipinitialspace), 'delimiter': ord(dialect.delimiter), 'doublequote': int(dialect.doublequote), 'strict': int(dialect.strict), } if dialect.escapechar is not None: d['is_escape_char'] = '== %d' % ord(dialect.escapechar) else: d['is_escape_char'] = '&& 0' lib = ffi.verify(r''' typedef enum { START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, EAT_CRNL } ParserState; typedef enum { QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE } QuoteStyle; typedef struct { ParserState state; /* current CSV parse state */ char *field; /* build current field in here */ int field_size; /* size of allocated buffer */ int field_len; /* length of current field */ int numeric_field; /* treat field as numeric */ } ReaderObj; static void parse_add_char(ReaderObj *self, char c) { *self->field++ = c; } static void parse_save_field(ReaderObj *self) { *self->field++ = 0; } static int parse_process_char(ReaderObj *self, char c) { switch (self->state) { case START_RECORD: /* start of record */ if (c == '\0') /* empty line - return [] */ break; else if (c == '\n' || c == '\r') { self->state = EAT_CRNL; break; } /* normal character - handle as START_FIELD */ self->state = START_FIELD; /* fallthru */ case START_FIELD: /* expecting field */ if (c == '\n' || c == '\r' || c == '\0') { /* save empty field - return [fields] */ parse_save_field(self); self->state = (c == '\0' ? START_RECORD : EAT_CRNL); } else if (c == %(quotechar)d && %(quoting)d != QUOTE_NONE) { /* start quoted field */ self->state = IN_QUOTED_FIELD; } else if (c %(is_escape_char)s) { /* possible escaped character */ self->state = ESCAPED_CHAR; } else if (c == ' ' && %(skipinitialspace)d) /* ignore space at start of field */ ; else if (c == %(delimiter)d) { /* save empty field */ parse_save_field(self); } else { /* begin new unquoted field */ if (%(quoting)d == QUOTE_NONNUMERIC) self->numeric_field = 1; parse_add_char(self, c); self->state = IN_FIELD; } break; case ESCAPED_CHAR: if (c == '\0') c = '\n'; parse_add_char(self, c); self->state = IN_FIELD; break; case IN_FIELD: /* in unquoted field */ if (c == '\n' || c == '\r' || c == '\0') { /* end of line - return [fields] */ parse_save_field(self); self->state = (c == '\0' ? START_RECORD : EAT_CRNL); } else if (c %(is_escape_char)s) { /* possible escaped character */ self->state = ESCAPED_CHAR; } else if (c == %(delimiter)d) { /* save field - wait for new field */ parse_save_field(self); self->state = START_FIELD; } else { /* normal character - save in field */ parse_add_char(self, c); } break; case IN_QUOTED_FIELD: /* in quoted field */ if (c == '\0') ; else if (c %(is_escape_char)s) { /* Possible escape character */ self->state = ESCAPE_IN_QUOTED_FIELD; } else if (c == %(quotechar)d && %(quoting)d != QUOTE_NONE) { if (%(doublequote)d) { /* doublequote; " represented by "" */ self->state = QUOTE_IN_QUOTED_FIELD; } else { /* end of quote part of field */ self->state = IN_FIELD; } } else { /* normal character - save in field */ parse_add_char(self, c); } break; case ESCAPE_IN_QUOTED_FIELD: if (c == '\0') c = '\n'; parse_add_char(self, c); self->state = IN_QUOTED_FIELD; break; case QUOTE_IN_QUOTED_FIELD: /* doublequote - seen a quote in an quoted field */ if (%(quoting)d != QUOTE_NONE && c == %(quotechar)d) { /* save "" as " */ parse_add_char(self, c); self->state = IN_QUOTED_FIELD; } else if (c == %(delimiter)d) { /* save field - wait for new field */ parse_save_field(self); self->state = START_FIELD; } else if (c == '\n' || c == '\r' || c == '\0') { /* end of line - return [fields] */ parse_save_field(self); self->state = (c == '\0' ? START_RECORD : EAT_CRNL); } else if (!%(strict)d) { parse_add_char(self, c); self->state = IN_FIELD; } else { /* illegal */ /*PyErr_Format(error_obj, "'%%c' expected after '%%c'", dialect->delimiter, dialect->quotechar);*/ return -1; } break; case EAT_CRNL: if (c == '\n' || c == '\r') ; else if (c == '\0') self->state = START_RECORD; else { /*PyErr_Format(error_obj, "new-line character seen in unquoted field - do you need to open the file in universal-newline mode?");*/ return -1; } break; } return 0; } static void parse_reset(ReaderObj *self, char *rawline) { self->field = rawline; self->state = START_RECORD; self->numeric_field = 0; } long parse_line(char *rawline, long inputlength) { char *p; ReaderObj reader; parse_reset(&reader, rawline); for (p=rawline; inputlength > 0; inputlength--, p++) { if (parse_process_char(&reader, *p) < 0) return -1; } if (parse_process_char(&reader, 0) < 0) return -1; return reader.field - rawline - 1; } ''' % d) return ffi, lib def fastcsv_reader(f, dialect): dialect = csv.get_dialect(dialect) try: ffi, lib = dialect2ffi[dialect] except KeyError: ffi, lib = dialect2ffi[dialect] = _make_ffi_from_dialect(dialect) # linelen = -1 for line in f: if linelen <= len(line): linelen = 2 * len(line) rawline = ffi.new("char[]", linelen) ffi.buffer(rawline, len(line))[:] = line n = lib.parse_line(rawline, len(line)) assert n >= 0 yield ffi.buffer(rawline, n)[:].split('\x00') if __name__ == '__main__': csv.register_dialect('unixpwd', delimiter=':', quoting=csv.QUOTE_NONE) with open('/etc/passwd', 'rb') as f: reader = fastcsv_reader(f, 'unixpwd') for row in reader: print row cffi-0.8.2/demo/cffi-cocoa.py0000644000076400001440000000643212004743070016205 0ustar arigousers00000000000000# Based on http://cocoawithlove.com/2010/09/minimalist-cocoa-programming.html # by Juraj Sukop. This demo was eventually expanded into a more complete # Cocoa library available at https://bitbucket.org/sukop/nspython . from cffi import FFI ffi = FFI() ffi.cdef(''' typedef signed char BOOL; typedef long NSInteger; typedef unsigned long NSUInteger; typedef NSInteger NSApplicationActivationPolicy; typedef NSUInteger NSBackingStoreType; typedef NSUInteger NSStringEncoding; typedef double CGFloat; struct CGPoint { CGFloat x; CGFloat y; }; typedef struct CGPoint CGPoint; struct CGSize { CGFloat width; CGFloat height; }; typedef struct CGSize CGSize; struct CGRect { CGPoint origin; CGSize size; }; typedef struct CGRect CGRect; typedef CGPoint NSPoint; typedef CGSize NSSize; typedef CGRect NSRect; typedef struct objc_class *Class; typedef struct objc_object { Class isa; } *id; typedef struct objc_selector *SEL; SEL sel_registerName(const char *str); id objc_getClass(const char *name); id objc_msgSend(id theReceiver, SEL theSelector, ...); ''') objc = ffi.dlopen('objc') appkit = ffi.dlopen('AppKit') nil = ffi.NULL YES = ffi.cast('BOOL', 1) NO = ffi.cast('BOOL', 0) NSASCIIStringEncoding = ffi.cast('NSStringEncoding', 1) NSApplicationActivationPolicyRegular = ffi.cast('NSApplicationActivationPolicy', 0) NSTitledWindowMask = ffi.cast('NSUInteger', 1) NSBackingStoreBuffered = ffi.cast('NSBackingStoreType', 2) NSMakePoint = lambda x, y: ffi.new('NSPoint *', (x, y))[0] NSMakeRect = lambda x, y, w, h: ffi.new('NSRect *', ((x, y), (w, h)))[0] get, send, sel = objc.objc_getClass, objc.objc_msgSend, objc.sel_registerName at = lambda s: send( get('NSString'), sel('stringWithCString:encoding:'), ffi.new('char[]', s), NSASCIIStringEncoding) send(get('NSAutoreleasePool'), sel('new')) app = send(get('NSApplication'), sel('sharedApplication')) send(app, sel('setActivationPolicy:'), NSApplicationActivationPolicyRegular) menubar = send(send(get('NSMenu'), sel('new')), sel('autorelease')) appMenuItem = send(send(get('NSMenuItem'), sel('new')), sel('autorelease')) send(menubar, sel('addItem:'), appMenuItem) send(app, sel('setMainMenu:'), menubar) appMenu = send(send(get('NSMenu'), sel('new')), sel('autorelease')) appName = send(send(get('NSProcessInfo'), sel('processInfo')), sel('processName')) quitTitle = send(at('Quit '), sel('stringByAppendingString:'), appName) quitMenuItem = send(send(send( get('NSMenuItem'), sel('alloc')), sel('initWithTitle:action:keyEquivalent:'), quitTitle, sel('terminate:'), at('q')), sel('autorelease')) send(appMenu, sel('addItem:'), quitMenuItem) send(appMenuItem, sel('setSubmenu:'), appMenu) window = send(send(send( get('NSWindow'), sel('alloc')), sel('initWithContentRect:styleMask:backing:defer:'), NSMakeRect(0, 0, 200, 200), NSTitledWindowMask, NSBackingStoreBuffered, NO), sel('autorelease')) send(window, sel('cascadeTopLeftFromPoint:'), NSMakePoint(20, 20)) send(window, sel('setTitle:'), appName) send(window, sel('makeKeyAndOrderFront:'), nil) send(app, sel('activateIgnoringOtherApps:'), YES) send(app, sel('run')) cffi-0.8.2/demo/winclipboard.py0000644000076400001440000000273211771074220016674 0ustar arigousers00000000000000__author__ = "Israel Fruchter " from cffi import FFI ffi = FFI() ffi.cdef(''' typedef void * HANDLE; typedef HANDLE HWND; typedef int BOOL; typedef unsigned int UINT; typedef int SIZE_T; typedef char * LPTSTR; typedef HANDLE HGLOBAL; typedef HANDLE LPVOID; HWND GetConsoleWindow(void); LPVOID GlobalLock( HGLOBAL hMem ); BOOL GlobalUnlock( HGLOBAL hMem ); HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes); BOOL OpenClipboard(HWND hWndNewOwner); BOOL CloseClipboard(void); BOOL EmptyClipboard(void); HANDLE SetClipboardData(UINT uFormat, HANDLE hMem); #define CF_TEXT ... #define GMEM_MOVEABLE ... void * memcpy(void * s1, void * s2, int n); ''') lib = ffi.verify(''' #include ''', libraries=["user32"]) globals().update(lib.__dict__) def CopyToClipboard(string): ''' use win32 api to copy `string` to the clipboard ''' hWnd = GetConsoleWindow() if OpenClipboard(hWnd): cstring = ffi.new("char[]", string) size = ffi.sizeof(cstring) # make it a moveable memory for other processes hGlobal = GlobalAlloc(GMEM_MOVEABLE, size) buffer = GlobalLock(hGlobal) memcpy(buffer, cstring, size) GlobalUnlock(hGlobal) res = EmptyClipboard() res = SetClipboardData(CF_TEXT, buffer) CloseClipboard() CopyToClipboard("hello world from cffi") cffi-0.8.2/demo/y.py0000644000076400001440000000006412030625061014455 0ustar arigousers00000000000000import cffi ffi = cffi.FFI() ffi.new("char[]", 4) cffi-0.8.2/demo/ui.py0000755000076400001440000000014311767401333014635 0ustar arigousers00000000000000#!/usr/bin/env python import os import curses os.chdir('/home/arigo/Music/x/') execfile('ui.py') cffi-0.8.2/demo/xclient.py0000644000076400001440000000213212004743246015660 0ustar arigousers00000000000000from cffi import FFI ffi = FFI() ffi.cdef(""" typedef ... Display; typedef struct { ...; } Window; typedef struct { int type; ...; } XEvent; Display *XOpenDisplay(char *display_name); Window DefaultRootWindow(Display *display); int XMapRaised(Display *display, Window w); Window XCreateSimpleWindow(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, unsigned long border, unsigned long background); int XNextEvent(Display *display, XEvent *event_return); """) lib = ffi.verify(""" #include """, libraries=['X11']) globals().update(lib.__dict__) class XError(Exception): pass def main(): display = XOpenDisplay(ffi.NULL) if display == ffi.NULL: raise XError("cannot open display") w = XCreateSimpleWindow(display, DefaultRootWindow(display), 10, 10, 500, 350, 0, 0, 0) XMapRaised(display, w) event = ffi.new("XEvent *") XNextEvent(display, event) if __name__ == '__main__': main() cffi-0.8.2/cffi/0000755000076400001440000000000012306266401013623 5ustar arigousers00000000000000cffi-0.8.2/cffi/verifier.py0000644000076400001440000002106412237455272016024 0ustar arigousers00000000000000import sys, os, binascii, imp, shutil from . import __version__ from . import ffiplatform class Verifier(object): def __init__(self, ffi, preamble, tmpdir=None, modulename=None, ext_package=None, tag='', force_generic_engine=False, **kwds): self.ffi = ffi self.preamble = preamble if not modulename: flattened_kwds = ffiplatform.flatten(kwds) vengine_class = _locate_engine_class(ffi, force_generic_engine) self._vengine = vengine_class(self) self._vengine.patch_extension_kwds(kwds) self.kwds = kwds # if modulename: if tag: raise TypeError("can't specify both 'modulename' and 'tag'") else: key = '\x00'.join([sys.version[:3], __version__, preamble, flattened_kwds] + ffi._cdefsources) if sys.version_info >= (3,): key = key.encode('utf-8') k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) k1 = k1.lstrip('0x').rstrip('L') k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) k2 = k2.lstrip('0').rstrip('L') modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key, k1, k2) suffix = _get_so_suffixes()[0] self.tmpdir = tmpdir or _caller_dir_pycache() self.sourcefilename = os.path.join(self.tmpdir, modulename + '.c') self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) self.ext_package = ext_package self._has_source = False self._has_module = False def write_source(self, file=None): """Write the C source code. It is produced in 'self.sourcefilename', which can be tweaked beforehand.""" with self.ffi._lock: if self._has_source and file is None: raise ffiplatform.VerificationError( "source code already written") self._write_source(file) def compile_module(self): """Write the C source code (if not done already) and compile it. This produces a dynamic link library in 'self.modulefilename'.""" with self.ffi._lock: if self._has_module: raise ffiplatform.VerificationError("module already compiled") if not self._has_source: self._write_source() self._compile_module() def load_library(self): """Get a C module from this Verifier instance. Returns an instance of a FFILibrary class that behaves like the objects returned by ffi.dlopen(), but that delegates all operations to the C module. If necessary, the C code is written and compiled first. """ with self.ffi._lock: if not self._has_module: self._locate_module() if not self._has_module: if not self._has_source: self._write_source() self._compile_module() return self._load_library() def get_module_name(self): basename = os.path.basename(self.modulefilename) # kill both the .so extension and the other .'s, as introduced # by Python 3: 'basename.cpython-33m.so' basename = basename.split('.', 1)[0] # and the _d added in Python 2 debug builds --- but try to be # conservative and not kill a legitimate _d if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): basename = basename[:-2] return basename def get_extension(self): if not self._has_source: with self.ffi._lock: if not self._has_source: self._write_source() sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) modname = self.get_module_name() return ffiplatform.get_extension(sourcename, modname, **self.kwds) def generates_python_module(self): return self._vengine._gen_python_module # ---------- def _locate_module(self): if not os.path.isfile(self.modulefilename): if self.ext_package: try: pkg = __import__(self.ext_package, None, None, ['__doc__']) except ImportError: return # cannot import the package itself, give up # (e.g. it might be called differently before installation) path = pkg.__path__ else: path = None filename = self._vengine.find_module(self.get_module_name(), path, _get_so_suffixes()) if filename is None: return self.modulefilename = filename self._vengine.collect_types() self._has_module = True def _write_source(self, file=None): must_close = (file is None) if must_close: _ensure_dir(self.sourcefilename) file = open(self.sourcefilename, 'w') self._vengine._f = file try: self._vengine.write_source_to_f() finally: del self._vengine._f if must_close: file.close() if must_close: self._has_source = True def _compile_module(self): # compile this C source tmpdir = os.path.dirname(self.sourcefilename) outputfilename = ffiplatform.compile(tmpdir, self.get_extension()) try: same = ffiplatform.samefile(outputfilename, self.modulefilename) except OSError: same = False if not same: _ensure_dir(self.modulefilename) shutil.move(outputfilename, self.modulefilename) self._has_module = True def _load_library(self): assert self._has_module return self._vengine.load_library() # ____________________________________________________________ _FORCE_GENERIC_ENGINE = False # for tests def _locate_engine_class(ffi, force_generic_engine): if _FORCE_GENERIC_ENGINE: force_generic_engine = True if not force_generic_engine: if '__pypy__' in sys.builtin_module_names: force_generic_engine = True else: try: import _cffi_backend except ImportError: _cffi_backend = '?' if ffi._backend is not _cffi_backend: force_generic_engine = True if force_generic_engine: from . import vengine_gen return vengine_gen.VGenericEngine else: from . import vengine_cpy return vengine_cpy.VCPythonEngine # ____________________________________________________________ _TMPDIR = None def _caller_dir_pycache(): if _TMPDIR: return _TMPDIR filename = sys._getframe(2).f_code.co_filename return os.path.abspath(os.path.join(os.path.dirname(filename), '__pycache__')) def set_tmpdir(dirname): """Set the temporary directory to use instead of __pycache__.""" global _TMPDIR _TMPDIR = dirname def cleanup_tmpdir(tmpdir=None, keep_so=False): """Clean up the temporary directory by removing all files in it called `_cffi_*.{c,so}` as well as the `build` subdirectory.""" tmpdir = tmpdir or _caller_dir_pycache() try: filelist = os.listdir(tmpdir) except OSError: return if keep_so: suffix = '.c' # only remove .c files else: suffix = _get_so_suffixes()[0].lower() for fn in filelist: if fn.lower().startswith('_cffi_') and ( fn.lower().endswith(suffix) or fn.lower().endswith('.c')): try: os.unlink(os.path.join(tmpdir, fn)) except OSError: pass clean_dir = [os.path.join(tmpdir, 'build')] for dir in clean_dir: try: for fn in os.listdir(dir): fn = os.path.join(dir, fn) if os.path.isdir(fn): clean_dir.append(fn) else: os.unlink(fn) except OSError: pass def _get_so_suffixes(): suffixes = [] for suffix, mode, type in imp.get_suffixes(): if type == imp.C_EXTENSION: suffixes.append(suffix) if not suffixes: # bah, no C_EXTENSION available. Occurs on pypy without cpyext if sys.platform == 'win32': suffixes = [".pyd"] else: suffixes = [".so"] return suffixes def _ensure_dir(filename): try: os.makedirs(os.path.dirname(filename)) except OSError: pass cffi-0.8.2/cffi/cparser.py0000644000076400001440000005551212306266214015646 0ustar arigousers00000000000000 from . import api, model from .commontypes import COMMON_TYPES, resolve_common_type try: from . import _pycparser as pycparser except ImportError: import pycparser import weakref, re, sys try: if sys.version_info < (3,): import thread as _thread else: import _thread lock = _thread.allocate_lock() except ImportError: lock = None _r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE) _r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)\s+(.*?)$", re.MULTILINE) _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(): global _parser_cache if _parser_cache is None: _parser_cache = pycparser.CParser() return _parser_cache def _preprocess(csource): # Remove comments. NOTE: this only work because the cdef() section # should not contain any string literal! csource = _r_comment.sub(' ', csource) # Remove the "#define FOO x" lines macros = {} for match in _r_define.finditer(csource): macroname, macrovalue = match.groups() macros[macroname] = macrovalue csource = _r_define.sub('', csource) # Replace "[...]" with "[__dotdotdotarray__]" csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) # Replace "...}" with "__dotdotdotNUM__}". This construction should # occur only at the end of enums; at the end of structs we have "...;}" # and at the end of vararg functions "...);". Also replace "=...[,}]" # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when # giving an unknown value. matches = list(_r_partial_enum.finditer(csource)) for number, match in enumerate(reversed(matches)): p = match.start() if csource[p] == '=': p2 = csource.find('...', p, match.end()) assert p2 > p csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number, csource[p2+3:]) else: assert csource[p:p+3] == '...' csource = '%s __dotdotdot%d__ %s' % (csource[:p], number, csource[p+3:]) # Replace all remaining "..." with the same name, "__dotdotdot__", # 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): def __init__(self): self._declarations = {} self._anonymous_counter = 0 self._structnode2type = weakref.WeakKeyDictionary() self._override = False self._packed = 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) ctn = _common_type_names(csource) typenames = [] for name in sorted(self._declarations): if name.startswith('typedef '): 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__;') csourcelines.append(csource) csource = '\n'.join(csourcelines) if lock is not None: lock.acquire() # pycparser is not thread-safe... try: ast = _get_parser().parse(csource) except pycparser.c_parser.ParseError as e: self.convert_pycparser_error(e, csource) finally: if lock is not None: lock.release() return ast, macros def convert_pycparser_error(self, e, csource): # xxx look for ":NUM:" at the start of str(e) and try to interpret # it as a line number line = None msg = str(e) if msg.startswith(':') and ':' in msg[1:]: linenum = msg[1:msg.find(':',1)] if linenum.isdigit(): linenum = int(linenum, 10) csourcelines = csource.splitlines() if 1 <= linenum <= len(csourcelines): line = csourcelines[linenum-1] if line: msg = 'cannot parse "%s"\n%s' % (line.strip(), msg) else: msg = 'parse error\n%s' % (msg,) raise api.CDefError(msg) def parse(self, csource, override=False, packed=False): prev_override = self._override prev_packed = self._packed try: self._override = override self._packed = packed self._internal_parse(csource) finally: self._override = prev_override self._packed = prev_packed def _internal_parse(self, csource): ast, macros = self._parse(csource) # add the macros for key, value in macros.items(): value = value.strip() if value != '...': raise api.CDefError('only supports the syntax "#define ' '%s ..." for now (literally)' % key) self._declare('macro ' + key, value) # find the first "__dotdotdot__" and use that as a separator # between the repeated typedefs and the real csource iterator = iter(ast.ext) for decl in iterator: if decl.name == '__dotdotdot__': break # for decl in iterator: if isinstance(decl, pycparser.c_ast.Decl): self._parse_decl(decl) elif isinstance(decl, pycparser.c_ast.Typedef): if not decl.name: raise api.CDefError("typedef does not declare any name", decl) if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and decl.type.type.names == ['__dotdotdot__']): realtype = model.unknown_type(decl.name) elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and isinstance(decl.type.type.type, pycparser.c_ast.IdentifierType) and decl.type.type.type.names == ['__dotdotdot__']): realtype = model.unknown_ptr_type(decl.name) else: realtype = self._get_type(decl.type, name=decl.name) self._declare('typedef ' + decl.name, realtype) else: raise api.CDefError("unrecognized construct", decl) def _parse_decl(self, decl): node = decl.type if isinstance(node, pycparser.c_ast.FuncDecl): tp = self._get_type(node, name=decl.name) assert isinstance(tp, model.RawFunctionType) tp = self._get_type_pointer(tp) self._declare('function ' + decl.name, tp) else: 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_union_enum_type('struct', node) elif isinstance(node, pycparser.c_ast.Union): if node.decls is not None: self._get_struct_union_enum_type('union', node) elif isinstance(node, pycparser.c_ast.Enum): if node.values is not None: self._get_struct_union_enum_type('enum', node) elif not decl.name: raise api.CDefError("construct does not declare any variable", decl) # if decl.name: tp = self._get_type(node, partial_length_ok=True) if self._is_constant_globalvar(node): self._declare('constant ' + decl.name, tp) else: self._declare('variable ' + decl.name, tp) def parse_type(self, cdecl): ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl) assert not macros exprnode = ast.ext[-1].type.args.params[0] if isinstance(exprnode, pycparser.c_ast.ID): raise api.CDefError("unknown identifier '%s'" % (exprnode.name,)) return self._get_type(exprnode.type) def _declare(self, name, obj): if name in self._declarations: if self._declarations[name] is obj: return if not self._override: raise api.FFIError( "multiple declarations of %s (for interactive usage, " "try cdef(xx, override=True))" % (name,)) assert '__dotdotdot__' not in name.split() self._declarations[name] = obj def _get_type_pointer(self, type, const=False): if isinstance(type, model.RawFunctionType): return type.as_function_pointer() if const: return model.ConstPointerType(type) return model.PointerType(type) def _get_type(self, typenode, name=None, partial_length_ok=False): # first, dereference typedefs, if we have it already parsed, we're good if (isinstance(typenode, pycparser.c_ast.TypeDecl) and isinstance(typenode.type, pycparser.c_ast.IdentifierType) and len(typenode.type.names) == 1 and ('typedef ' + typenode.type.names[0]) in self._declarations): type = self._declarations['typedef ' + typenode.type.names[0]] return type # if isinstance(typenode, pycparser.c_ast.ArrayDecl): # array type if typenode.dim is None: length = None else: length = self._parse_constant( typenode.dim, partial_length_ok=partial_length_ok) return model.ArrayType(self._get_type(typenode.type), length) # if isinstance(typenode, pycparser.c_ast.PtrDecl): # pointer type const = (isinstance(typenode.type, pycparser.c_ast.TypeDecl) and 'const' in typenode.type.quals) return self._get_type_pointer(self._get_type(typenode.type), const) # if isinstance(typenode, pycparser.c_ast.TypeDecl): type = typenode.type if isinstance(type, pycparser.c_ast.IdentifierType): # assume a primitive type. get it from .names, but reduce # synonyms to a single chosen combination names = list(type.names) if names != ['signed', 'char']: # keep this unmodified prefixes = {} while names: name = names[0] if name in ('short', 'long', 'signed', 'unsigned'): prefixes[name] = prefixes.get(name, 0) + 1 del names[0] else: break # ignore the 'signed' prefix below, and reorder the others newnames = [] for prefix in ('unsigned', 'short', 'long'): for i in range(prefixes.get(prefix, 0)): newnames.append(prefix) if not names: names = ['int'] # implicitly if names == ['int']: # but kill it if 'short' or 'long' if 'short' in prefixes or 'long' in prefixes: names = [] names = newnames + names ident = ' '.join(names) if ident == 'void': return model.void_type if ident == '__dotdotdot__': raise api.FFIError('bad usage of "..."') return resolve_common_type(ident) # if isinstance(type, pycparser.c_ast.Struct): # 'struct foobar' return self._get_struct_union_enum_type('struct', type, name) # if isinstance(type, pycparser.c_ast.Union): # 'union foobar' return self._get_struct_union_enum_type('union', type, name) # if isinstance(type, pycparser.c_ast.Enum): # 'enum foobar' return self._get_struct_union_enum_type('enum', type, name) # if isinstance(typenode, pycparser.c_ast.FuncDecl): # a function type return self._parse_function_type(typenode, name) # # nested anonymous structs or unions end up here if isinstance(typenode, pycparser.c_ast.Struct): return self._get_struct_union_enum_type('struct', typenode, name, nested=True) if isinstance(typenode, pycparser.c_ast.Union): return self._get_struct_union_enum_type('union', typenode, name, nested=True) # raise api.FFIError("bad or unsupported type declaration") def _parse_function_type(self, typenode, funcname=None): params = list(getattr(typenode.args, 'params', [])) ellipsis = ( len(params) > 0 and isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and isinstance(params[-1].type.type, pycparser.c_ast.IdentifierType) and params[-1].type.type.names == ['__dotdotdot__']) if ellipsis: params.pop() if not params: raise api.CDefError( "%s: a function with only '(...)' as argument" " is not correct C" % (funcname or 'in expression')) elif (len(params) == 1 and isinstance(params[0].type, pycparser.c_ast.TypeDecl) and isinstance(params[0].type.type, pycparser.c_ast.IdentifierType) and list(params[0].type.type.names) == ['void']): del params[0] args = [self._as_func_arg(self._get_type(argdeclnode.type)) for argdeclnode in params] result = self._get_type(typenode.type) return model.RawFunctionType(tuple(args), result, ellipsis) def _as_func_arg(self, type): if isinstance(type, model.ArrayType): return model.PointerType(type.item) elif isinstance(type, model.RawFunctionType): return type.as_function_pointer() else: return type def _is_constant_globalvar(self, typenode): if isinstance(typenode, pycparser.c_ast.PtrDecl): return 'const' in typenode.quals if isinstance(typenode, pycparser.c_ast.TypeDecl): return 'const' in typenode.quals return False def _get_struct_union_enum_type(self, kind, type, name=None, nested=False): # 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 # an AST that is not a tree, but a DAG, with the "type" node of the # two branches foo_t and foo_p of the trees being the same node. # It's a bit silly but detecting "DAG-ness" in the AST tree seems # to be the only way to distinguish this case from two independent # structs. See test_struct_with_two_usages. try: return self._structnode2type[type] except KeyError: pass # # Note that this must handle parsing "struct foo" any number of # times and always return the same StructType object. Additionally, # one of these times (not necessarily the first), the fields of # the struct can be specified with "struct foo { ...fields... }". # If no name is given, then we have to create a new anonymous struct # with no caching; in this case, the fields are either specified # right now or never. # force_name = name name = type.name # # get the type or create it if needed if name is None: # 'force_name' is used to guess a more readable name for # anonymous structs, for the common case "typedef struct { } foo". if force_name is not None: explicit_name = '$%s' % force_name else: self._anonymous_counter += 1 explicit_name = '$%d' % self._anonymous_counter tp = None else: explicit_name = name key = '%s %s' % (kind, name) tp = self._declarations.get(key, None) # if tp is None: if kind == 'struct': 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) if not tp.forcename: tp.force_the_name(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. if type.decls is None: return tp # if tp.fldnames is not None: raise api.CDefError("duplicate declaration of struct %s" % name) fldnames = [] fldtypes = [] fldbitsize = [] for decl in type.decls: if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and ''.join(decl.type.names) == '__dotdotdot__'): # XXX pycparser is inconsistent: 'names' should be a list # of strings, but is sometimes just one string. Use # str.join() as a way to cope with both. self._make_partial(tp, nested) continue if decl.bitsize is None: bitsize = -1 else: bitsize = self._parse_constant(decl.bitsize) self._partial_length = False type = self._get_type(decl.type, partial_length_ok=True) if self._partial_length: self._make_partial(tp, nested) if isinstance(type, model.StructType) and type.partial: self._make_partial(tp, nested) fldnames.append(decl.name or '') fldtypes.append(type) fldbitsize.append(bitsize) tp.fldnames = tuple(fldnames) tp.fldtypes = tuple(fldtypes) tp.fldbitsize = tuple(fldbitsize) if fldbitsize != [-1] * len(fldbitsize): if isinstance(tp, model.StructType) and tp.partial: raise NotImplementedError("%s: using both bitfields and '...;'" % (tp,)) tp.packed = self._packed return tp def _make_partial(self, tp, nested): if not isinstance(tp, model.StructOrUnion): raise api.CDefError("%s cannot be partial" % (tp,)) if not tp.has_c_name() and not nested: raise NotImplementedError("%s is partial but has no C name" %(tp,)) tp.partial = True def _parse_constant(self, exprnode, partial_length_ok=False): # for now, limited to expressions that are an immediate number # or negative number if isinstance(exprnode, pycparser.c_ast.Constant): return int(exprnode.value, 0) # if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and exprnode.op == '-'): return -self._parse_constant(exprnode.expr) # if partial_length_ok: if (isinstance(exprnode, pycparser.c_ast.ID) and exprnode.name == '__dotdotdotarray__'): self._partial_length = True return '...' # raise api.FFIError("unsupported expression: expected a " "simple numeric constant") def _build_enum_type(self, explicit_name, decls): if decls is not None: enumerators1 = [enum.name for enum in decls.enumerators] enumerators = [s for s in enumerators1 if not _r_enum_dotdotdot.match(s)] partial = len(enumerators) < len(enumerators1) enumerators = tuple(enumerators) enumvalues = [] nextenumvalue = 0 for enum in decls.enumerators[:len(enumerators)]: if enum.value is not None: nextenumvalue = self._parse_constant(enum.value) enumvalues.append(nextenumvalue) nextenumvalue += 1 enumvalues = tuple(enumvalues) tp = model.EnumType(explicit_name, enumerators, enumvalues) tp.partial = partial else: # opaque enum tp = model.EnumType(explicit_name, (), ()) return tp def include(self, other): for name, tp in other._declarations.items(): kind = name.split(' ', 1)[0] if kind in ('typedef', 'struct', 'union', 'enum'): self._declare(name, tp) cffi-0.8.2/cffi/vengine_cpy.py0000644000076400001440000011173012306266214016510 0ustar arigousers00000000000000import sys, imp from . import model, ffiplatform class VCPythonEngine(object): _class_key = 'x' _gen_python_module = True def __init__(self, verifier): self.verifier = verifier self.ffi = verifier.ffi self._struct_pending_verification = {} self._types_of_builtin_functions = {} def patch_extension_kwds(self, kwds): pass def find_module(self, module_name, path, so_suffixes): try: f, filename, descr = imp.find_module(module_name, path) except ImportError: return None if f is not None: f.close() # Note that after a setuptools installation, there are both .py # and .so files with the same basename. The code here relies on # imp.find_module() locating the .so in priority. if descr[0] not in so_suffixes: return None return filename def collect_types(self): self._typesdict = {} self._generate("collecttype") def _prnt(self, what=''): self._f.write(what + '\n') def _gettypenum(self, type): # a KeyError here is a bug. please report it! :-) return self._typesdict[type] def _do_collect_type(self, tp): if ((not isinstance(tp, model.PrimitiveType) or tp.name == 'long double') and tp not in self._typesdict): num = len(self._typesdict) self._typesdict[tp] = num def write_source_to_f(self): self.collect_types() # # The new module will have a _cffi_setup() function that receives # objects from the ffi world, and that calls some setup code in # the module. This setup code is split in several independent # functions, e.g. one per constant. The functions are "chained" # by ending in a tail call to each other. # # This is further split in two chained lists, depending on if we # can do it at import-time or if we must wait for _cffi_setup() to # provide us with the objects. This is needed because we # need the values of the enum constants in order to build the # that we may have to pass to _cffi_setup(). # # The following two 'chained_list_constants' items contains # the head of these two chained lists, as a string that gives the # call to do, if any. self._chained_list_constants = ['0', '0'] # prnt = self._prnt # first paste some standard set of lines that are mostly '#define' prnt(cffimod_header) prnt() # then paste the C source given by the user, verbatim. prnt(self.verifier.preamble) prnt() # # call generate_cpy_xxx_decl(), for every xxx found from # ffi._parser._declarations. This generates all the functions. self._generate("decl") # # implement the function _cffi_setup_custom() as calling the # head of the chained list. self._generate_setup_custom() prnt() # # produce the method table, including the entries for the # generated Python->C function wrappers, which are done # by generate_cpy_function_method(). prnt('static PyMethodDef _cffi_methods[] = {') self._generate("method") prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS},') prnt(' {NULL, NULL} /* Sentinel */') prnt('};') prnt() # # standard init. modname = self.verifier.get_module_name() if sys.version_info >= (3,): prnt('static struct PyModuleDef _cffi_module_def = {') prnt(' PyModuleDef_HEAD_INIT,') prnt(' "%s",' % modname) prnt(' NULL,') prnt(' -1,') prnt(' _cffi_methods,') prnt(' NULL, NULL, NULL, NULL') prnt('};') prnt() initname = 'PyInit_%s' % modname createmod = 'PyModule_Create(&_cffi_module_def)' errorcase = 'return NULL' finalreturn = 'return lib' else: initname = 'init%s' % modname createmod = 'Py_InitModule("%s", _cffi_methods)' % modname errorcase = 'return' finalreturn = 'return' prnt('PyMODINIT_FUNC') prnt('%s(void)' % initname) prnt('{') prnt(' PyObject *lib;') prnt(' lib = %s;' % createmod) prnt(' if (lib == NULL || %s < 0)' % ( self._chained_list_constants[False],)) prnt(' %s;' % errorcase) prnt(' _cffi_init();') prnt(' %s;' % finalreturn) prnt('}') def load_library(self): # XXX review all usages of 'self' here! # import it as a new extension module try: module = imp.load_dynamic(self.verifier.get_module_name(), self.verifier.modulefilename) except ImportError as e: error = "importing %r: %s" % (self.verifier.modulefilename, e) raise ffiplatform.VerificationError(error) # # call loading_cpy_struct() to get the struct layout inferred by # the C compiler self._load(module, 'loading') # # the C code will need the objects. Collect them in # order in a list. revmapping = dict([(value, key) for (key, value) in self._typesdict.items()]) lst = [revmapping[i] for i in range(len(revmapping))] lst = list(map(self.ffi._get_cached_btype, lst)) # # build the FFILibrary class and instance and call _cffi_setup(). # this will set up some fields like '_cffi_types', and only then # it will invoke the chained list of functions that will really # build (notably) the constant objects, as if they are # pointers, and store them as attributes on the 'library' object. class FFILibrary(object): _cffi_python_module = module _cffi_ffi = self.ffi _cffi_dir = [] def __dir__(self): return FFILibrary._cffi_dir + list(self.__dict__) library = FFILibrary() if module._cffi_setup(lst, ffiplatform.VerificationError, library): import warnings warnings.warn("reimporting %r might overwrite older definitions" % (self.verifier.get_module_name())) # # finally, call the loaded_cpy_xxx() functions. This will perform # the final adjustments, like copying the Python->C wrapper # functions from the module to the 'library' object, and setting # up the FFILibrary class with properties for the global C variables. self._load(module, 'loaded', library=library) module._cffi_original_ffi = self.ffi module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions return library def _get_declarations(self): return sorted(self.ffi._parser._declarations.items()) def _generate(self, step_name): for name, tp in self._get_declarations(): kind, realname = name.split(' ', 1) try: method = getattr(self, '_generate_cpy_%s_%s' % (kind, step_name)) except AttributeError: raise ffiplatform.VerificationError( "not implemented in verify(): %r" % name) try: method(tp, realname) except Exception as e: model.attach_exception_info(e, name) raise def _load(self, module, step_name, **kwds): for name, tp in self._get_declarations(): kind, realname = name.split(' ', 1) method = getattr(self, '_%s_cpy_%s' % (step_name, kind)) try: method(tp, realname, module, **kwds) except Exception as e: model.attach_exception_info(e, name) raise def _generate_nothing(self, tp, name): pass def _loaded_noop(self, tp, name, module, **kwds): pass # ---------- def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): extraarg = '' if isinstance(tp, model.PrimitiveType): if tp.is_integer_type() and tp.name != '_Bool': converter = '_cffi_to_c_int' extraarg = ', %s' % tp.name else: converter = '_cffi_to_c_%s' % (tp.name.replace(' ', '_'),) errvalue = '-1' # elif isinstance(tp, model.PointerType): self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, tovar, errcode) return # elif isinstance(tp, (model.StructOrUnion, model.EnumType)): # a struct (not a struct pointer) as a function argument self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' % (tovar, self._gettypenum(tp), fromvar)) self._prnt(' %s;' % errcode) return # elif isinstance(tp, model.FunctionPtrType): converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) errvalue = 'NULL' # else: raise NotImplementedError(tp) # self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( tovar, tp.get_c_name(''), errvalue)) self._prnt(' %s;' % errcode) def _extra_local_variables(self, tp, localvars): if isinstance(tp, model.PointerType): localvars.add('Py_ssize_t datasize') def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( self._gettypenum(tp), fromvar, tovar)) self._prnt(' if (datasize != 0) {') self._prnt(' if (datasize < 0)') self._prnt(' %s;' % errcode) self._prnt(' %s = alloca(datasize);' % (tovar,)) self._prnt(' memset((void *)%s, 0, datasize);' % (tovar,)) self._prnt(' if (_cffi_convert_array_from_object(' '(char *)%s, _cffi_type(%d), %s) < 0)' % ( tovar, self._gettypenum(tp), fromvar)) self._prnt(' %s;' % errcode) self._prnt(' }') def _convert_expr_from_c(self, tp, var, context): if isinstance(tp, model.PrimitiveType): if tp.is_integer_type(): return '_cffi_from_c_int(%s, %s)' % (var, tp.name) elif tp.name != 'long double': return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) else: return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( var, self._gettypenum(tp)) elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( var, self._gettypenum(tp)) elif isinstance(tp, model.ArrayType): return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( var, self._gettypenum(model.PointerType(tp.item))) elif isinstance(tp, model.StructType): if tp.fldnames is None: raise TypeError("'%s' is used as %s, but is opaque" % ( tp._get_c_name(), context)) return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( var, self._gettypenum(tp)) elif isinstance(tp, model.EnumType): return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( var, self._gettypenum(tp)) else: raise NotImplementedError(tp) # ---------- # typedefs: generates no code so far _generate_cpy_typedef_collecttype = _generate_nothing _generate_cpy_typedef_decl = _generate_nothing _generate_cpy_typedef_method = _generate_nothing _loading_cpy_typedef = _loaded_noop _loaded_cpy_typedef = _loaded_noop # ---------- # function declarations def _generate_cpy_function_collecttype(self, tp, name): assert isinstance(tp, model.FunctionPtrType) if tp.ellipsis: self._do_collect_type(tp) else: # don't call _do_collect_type(tp) in this common case, # otherwise test_autofilled_struct_as_argument fails for type in tp.args: self._do_collect_type(type) self._do_collect_type(tp.result) def _generate_cpy_function_decl(self, tp, name): assert isinstance(tp, model.FunctionPtrType) if tp.ellipsis: # cannot support vararg functions better than this: check for its # exact type (including the fixed arguments), and build it as a # constant function pointer (no CPython wrapper) self._generate_cpy_const(False, name, tp) return prnt = self._prnt numargs = len(tp.args) if numargs == 0: argname = 'no_arg' elif numargs == 1: argname = 'arg0' else: argname = 'args' prnt('static PyObject *') prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) prnt('{') # context = 'argument of %s' % name for i, type in enumerate(tp.args): prnt(' %s;' % type.get_c_name(' x%d' % i, context)) # localvars = set() for type in tp.args: self._extra_local_variables(type, localvars) for decl in localvars: prnt(' %s;' % (decl,)) # if not isinstance(tp.result, model.VoidType): result_code = 'result = ' context = 'result of %s' % name prnt(' %s;' % tp.result.get_c_name(' result', context)) else: result_code = '' # if len(tp.args) > 1: rng = range(len(tp.args)) for i in rng: prnt(' PyObject *arg%d;' % i) prnt() prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % ( 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng]))) prnt(' return NULL;') prnt() # for i, type in enumerate(tp.args): self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, 'return NULL') prnt() # prnt(' Py_BEGIN_ALLOW_THREADS') prnt(' _cffi_restore_errno();') prnt(' { %s%s(%s); }' % ( result_code, name, ', '.join(['x%d' % i for i in range(len(tp.args))]))) prnt(' _cffi_save_errno();') prnt(' Py_END_ALLOW_THREADS') prnt() # if result_code: prnt(' return %s;' % self._convert_expr_from_c(tp.result, 'result', 'result type')) else: prnt(' Py_INCREF(Py_None);') prnt(' return Py_None;') prnt('}') prnt() def _generate_cpy_function_method(self, tp, name): if tp.ellipsis: return numargs = len(tp.args) if numargs == 0: meth = 'METH_NOARGS' elif numargs == 1: meth = 'METH_O' else: meth = 'METH_VARARGS' self._prnt(' {"%s", _cffi_f_%s, %s},' % (name, name, meth)) _loading_cpy_function = _loaded_noop def _loaded_cpy_function(self, tp, name, module, library): if tp.ellipsis: return func = getattr(module, name) setattr(library, name, func) self._types_of_builtin_functions[func] = tp # ---------- # named structs _generate_cpy_struct_collecttype = _generate_nothing def _generate_cpy_struct_decl(self, tp, name): assert name == tp.name self._generate_struct_or_union_decl(tp, 'struct', name) def _generate_cpy_struct_method(self, tp, name): self._generate_struct_or_union_method(tp, 'struct', name) def _loading_cpy_struct(self, tp, name, module): self._loading_struct_or_union(tp, 'struct', name, module) def _loaded_cpy_struct(self, tp, name, module, **kwds): self._loaded_struct_or_union(tp) _generate_cpy_union_collecttype = _generate_nothing def _generate_cpy_union_decl(self, tp, name): assert name == tp.name self._generate_struct_or_union_decl(tp, 'union', name) def _generate_cpy_union_method(self, tp, name): self._generate_struct_or_union_method(tp, 'union', name) def _loading_cpy_union(self, tp, name, module): self._loading_struct_or_union(tp, 'union', name, module) def _loaded_cpy_union(self, tp, name, module, **kwds): self._loaded_struct_or_union(tp) def _generate_struct_or_union_decl(self, tp, prefix, name): if tp.fldnames is None: return # nothing to do with opaque structs checkfuncname = '_cffi_check_%s_%s' % (prefix, name) layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) cname = ('%s %s' % (prefix, name)).strip() # prnt = self._prnt prnt('static void %s(%s *p)' % (checkfuncname, cname)) prnt('{') prnt(' /* only to generate compile-time warnings or errors */') for fname, ftype, fbitsize in tp.enumfields(): if (isinstance(ftype, model.PrimitiveType) and ftype.is_integer_type()) or fbitsize >= 0: # accept all integers, but complain on float or double prnt(' (void)((p->%s) << 1);' % fname) else: # only accept exactly the type declared. try: prnt(' { %s = &p->%s; (void)tmp; }' % ( ftype.get_c_name('*tmp', 'field %r'%fname), fname)) except ffiplatform.VerificationError as e: prnt(' /* %s */' % str(e)) # cannot verify it, ignore prnt('}') prnt('static PyObject *') prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,)) prnt('{') prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) prnt(' static Py_ssize_t nums[] = {') prnt(' sizeof(%s),' % cname) prnt(' offsetof(struct _cffi_aligncheck, y),') for fname, ftype, fbitsize in tp.enumfields(): if fbitsize >= 0: continue # xxx ignore fbitsize for now prnt(' offsetof(%s, %s),' % (cname, fname)) if isinstance(ftype, model.ArrayType) and ftype.length is None: prnt(' 0, /* %s */' % ftype._get_c_name()) else: prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) prnt(' -1') prnt(' };') prnt(' return _cffi_get_struct_layout(nums);') prnt(' /* the next line is not executed, but compiled */') prnt(' %s(0);' % (checkfuncname,)) prnt('}') prnt() def _generate_struct_or_union_method(self, tp, prefix, name): if tp.fldnames is None: return # nothing to do with opaque structs layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) self._prnt(' {"%s", %s, METH_NOARGS},' % (layoutfuncname, layoutfuncname)) def _loading_struct_or_union(self, tp, prefix, name, module): if tp.fldnames is None: return # nothing to do with opaque structs layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) # function = getattr(module, layoutfuncname) layout = function() if isinstance(tp, model.StructOrUnion) and tp.partial: # use the function()'s sizes and offsets to guide the # layout of the struct totalsize = layout[0] totalalignment = layout[1] fieldofs = layout[2::2] fieldsize = layout[3::2] tp.force_flatten() assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment else: cname = ('%s %s' % (prefix, name)).strip() self._struct_pending_verification[tp] = layout, cname def _loaded_struct_or_union(self, tp): if tp.fldnames is None: return # nothing to do with opaque structs self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered if tp in self._struct_pending_verification: # check that the layout sizes and offsets match the real ones def check(realvalue, expectedvalue, msg): if realvalue != expectedvalue: raise ffiplatform.VerificationError( "%s (we have %d, but C compiler says %d)" % (msg, expectedvalue, realvalue)) ffi = self.ffi BStruct = ffi._get_cached_btype(tp) layout, cname = self._struct_pending_verification.pop(tp) check(layout[0], ffi.sizeof(BStruct), "wrong total size") check(layout[1], ffi.alignof(BStruct), "wrong total alignment") i = 2 for fname, ftype, fbitsize in tp.enumfields(): if fbitsize >= 0: continue # xxx ignore fbitsize for now check(layout[i], ffi.offsetof(BStruct, fname), "wrong offset for field %r" % (fname,)) if layout[i+1] != 0: BField = ffi._get_cached_btype(ftype) check(layout[i+1], ffi.sizeof(BField), "wrong size for field %r" % (fname,)) i += 2 assert i == len(layout) # ---------- # 'anonymous' declarations. These are produced for anonymous structs # or unions; the 'name' is obtained by a typedef. _generate_cpy_anonymous_collecttype = _generate_nothing def _generate_cpy_anonymous_decl(self, 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): if not isinstance(tp, model.EnumType): self._generate_struct_or_union_method(tp, '', name) def _loading_cpy_anonymous(self, 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): 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' def _generate_cpy_const(self, is_int, name, tp=None, category='const', vartp=None, delayed=True, size_too=False): prnt = self._prnt funcname = '_cffi_%s_%s' % (category, name) prnt('static int %s(PyObject *lib)' % funcname) prnt('{') prnt(' PyObject *o;') prnt(' int res;') if not is_int: prnt(' %s;' % (vartp or tp).get_c_name(' i', name)) else: assert category == 'const' # if not is_int: if category == 'var': realexpr = '&' + name else: realexpr = name prnt(' i = (%s);' % (realexpr,)) prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i', 'variable type'),)) assert delayed else: prnt(' if (LONG_MIN <= (%s) && (%s) <= LONG_MAX)' % (name, name)) prnt(' o = PyInt_FromLong((long)(%s));' % (name,)) prnt(' else if ((%s) <= 0)' % (name,)) prnt(' o = PyLong_FromLongLong((long long)(%s));' % (name,)) prnt(' else') prnt(' o = PyLong_FromUnsignedLongLong(' '(unsigned long long)(%s));' % (name,)) prnt(' if (o == NULL)') prnt(' return -1;') if size_too: prnt(' {') prnt(' PyObject *o1 = o;') prnt(' o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));' % (name,)) prnt(' Py_DECREF(o1);') prnt(' if (o == NULL)') prnt(' return -1;') prnt(' }') prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name) prnt(' Py_DECREF(o);') prnt(' if (res < 0)') prnt(' return -1;') prnt(' return %s;' % self._chained_list_constants[delayed]) self._chained_list_constants[delayed] = funcname + '(lib)' prnt('}') prnt() def _generate_cpy_constant_collecttype(self, tp, name): is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() if not is_int: self._do_collect_type(tp) def _generate_cpy_constant_decl(self, tp, name): is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() self._generate_cpy_const(is_int, name, tp) _generate_cpy_constant_method = _generate_nothing _loading_cpy_constant = _loaded_noop _loaded_cpy_constant = _loaded_noop # ---------- # enums 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_e_%s_%s' % (prefix, name) prnt = self._prnt prnt('static int %s(PyObject *lib)' % funcname) prnt('{') for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): if enumvalue < 0: prnt(' if ((%s) >= 0 || (long)(%s) != %dL) {' % ( enumerator, enumerator, enumvalue)) else: prnt(' if ((%s) < 0 || (unsigned long)(%s) != %dUL) {' % ( enumerator, enumerator, enumvalue)) prnt(' char buf[64];') prnt(' if ((%s) < 0)' % enumerator) prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % enumerator) prnt(' else') prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % enumerator) prnt(' PyErr_Format(_cffi_VerificationError,') prnt(' "enum %s: %s has the real value %s, ' 'not %s",') prnt(' "%s", "%s", buf, "%d");' % ( name, enumerator, enumvalue)) prnt(' return -1;') prnt(' }') prnt(' return %s;' % self._chained_list_constants[True]) self._chained_list_constants[True] = funcname + '(lib)' prnt('}') prnt() _generate_cpy_enum_collecttype = _generate_nothing _generate_cpy_enum_method = _generate_nothing def _loading_cpy_enum(self, tp, name, module): if tp.partial: enumvalues = [getattr(module, enumerator) for enumerator in tp.enumerators] tp.enumvalues = tuple(enumvalues) tp.partial_resolved = True def _loaded_cpy_enum(self, tp, name, module, library): for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): setattr(library, enumerator, enumvalue) # ---------- # macros: for now only for integers def _generate_cpy_macro_decl(self, tp, name): assert tp == '...' self._generate_cpy_const(True, name) _generate_cpy_macro_collecttype = _generate_nothing _generate_cpy_macro_method = _generate_nothing _loading_cpy_macro = _loaded_noop _loaded_cpy_macro = _loaded_noop # ---------- # global variables def _generate_cpy_variable_collecttype(self, tp, name): if isinstance(tp, model.ArrayType): tp_ptr = model.PointerType(tp.item) else: tp_ptr = model.PointerType(tp) self._do_collect_type(tp_ptr) def _generate_cpy_variable_decl(self, tp, name): if isinstance(tp, model.ArrayType): tp_ptr = model.PointerType(tp.item) self._generate_cpy_const(False, name, tp, vartp=tp_ptr, size_too = (tp.length == '...')) else: tp_ptr = model.PointerType(tp) self._generate_cpy_const(False, name, tp_ptr, category='var') _generate_cpy_variable_method = _generate_nothing _loading_cpy_variable = _loaded_noop def _loaded_cpy_variable(self, tp, name, module, library): value = getattr(library, name) if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the # sense that "a=..." is forbidden if tp.length == '...': assert isinstance(value, tuple) (value, size) = value BItemType = self.ffi._get_cached_btype(tp.item) length, rest = divmod(size, self.ffi.sizeof(BItemType)) if rest != 0: raise ffiplatform.VerificationError( "bad size: %r does not seem to be an array of %s" % (name, tp.item)) tp = tp.resolve_length(length) # 'value' is a which we have to replace with # a if the N is actually known if tp.length is not None: BArray = self.ffi._get_cached_btype(tp) value = self.ffi.cast(BArray, value) setattr(library, name, value) return # remove ptr= from the library instance, and replace # it by a property on the class, which reads/writes into ptr[0]. ptr = value delattr(library, name) def getter(library): return ptr[0] def setter(library, value): ptr[0] = value setattr(type(library), name, property(getter, setter)) type(library)._cffi_dir.append(name) # ---------- def _generate_setup_custom(self): prnt = self._prnt prnt('static int _cffi_setup_custom(PyObject *lib)') prnt('{') prnt(' return %s;' % self._chained_list_constants[True]) prnt('}') cffimod_header = r''' #include #include #ifdef MS_WIN32 #include /* for alloca() */ typedef __int8 int8_t; typedef __int16 int16_t; typedef __int32 int32_t; typedef __int64 int64_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; typedef unsigned char _Bool; #endif #if PY_MAJOR_VERSION < 3 # undef PyCapsule_CheckExact # undef PyCapsule_GetPointer # define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule)) # define PyCapsule_GetPointer(capsule, name) \ (PyCObject_AsVoidPtr(capsule)) #endif #if PY_MAJOR_VERSION >= 3 # define PyInt_FromLong PyLong_FromLong #endif #define _cffi_from_c_double PyFloat_FromDouble #define _cffi_from_c_float PyFloat_FromDouble #define _cffi_from_c_long PyInt_FromLong #define _cffi_from_c_ulong PyLong_FromUnsignedLong #define _cffi_from_c_longlong PyLong_FromLongLong #define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong #define _cffi_to_c_double PyFloat_AsDouble #define _cffi_to_c_float PyFloat_AsDouble #define _cffi_from_c_int(x, type) \ (((type)-1) > 0 ? /* unsigned */ \ (sizeof(type) < sizeof(long) ? PyInt_FromLong(x) : \ sizeof(type) == sizeof(long) ? PyLong_FromUnsignedLong(x) : \ PyLong_FromUnsignedLongLong(x)) \ : (sizeof(type) <= sizeof(long) ? PyInt_FromLong(x) : \ PyLong_FromLongLong(x))) #define _cffi_to_c_int(o, type) \ (sizeof(type) == 1 ? (((type)-1) > 0 ? _cffi_to_c_u8(o) \ : _cffi_to_c_i8(o)) : \ sizeof(type) == 2 ? (((type)-1) > 0 ? _cffi_to_c_u16(o) \ : _cffi_to_c_i16(o)) : \ sizeof(type) == 4 ? (((type)-1) > 0 ? _cffi_to_c_u32(o) \ : _cffi_to_c_i32(o)) : \ sizeof(type) == 8 ? (((type)-1) > 0 ? _cffi_to_c_u64(o) \ : _cffi_to_c_i64(o)) : \ (Py_FatalError("unsupported size for type " #type), 0)) #define _cffi_to_c_i8 \ ((int(*)(PyObject *))_cffi_exports[1]) #define _cffi_to_c_u8 \ ((int(*)(PyObject *))_cffi_exports[2]) #define _cffi_to_c_i16 \ ((int(*)(PyObject *))_cffi_exports[3]) #define _cffi_to_c_u16 \ ((int(*)(PyObject *))_cffi_exports[4]) #define _cffi_to_c_i32 \ ((int(*)(PyObject *))_cffi_exports[5]) #define _cffi_to_c_u32 \ ((unsigned int(*)(PyObject *))_cffi_exports[6]) #define _cffi_to_c_i64 \ ((long long(*)(PyObject *))_cffi_exports[7]) #define _cffi_to_c_u64 \ ((unsigned long long(*)(PyObject *))_cffi_exports[8]) #define _cffi_to_c_char \ ((int(*)(PyObject *))_cffi_exports[9]) #define _cffi_from_c_pointer \ ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) #define _cffi_to_c_pointer \ ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) #define _cffi_get_struct_layout \ ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12]) #define _cffi_restore_errno \ ((void(*)(void))_cffi_exports[13]) #define _cffi_save_errno \ ((void(*)(void))_cffi_exports[14]) #define _cffi_from_c_char \ ((PyObject *(*)(char))_cffi_exports[15]) #define _cffi_from_c_deref \ ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) #define _cffi_to_c \ ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) #define _cffi_from_c_struct \ ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) #define _cffi_to_c_wchar_t \ ((wchar_t(*)(PyObject *))_cffi_exports[19]) #define _cffi_from_c_wchar_t \ ((PyObject *(*)(wchar_t))_cffi_exports[20]) #define _cffi_to_c_long_double \ ((long double(*)(PyObject *))_cffi_exports[21]) #define _cffi_to_c__Bool \ ((_Bool(*)(PyObject *))_cffi_exports[22]) #define _cffi_prepare_pointer_call_argument \ ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) #define _cffi_convert_array_from_object \ ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) #define _CFFI_NUM_EXPORTS 25 typedef struct _ctypedescr CTypeDescrObject; static void *_cffi_exports[_CFFI_NUM_EXPORTS]; static PyObject *_cffi_types, *_cffi_VerificationError; static int _cffi_setup_custom(PyObject *lib); /* forward */ static PyObject *_cffi_setup(PyObject *self, PyObject *args) { PyObject *library; int was_alive = (_cffi_types != NULL); if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError, &library)) return NULL; Py_INCREF(_cffi_types); Py_INCREF(_cffi_VerificationError); if (_cffi_setup_custom(library) < 0) return NULL; return PyBool_FromLong(was_alive); } static void _cffi_init(void) { PyObject *module = PyImport_ImportModule("_cffi_backend"); PyObject *c_api_object; if (module == NULL) return; c_api_object = PyObject_GetAttrString(module, "_C_API"); if (c_api_object == NULL) return; if (!PyCapsule_CheckExact(c_api_object)) { Py_DECREF(c_api_object); PyErr_SetNone(PyExc_ImportError); return; } memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), _CFFI_NUM_EXPORTS * sizeof(void *)); Py_DECREF(c_api_object); } #define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) /**********/ ''' cffi-0.8.2/cffi/ffiplatform.py0000644000076400001440000000626012104773620016514 0ustar arigousers00000000000000import os class VerificationError(Exception): """ An error raised when verification fails """ class VerificationMissing(Exception): """ An error raised when incomplete structures are passed into cdef, but no verification has been done """ def get_extension(srcfilename, modname, sources=(), **kwds): from distutils.core import Extension allsources = [srcfilename] allsources.extend(sources) return Extension(name=modname, sources=allsources, **kwds) def compile(tmpdir, ext): """Compile a C extension module using distutils.""" saved_environ = os.environ.copy() try: outputfilename = _build(tmpdir, ext) outputfilename = os.path.abspath(outputfilename) finally: # workaround for a distutils bugs where some env vars can # become longer and longer every time it is used for key, value in saved_environ.items(): if os.environ.get(key) != value: os.environ[key] = value return outputfilename def _build(tmpdir, ext): # XXX compact but horrible :-( from distutils.core import Distribution import distutils.errors # dist = Distribution({'ext_modules': [ext]}) options = dist.get_option_dict('build_ext') options['force'] = ('ffiplatform', True) options['build_lib'] = ('ffiplatform', tmpdir) options['build_temp'] = ('ffiplatform', tmpdir) # try: dist.run_command('build_ext') except (distutils.errors.CompileError, distutils.errors.LinkError) as e: raise VerificationError('%s: %s' % (e.__class__.__name__, e)) # cmd_obj = dist.get_command_obj('build_ext') [soname] = cmd_obj.get_outputs() return soname try: from os.path import samefile except ImportError: def samefile(f1, f2): return os.path.abspath(f1) == os.path.abspath(f2) def maybe_relative_path(path): if not os.path.isabs(path): return path # already relative dir = path names = [] while True: prevdir = dir dir, name = os.path.split(prevdir) if dir == prevdir or not dir: return path # failed to make it relative names.append(name) try: if samefile(dir, os.curdir): names.reverse() return os.path.join(*names) except OSError: pass # ____________________________________________________________ try: int_or_long = (int, long) import cStringIO except NameError: int_or_long = int # Python 3 import io as cStringIO def _flatten(x, f): if isinstance(x, str): f.write('%ds%s' % (len(x), x)) elif isinstance(x, dict): keys = sorted(x.keys()) f.write('%dd' % len(keys)) for key in keys: _flatten(key, f) _flatten(x[key], f) elif isinstance(x, (list, tuple)): f.write('%dl' % len(x)) for value in x: _flatten(value, f) elif isinstance(x, int_or_long): f.write('%di' % (x,)) else: raise TypeError( "the keywords to verify() contains unsupported object %r" % (x,)) def flatten(x): f = cStringIO.StringIO() _flatten(x, f) return f.getvalue() cffi-0.8.2/cffi/api.py0000644000076400001440000004675312306266214014767 0ustar arigousers00000000000000import sys, types from .lock import allocate_lock try: callable except NameError: # Python 3.1 from collections import Callable callable = lambda x: isinstance(x, Callable) try: basestring except NameError: # Python 3.x basestring = str class FFIError(Exception): pass class CDefError(Exception): def __str__(self): try: line = 'line %d: ' % (self.args[1].coord.line,) except (AttributeError, TypeError, IndexError): line = '' return '%s%s' % (line, self.args[0]) class FFI(object): r''' The main top-level class that you instantiate once, or once per module. Example usage: ffi = FFI() ffi.cdef(""" int printf(const char *, ...); """) C = ffi.dlopen(None) # standard library -or- C = ffi.verify() # use a C compiler: verify the decl above is right C.printf("hello, %s!\n", ffi.new("char[]", "world")) ''' def __init__(self, backend=None): """Create an FFI instance. The 'backend' argument is used to select a non-default backend, mostly for tests. """ from . import cparser, model if backend is None: # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with # _cffi_backend.so compiled. import _cffi_backend as backend from . import __version__ assert (backend.__version__ == __version__ or backend.__version__ == __version__[:3]) # (If you insist you can also try to pass the option # 'backend=backend_ctypes.CTypesBackend()', but don't # rely on it! It's probably not going to work well.) self._backend = backend self._lock = allocate_lock() self._parser = cparser.Parser() self._cached_btypes = {} self._parsed_types = types.ModuleType('parsed_types').__dict__ self._new_types = types.ModuleType('new_types').__dict__ self._function_caches = [] self._libraries = [] self._cdefsources = [] if hasattr(backend, 'set_ffi'): backend.set_ffi(self) for name in backend.__dict__: if name.startswith('RTLD_'): setattr(self, name, getattr(backend, name)) # with self._lock: self.BVoidP = self._get_cached_btype(model.voidp_type) if isinstance(backend, types.ModuleType): # _cffi_backend: attach these constants to the class if not hasattr(FFI, 'NULL'): FFI.NULL = self.cast(self.BVoidP, 0) FFI.CData, FFI.CType = backend._get_types() else: # ctypes backend: attach these constants to the instance self.NULL = self.cast(self.BVoidP, 0) self.CData, self.CType = backend._get_types() def cdef(self, csource, override=False, packed=False): """Parse the given C source. This registers all declared functions, types, and global variables. The functions and global variables can then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. The types can be used in 'ffi.new()' and other functions. If 'packed' is specified as True, all structs declared inside this cdef are packed, i.e. laid out without any field alignment at all. """ if not isinstance(csource, str): # unicode, on Python 2 if not isinstance(csource, basestring): raise TypeError("cdef() argument must be a string") csource = csource.encode('ascii') with self._lock: self._parser.parse(csource, override=override, packed=packed) self._cdefsources.append(csource) if override: for cache in self._function_caches: cache.clear() def dlopen(self, name, flags=0): """Load and return a dynamic library identified by 'name'. The standard C library can be loaded by passing None. Note that functions and types declared by 'ffi.cdef()' are not linked to a particular library, just like C headers; in the library we only look for the actual (untyped) symbols. """ assert isinstance(name, basestring) or name is None with self._lock: lib, function_cache = _make_ffi_library(self, name, flags) self._function_caches.append(function_cache) self._libraries.append(lib) return lib def _typeof_locked(self, cdecl): # call me with the lock! key = cdecl if key in self._parsed_types: return self._parsed_types[key] # if not isinstance(cdecl, str): # unicode, on Python 2 cdecl = cdecl.encode('ascii') # type = self._parser.parse_type(cdecl) really_a_function_type = type.is_raw_function if really_a_function_type: type = type.as_function_pointer() btype = self._get_cached_btype(type) result = btype, really_a_function_type self._parsed_types[key] = result return result def _typeof(self, cdecl, consider_function_as_funcptr=False): # string -> ctype object try: result = self._parsed_types[cdecl] except KeyError: with self._lock: result = self._typeof_locked(cdecl) # btype, really_a_function_type = result if really_a_function_type and not consider_function_as_funcptr: raise CDefError("the type %r is a function type, not a " "pointer-to-function type" % (cdecl,)) return btype def typeof(self, cdecl): """Parse the C type given as a string and return the corresponding object. It can also be used on 'cdata' instance to get its C type. """ if isinstance(cdecl, basestring): return self._typeof(cdecl) if isinstance(cdecl, self.CData): return self._backend.typeof(cdecl) if isinstance(cdecl, types.BuiltinFunctionType): res = _builtin_function_type(cdecl) if res is not None: return res if (isinstance(cdecl, types.FunctionType) and hasattr(cdecl, '_cffi_base_type')): with self._lock: return self._get_cached_btype(cdecl._cffi_base_type) raise TypeError(type(cdecl)) def sizeof(self, cdecl): """Return the size in bytes of the argument. It can be a string naming a C type, or a 'cdata' instance. """ if isinstance(cdecl, basestring): BType = self._typeof(cdecl) return self._backend.sizeof(BType) else: return self._backend.sizeof(cdecl) def alignof(self, cdecl): """Return the natural alignment size in bytes of the C type given as a string. """ if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl) return self._backend.alignof(cdecl) def offsetof(self, cdecl, fieldname): """Return the offset of the named field inside the given structure, which must be given as a C type name. """ if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl) return self._backend.typeoffsetof(cdecl, fieldname)[1] def new(self, cdecl, init=None): """Allocate an instance according to the specified C type and return a pointer to it. The specified C type must be either a pointer or an array: ``new('X *')`` allocates an X and returns a pointer to it, whereas ``new('X[n]')`` allocates an array of n X'es and returns an array referencing it (which works mostly like a pointer, like in C). You can also use ``new('X[]', n)`` to allocate an array of a non-constant length n. The memory is initialized following the rules of declaring a global variable in C: by default it is zero-initialized, but an explicit initializer can be given which can be used to fill all or part of the memory. When the returned object goes out of scope, the memory is freed. In other words the returned object has ownership of the value of type 'cdecl' that it points to. This means that the raw data can be used as long as this object is kept alive, but must not be used for a longer time. Be careful about that when copying the pointer to the memory somewhere else, e.g. into another structure. """ if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl) return self._backend.newp(cdecl, init) def cast(self, cdecl, source): """Similar to a C cast: returns an instance of the named C type initialized with the given 'source'. The source is casted between integers or pointers of any type. """ if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl) return self._backend.cast(cdecl, source) def string(self, cdata, maxlen=-1): """Return a Python string (or unicode string) from the 'cdata'. If 'cdata' is a pointer or array of characters or bytes, returns the null-terminated string. The returned string extends until the first null character, or at most 'maxlen' characters. If 'cdata' is an array then 'maxlen' defaults to its length. If 'cdata' is a pointer or array of wchar_t, returns a unicode string following the same rules. If 'cdata' is a single character or byte or a wchar_t, returns it as a string or unicode string. If 'cdata' is an enum, returns the value of the enumerator as a string, or 'NUMBER' if the value is out of range. """ return self._backend.string(cdata, maxlen) def buffer(self, cdata, size=-1): """Return a read-write buffer object that references the raw C data pointed to by the given 'cdata'. The 'cdata' must be a pointer or an array. Can be passed to functions expecting a buffer, or directly manipulated with: buf[:] get a copy of it in a regular string, or buf[idx] as a single character buf[:] = ... buf[idx] = ... change the content """ return self._backend.buffer(cdata, size) def callback(self, cdecl, python_callable=None, error=None): """Return a callback object or a decorator making such a callback object. 'cdecl' must name a C function pointer type. The callback invokes the specified 'python_callable' (which may be provided either directly or via a decorator). Important: the callback object must be manually kept alive for as long as the callback may be invoked from the C level. """ def callback_decorator_wrap(python_callable): if not callable(python_callable): raise TypeError("the 'python_callable' argument " "is not callable") return self._backend.callback(cdecl, python_callable, error) if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl, consider_function_as_funcptr=True) if python_callable is None: return callback_decorator_wrap # decorator mode else: return callback_decorator_wrap(python_callable) # direct mode def getctype(self, cdecl, replace_with=''): """Return a string giving the C type 'cdecl', which may be itself a string or a object. If 'replace_with' is given, it gives extra text to append (or insert for more complicated C types), like a variable name, or '*' to get actually the C type 'pointer-to-cdecl'. """ if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl) replace_with = replace_with.strip() if (replace_with.startswith('*') and '&[' in self._backend.getcname(cdecl, '&')): replace_with = '(%s)' % replace_with elif replace_with and not replace_with[0] in '[(': replace_with = ' ' + replace_with return self._backend.getcname(cdecl, replace_with) def gc(self, cdata, destructor): """Return a new cdata object that points to the same data. Later, when this new cdata object is garbage-collected, 'destructor(old_cdata_object)' will be called. """ with self._lock: try: gc_weakrefs = self.gc_weakrefs except AttributeError: from .gc_weakref import GcWeakrefs gc_weakrefs = self.gc_weakrefs = GcWeakrefs(self) return gc_weakrefs.build(cdata, destructor) def _get_cached_btype(self, type): assert self._lock.acquire(False) is False # call me with the lock! try: BType = self._cached_btypes[type] except KeyError: finishlist = [] BType = type.get_cached_btype(self, finishlist) for type in finishlist: type.finish_backend_type(self, finishlist) return BType def verify(self, source='', tmpdir=None, **kwargs): """Verify that the current ffi signatures compile on this machine, and return a dynamic library object. The dynamic library can be used to call functions and access global variables declared in this 'ffi'. The library is compiled by the C compiler: it gives you C-level API compatibility (including calling macros). This is unlike 'ffi.dlopen()', which requires binary compatibility in the signatures. """ from .verifier import Verifier, _caller_dir_pycache tmpdir = tmpdir or _caller_dir_pycache() self.verifier = Verifier(self, source, tmpdir, **kwargs) lib = self.verifier.load_library() self._libraries.append(lib) return lib def _get_errno(self): return self._backend.get_errno() def _set_errno(self, errno): self._backend.set_errno(errno) errno = property(_get_errno, _set_errno, None, "the value of 'errno' from/to the C calls") def getwinerror(self, code=-1): return self._backend.getwinerror(code) def _pointer_to(self, ctype): from . import model with self._lock: return model.pointer_cache(self, ctype) def addressof(self, cdata, field=None): """Return the address of a . If 'field' is specified, return the address of this field. """ ctype = self._backend.typeof(cdata) ctype, offset = self._backend.typeoffsetof(ctype, field) ctypeptr = self._pointer_to(ctype) return self._backend.rawaddressof(ctypeptr, cdata, offset) def include(self, ffi_to_include): """Includes the typedefs, structs, unions and enums defined in another FFI instance. Usage is similar to a #include in C, where a part of the program might include types defined in another part for its own usage. Note that the include() method has no effect on functions, constants and global variables, which must anyway be accessed directly from the lib object returned by the original FFI instance. """ with ffi_to_include._lock: with self._lock: self._parser.include(ffi_to_include._parser) self._cdefsources.append('[') self._cdefsources.extend(ffi_to_include._cdefsources) self._cdefsources.append(']') def new_handle(self, x): return self._backend.newp_handle(self.BVoidP, x) def from_handle(self, x): return self._backend.from_handle(x) def _load_backend_lib(backend, name, flags): if name is None: if sys.platform != "win32": return backend.load_library(None, flags) name = "c" # Windows: load_library(None) fails, but this works # (backward compatibility hack only) try: if '.' not in name and '/' not in name: raise OSError("library not found: %r" % (name,)) return backend.load_library(name, flags) except OSError: import ctypes.util path = ctypes.util.find_library(name) if path is None: raise # propagate the original OSError return backend.load_library(path, flags) def _make_ffi_library(ffi, libname, flags): import os backend = ffi._backend backendlib = _load_backend_lib(backend, libname, flags) copied_enums = [] # def make_accessor_locked(name): key = 'function ' + name if key in ffi._parser._declarations: tp = ffi._parser._declarations[key] BType = ffi._get_cached_btype(tp) try: value = backendlib.load_function(BType, name) except KeyError as e: raise AttributeError('%s: %s' % (name, e)) library.__dict__[name] = value return # key = 'variable ' + name if key in ffi._parser._declarations: tp = ffi._parser._declarations[key] BType = ffi._get_cached_btype(tp) read_variable = backendlib.read_variable write_variable = backendlib.write_variable setattr(FFILibrary, name, property( lambda self: read_variable(BType, name), lambda self, value: write_variable(BType, name, value))) return # if not copied_enums: from . import model for key, tp in ffi._parser._declarations.items(): if not isinstance(tp, model.EnumType): continue for enumname, enumval in zip(tp.enumerators, tp.enumvalues): if enumname not in library.__dict__: library.__dict__[enumname] = enumval copied_enums.append(True) if name in library.__dict__: return # raise AttributeError(name) # def make_accessor(name): with ffi._lock: if name in library.__dict__ or name in FFILibrary.__dict__: return # added by another thread while waiting for the lock make_accessor_locked(name) # class FFILibrary(object): def __getattr__(self, name): make_accessor(name) return getattr(self, name) def __setattr__(self, name, value): try: property = getattr(self.__class__, name) except AttributeError: make_accessor(name) setattr(self, name, value) else: property.__set__(self, value) # if libname is not None: try: if not isinstance(libname, str): # unicode, on Python 2 libname = libname.encode('utf-8') FFILibrary.__name__ = 'FFILibrary_%s' % libname except UnicodeError: pass library = FFILibrary() return library, library.__dict__ def _builtin_function_type(func): # a hack to make at least ffi.typeof(builtin_function) work, # if the builtin function was obtained by 'vengine_cpy'. import sys try: module = sys.modules[func.__module__] ffi = module._cffi_original_ffi types_of_builtin_funcs = module._cffi_types_of_builtin_funcs tp = types_of_builtin_funcs[func] except (KeyError, AttributeError, TypeError): return None else: with ffi._lock: return ffi._get_cached_btype(tp) cffi-0.8.2/cffi/lock.py0000644000076400001440000000135312237636277015146 0ustar arigousers00000000000000import sys if sys.version_info < (3,): try: from thread import allocate_lock except ImportError: from dummy_thread import allocate_lock else: try: from _thread import allocate_lock except ImportError: from _dummy_thread import allocate_lock ##import sys ##l1 = allocate_lock ##class allocate_lock(object): ## def __init__(self): ## self._real = l1() ## def __enter__(self): ## for i in range(4, 0, -1): ## print sys._getframe(i).f_code ## print ## return self._real.__enter__() ## def __exit__(self, *args): ## return self._real.__exit__(*args) ## def acquire(self, f): ## assert f is False ## return self._real.acquire(f) cffi-0.8.2/cffi/backend_ctypes.py0000644000076400001440000011452012306266214017160 0ustar arigousers00000000000000import ctypes, ctypes.util, operator, sys from . import model if sys.version_info < (3,): integer_types = (int, long) bytechr = chr else: unicode = str integer_types = int xrange = range bytechr = lambda num: bytes([num]) class CTypesType(type): pass class CTypesData(object): __metaclass__ = CTypesType __slots__ = ['__weakref__'] __name__ = '' def __init__(self, *args): raise TypeError("cannot instantiate %r" % (self.__class__,)) @classmethod def _newp(cls, init): raise TypeError("expected a pointer or array ctype, got '%s'" % (cls._get_c_name(),)) @staticmethod def _to_ctypes(value): raise TypeError @classmethod def _arg_to_ctypes(cls, *value): try: ctype = cls._ctype except AttributeError: raise TypeError("cannot create an instance of %r" % (cls,)) if value: res = cls._to_ctypes(*value) if not isinstance(res, ctype): res = cls._ctype(res) else: res = cls._ctype() return res @classmethod def _create_ctype_obj(cls, init): if init is None: return cls._arg_to_ctypes() else: return cls._arg_to_ctypes(init) @staticmethod def _from_ctypes(ctypes_value): raise TypeError @classmethod def _get_c_name(cls, replace_with=''): return cls._reftypename.replace(' &', replace_with) @classmethod def _fix_class(cls): cls.__name__ = 'CData<%s>' % (cls._get_c_name(),) cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),) cls.__module__ = 'ffi' def _get_own_repr(self): raise NotImplementedError def _addr_repr(self, address): if address == 0: return 'NULL' else: if address < 0: address += 1 << (8*ctypes.sizeof(ctypes.c_void_p)) return '0x%x' % address def __repr__(self, c_name=None): own = self._get_own_repr() return '' % (c_name or self._get_c_name(), own) def _convert_to_address(self, BClass): if BClass is None: raise TypeError("cannot convert %r to an address" % ( self._get_c_name(),)) else: raise TypeError("cannot convert %r to %r" % ( self._get_c_name(), BClass._get_c_name())) @classmethod def _get_size(cls): return ctypes.sizeof(cls._ctype) def _get_size_of_instance(self): return ctypes.sizeof(self._ctype) @classmethod def _cast_from(cls, source): raise TypeError("cannot cast to %r" % (cls._get_c_name(),)) def _cast_to_integer(self): return self._convert_to_address(None) @classmethod def _alignment(cls): return ctypes.alignment(cls._ctype) def __iter__(self): raise TypeError("cdata %r does not support iteration" % ( self._get_c_name()),) def _make_cmp(name): cmpfunc = getattr(operator, name) def cmp(self, other): if isinstance(other, CTypesData): return cmpfunc(self._convert_to_address(None), other._convert_to_address(None)) else: return NotImplemented cmp.func_name = name return cmp __eq__ = _make_cmp('__eq__') __ne__ = _make_cmp('__ne__') __lt__ = _make_cmp('__lt__') __le__ = _make_cmp('__le__') __gt__ = _make_cmp('__gt__') __ge__ = _make_cmp('__ge__') def __hash__(self): return hash(type(self)) ^ hash(self._convert_to_address(None)) def _to_string(self, maxlen): raise TypeError("string(): %r" % (self,)) class CTypesGenericPrimitive(CTypesData): __slots__ = [] def __eq__(self, other): return self is other def __ne__(self, other): return self is not other def __hash__(self): return object.__hash__(self) def _get_own_repr(self): return repr(self._from_ctypes(self._value)) class CTypesGenericArray(CTypesData): __slots__ = [] @classmethod def _newp(cls, init): return cls(init) def __iter__(self): for i in xrange(len(self)): yield self[i] def _get_own_repr(self): return self._addr_repr(ctypes.addressof(self._blob)) class CTypesGenericPtr(CTypesData): __slots__ = ['_address', '_as_ctype_ptr'] _automatic_casts = False @classmethod def _newp(cls, init): return cls(init) @classmethod def _cast_from(cls, source): if source is None: address = 0 elif isinstance(source, CTypesData): address = source._cast_to_integer() elif isinstance(source, integer_types): address = source else: raise TypeError("bad type for cast to %r: %r" % (cls, type(source).__name__)) return cls._new_pointer_at(address) @classmethod def _new_pointer_at(cls, address): self = cls.__new__(cls) self._address = address self._as_ctype_ptr = ctypes.cast(address, cls._ctype) return self def _get_own_repr(self): try: return self._addr_repr(self._address) except AttributeError: return '???' def _cast_to_integer(self): return self._address def __nonzero__(self): return bool(self._address) def __bool__(self): return bool(self._address) @classmethod def _to_ctypes(cls, value): if not isinstance(value, CTypesData): raise TypeError("unexpected %s object" % type(value).__name__) address = value._convert_to_address(cls) return ctypes.cast(address, cls._ctype) @classmethod def _from_ctypes(cls, ctypes_ptr): address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 return cls._new_pointer_at(address) @classmethod def _initialize(cls, ctypes_ptr, value): if value: ctypes_ptr.contents = cls._to_ctypes(value).contents def _convert_to_address(self, BClass): if (BClass in (self.__class__, None) or BClass._automatic_casts or self._automatic_casts): return self._address else: return CTypesData._convert_to_address(self, BClass) class CTypesBaseStructOrUnion(CTypesData): __slots__ = ['_blob'] @classmethod def _create_ctype_obj(cls, init): # may be overridden raise TypeError("cannot instantiate opaque type %s" % (cls,)) def _get_own_repr(self): return self._addr_repr(ctypes.addressof(self._blob)) @classmethod def _offsetof(cls, fieldname): return getattr(cls._ctype, fieldname).offset def _convert_to_address(self, BClass): if getattr(BClass, '_BItem', None) is self.__class__: return ctypes.addressof(self._blob) else: return CTypesData._convert_to_address(self, BClass) @classmethod def _from_ctypes(cls, ctypes_struct_or_union): self = cls.__new__(cls) self._blob = ctypes_struct_or_union return self @classmethod def _to_ctypes(cls, value): return value._blob def __repr__(self, c_name=None): return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) class CTypesBackend(object): PRIMITIVE_TYPES = { 'char': ctypes.c_char, 'short': ctypes.c_short, 'int': ctypes.c_int, 'long': ctypes.c_long, 'long long': ctypes.c_longlong, 'signed char': ctypes.c_byte, 'unsigned char': ctypes.c_ubyte, 'unsigned short': ctypes.c_ushort, 'unsigned int': ctypes.c_uint, 'unsigned long': ctypes.c_ulong, 'unsigned long long': ctypes.c_ulonglong, 'float': ctypes.c_float, 'double': ctypes.c_double, '_Bool': ctypes.c_bool, } for _name in ['unsigned long long', 'unsigned long', 'unsigned int', 'unsigned short', 'unsigned char']: _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] if _size == ctypes.sizeof(ctypes.c_void_p): PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name] if _size == ctypes.sizeof(ctypes.c_size_t): PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name] for _name in ['long long', 'long', 'int', 'short', 'signed char']: _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] if _size == ctypes.sizeof(ctypes.c_void_p): PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name] PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name] if _size == ctypes.sizeof(ctypes.c_size_t): PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name] def __init__(self): self.RTLD_LAZY = 0 # not supported anyway by ctypes self.RTLD_NOW = 0 self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL self.RTLD_LOCAL = ctypes.RTLD_LOCAL def set_ffi(self, ffi): self.ffi = ffi def _get_types(self): return CTypesData, CTypesType def load_library(self, path, flags=0): cdll = ctypes.CDLL(path, flags) return CTypesLibrary(self, cdll) def new_void_type(self): class CTypesVoid(CTypesData): __slots__ = [] _reftypename = 'void &' @staticmethod def _from_ctypes(novalue): return None @staticmethod def _to_ctypes(novalue): if novalue is not None: raise TypeError("None expected, got %s object" % (type(novalue).__name__,)) return None CTypesVoid._fix_class() return CTypesVoid def new_primitive_type(self, name): if name == 'wchar_t': raise NotImplementedError(name) ctype = self.PRIMITIVE_TYPES[name] if name == 'char': kind = 'char' elif name in ('float', 'double'): kind = 'float' else: if name in ('signed char', 'unsigned char'): kind = 'byte' elif name == '_Bool': kind = 'bool' else: kind = 'int' is_signed = (ctype(-1).value == -1) # def _cast_source_to_int(source): if isinstance(source, (integer_types, float)): source = int(source) elif isinstance(source, CTypesData): source = source._cast_to_integer() elif isinstance(source, bytes): source = ord(source) elif source is None: source = 0 else: raise TypeError("bad type for cast to %r: %r" % (CTypesPrimitive, type(source).__name__)) return source # class CTypesPrimitive(CTypesGenericPrimitive): __slots__ = ['_value'] _ctype = ctype _reftypename = '%s &' % name def __init__(self, value): self._value = value @staticmethod def _create_ctype_obj(init): if init is None: return ctype() return ctype(CTypesPrimitive._to_ctypes(init)) if kind == 'int' or kind == 'byte': @classmethod def _cast_from(cls, source): source = _cast_source_to_int(source) source = ctype(source).value # cast within range return cls(source) def __int__(self): return self._value if kind == 'bool': @classmethod def _cast_from(cls, source): if not isinstance(source, (integer_types, float)): source = _cast_source_to_int(source) return cls(bool(source)) def __int__(self): return self._value if kind == 'char': @classmethod def _cast_from(cls, source): source = _cast_source_to_int(source) source = bytechr(source & 0xFF) return cls(source) def __int__(self): return ord(self._value) if kind == 'float': @classmethod def _cast_from(cls, source): if isinstance(source, float): pass elif isinstance(source, CTypesGenericPrimitive): if hasattr(source, '__float__'): source = float(source) else: source = int(source) else: source = _cast_source_to_int(source) source = ctype(source).value # fix precision return cls(source) def __int__(self): return int(self._value) def __float__(self): return self._value _cast_to_integer = __int__ if kind == 'int' or kind == 'byte' or kind == 'bool': @staticmethod def _to_ctypes(x): if not isinstance(x, integer_types): if isinstance(x, CTypesData): x = int(x) else: raise TypeError("integer expected, got %s" % type(x).__name__) if ctype(x).value != x: if not is_signed and x < 0: raise OverflowError("%s: negative integer" % name) else: raise OverflowError("%s: integer out of bounds" % name) return x if kind == 'char': @staticmethod def _to_ctypes(x): if isinstance(x, bytes) and len(x) == 1: return x if isinstance(x, CTypesPrimitive): # > return x._value raise TypeError("character expected, got %s" % type(x).__name__) if kind == 'float': @staticmethod def _to_ctypes(x): if not isinstance(x, (integer_types, float, CTypesData)): raise TypeError("float expected, got %s" % type(x).__name__) return ctype(x).value @staticmethod def _from_ctypes(value): return getattr(value, 'value', value) @staticmethod def _initialize(blob, init): blob.value = CTypesPrimitive._to_ctypes(init) if kind == 'char': def _to_string(self, maxlen): return self._value if kind == 'byte': def _to_string(self, maxlen): return chr(self._value & 0xff) # CTypesPrimitive._fix_class() return CTypesPrimitive def new_pointer_type(self, BItem): getbtype = self.ffi._get_cached_btype if BItem is getbtype(model.PrimitiveType('char')): kind = 'charp' elif BItem in (getbtype(model.PrimitiveType('signed char')), getbtype(model.PrimitiveType('unsigned char'))): kind = 'bytep' elif BItem is getbtype(model.void_type): kind = 'voidp' else: kind = 'generic' # class CTypesPtr(CTypesGenericPtr): __slots__ = ['_own'] if kind == 'charp': __slots__ += ['__as_strbuf'] _BItem = BItem if hasattr(BItem, '_ctype'): _ctype = ctypes.POINTER(BItem._ctype) _bitem_size = ctypes.sizeof(BItem._ctype) else: _ctype = ctypes.c_void_p if issubclass(BItem, CTypesGenericArray): _reftypename = BItem._get_c_name('(* &)') else: _reftypename = BItem._get_c_name(' * &') def __init__(self, init): ctypeobj = BItem._create_ctype_obj(init) if kind == 'charp': self.__as_strbuf = ctypes.create_string_buffer( ctypeobj.value + b'\x00') self._as_ctype_ptr = ctypes.cast( self.__as_strbuf, self._ctype) else: self._as_ctype_ptr = ctypes.pointer(ctypeobj) self._address = ctypes.cast(self._as_ctype_ptr, ctypes.c_void_p).value self._own = True def __add__(self, other): if isinstance(other, integer_types): return self._new_pointer_at(self._address + other * self._bitem_size) else: return NotImplemented def __sub__(self, other): if isinstance(other, integer_types): return self._new_pointer_at(self._address - other * self._bitem_size) elif type(self) is type(other): return (self._address - other._address) // self._bitem_size else: return NotImplemented def __getitem__(self, index): if getattr(self, '_own', False) and index != 0: raise IndexError return BItem._from_ctypes(self._as_ctype_ptr[index]) def __setitem__(self, index, value): self._as_ctype_ptr[index] = BItem._to_ctypes(value) if kind == 'charp' or kind == 'voidp': @classmethod def _arg_to_ctypes(cls, *value): if value and isinstance(value[0], bytes): return ctypes.c_char_p(value[0]) else: return super(CTypesPtr, cls)._arg_to_ctypes(*value) if kind == 'charp' or kind == 'bytep': def _to_string(self, maxlen): if maxlen < 0: maxlen = sys.maxsize p = ctypes.cast(self._as_ctype_ptr, ctypes.POINTER(ctypes.c_char)) n = 0 while n < maxlen and p[n] != b'\x00': n += 1 return b''.join([p[i] for i in range(n)]) def _get_own_repr(self): if getattr(self, '_own', False): return 'owning %d bytes' % ( ctypes.sizeof(self._as_ctype_ptr.contents),) return super(CTypesPtr, self)._get_own_repr() # if (BItem is self.ffi._get_cached_btype(model.void_type) or BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): CTypesPtr._automatic_casts = True # CTypesPtr._fix_class() return CTypesPtr def new_array_type(self, CTypesPtr, length): if length is None: brackets = ' &[]' else: brackets = ' &[%d]' % length BItem = CTypesPtr._BItem getbtype = self.ffi._get_cached_btype if BItem is getbtype(model.PrimitiveType('char')): kind = 'char' elif BItem in (getbtype(model.PrimitiveType('signed char')), getbtype(model.PrimitiveType('unsigned char'))): kind = 'byte' else: kind = 'generic' # class CTypesArray(CTypesGenericArray): __slots__ = ['_blob', '_own'] if length is not None: _ctype = BItem._ctype * length else: __slots__.append('_ctype') _reftypename = BItem._get_c_name(brackets) _declared_length = length _CTPtr = CTypesPtr def __init__(self, init): if length is None: if isinstance(init, integer_types): len1 = init init = None elif kind == 'char' and isinstance(init, bytes): len1 = len(init) + 1 # extra null else: init = tuple(init) len1 = len(init) self._ctype = BItem._ctype * len1 self._blob = self._ctype() self._own = True if init is not None: self._initialize(self._blob, init) @staticmethod def _initialize(blob, init): if isinstance(init, bytes): init = [init[i:i+1] for i in range(len(init))] else: init = tuple(init) if len(init) > len(blob): raise IndexError("too many initializers") addr = ctypes.cast(blob, ctypes.c_void_p).value PTR = ctypes.POINTER(BItem._ctype) itemsize = ctypes.sizeof(BItem._ctype) for i, value in enumerate(init): p = ctypes.cast(addr + i * itemsize, PTR) BItem._initialize(p.contents, value) def __len__(self): return len(self._blob) def __getitem__(self, index): if not (0 <= index < len(self._blob)): raise IndexError return BItem._from_ctypes(self._blob[index]) def __setitem__(self, index, value): if not (0 <= index < len(self._blob)): raise IndexError self._blob[index] = BItem._to_ctypes(value) if kind == 'char' or kind == 'byte': def _to_string(self, maxlen): if maxlen < 0: maxlen = len(self._blob) p = ctypes.cast(self._blob, ctypes.POINTER(ctypes.c_char)) n = 0 while n < maxlen and p[n] != b'\x00': n += 1 return b''.join([p[i] for i in range(n)]) def _get_own_repr(self): if getattr(self, '_own', False): return 'owning %d bytes' % (ctypes.sizeof(self._blob),) return super(CTypesArray, self)._get_own_repr() def _convert_to_address(self, BClass): if BClass in (CTypesPtr, None) or BClass._automatic_casts: return ctypes.addressof(self._blob) else: return CTypesData._convert_to_address(self, BClass) @staticmethod def _from_ctypes(ctypes_array): self = CTypesArray.__new__(CTypesArray) self._blob = ctypes_array return self @staticmethod def _arg_to_ctypes(value): return CTypesPtr._arg_to_ctypes(value) def __add__(self, other): if isinstance(other, integer_types): return CTypesPtr._new_pointer_at( ctypes.addressof(self._blob) + other * ctypes.sizeof(BItem._ctype)) else: return NotImplemented @classmethod def _cast_from(cls, source): raise NotImplementedError("casting to %r" % ( cls._get_c_name(),)) # CTypesArray._fix_class() return CTypesArray def _new_struct_or_union(self, kind, name, base_ctypes_class): # class struct_or_union(base_ctypes_class): pass struct_or_union.__name__ = '%s_%s' % (kind, name) # class CTypesStructOrUnion(CTypesBaseStructOrUnion): __slots__ = ['_blob'] _ctype = struct_or_union _reftypename = '%s &' % (name,) _kind = kind # CTypesStructOrUnion._fix_class() return CTypesStructOrUnion def new_struct_type(self, name): return self._new_struct_or_union('struct', name, ctypes.Structure) def new_union_type(self, name): return self._new_struct_or_union('union', name, ctypes.Union) def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, totalsize=-1, totalalignment=-1, sflags=0): if totalsize >= 0 or totalalignment >= 0: raise NotImplementedError("the ctypes backend of CFFI does not support " "structures completed by verify(); please " "compile and install the _cffi_backend module.") struct_or_union = CTypesStructOrUnion._ctype fnames = [fname for (fname, BField, bitsize) in fields] btypes = [BField for (fname, BField, bitsize) in fields] bitfields = [bitsize for (fname, BField, bitsize) in fields] # bfield_types = {} cfields = [] for (fname, BField, bitsize) in fields: if bitsize < 0: cfields.append((fname, BField._ctype)) bfield_types[fname] = BField else: cfields.append((fname, BField._ctype, bitsize)) bfield_types[fname] = Ellipsis if sflags & 8: struct_or_union._pack_ = 1 struct_or_union._fields_ = cfields CTypesStructOrUnion._bfield_types = bfield_types # @staticmethod def _create_ctype_obj(init): result = struct_or_union() if init is not None: initialize(result, init) return result CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj # def initialize(blob, init): if is_union: if len(init) > 1: raise ValueError("union initializer: %d items given, but " "only one supported (use a dict if needed)" % (len(init),)) if not isinstance(init, dict): if isinstance(init, (bytes, unicode)): raise TypeError("union initializer: got a str") init = tuple(init) if len(init) > len(fnames): raise ValueError("too many values for %s initializer" % CTypesStructOrUnion._get_c_name()) init = dict(zip(fnames, init)) addr = ctypes.addressof(blob) for fname, value in init.items(): BField, bitsize = name2fieldtype[fname] assert bitsize < 0, \ "not implemented: initializer with bit fields" offset = CTypesStructOrUnion._offsetof(fname) PTR = ctypes.POINTER(BField._ctype) p = ctypes.cast(addr + offset, PTR) BField._initialize(p.contents, value) is_union = CTypesStructOrUnion._kind == 'union' name2fieldtype = dict(zip(fnames, zip(btypes, bitfields))) # for fname, BField, bitsize in fields: if fname == '': raise NotImplementedError("nested anonymous structs/unions") if hasattr(CTypesStructOrUnion, fname): raise ValueError("the field name %r conflicts in " "the ctypes backend" % fname) if bitsize < 0: def getter(self, fname=fname, BField=BField, offset=CTypesStructOrUnion._offsetof(fname), PTR=ctypes.POINTER(BField._ctype)): addr = ctypes.addressof(self._blob) p = ctypes.cast(addr + offset, PTR) return BField._from_ctypes(p.contents) def setter(self, value, fname=fname, BField=BField): setattr(self._blob, fname, BField._to_ctypes(value)) # if issubclass(BField, CTypesGenericArray): setter = None if BField._declared_length == 0: def getter(self, fname=fname, BFieldPtr=BField._CTPtr, offset=CTypesStructOrUnion._offsetof(fname), PTR=ctypes.POINTER(BField._ctype)): addr = ctypes.addressof(self._blob) p = ctypes.cast(addr + offset, PTR) return BFieldPtr._from_ctypes(p) # else: def getter(self, fname=fname, BField=BField): return BField._from_ctypes(getattr(self._blob, fname)) def setter(self, value, fname=fname, BField=BField): # xxx obscure workaround value = BField._to_ctypes(value) oldvalue = getattr(self._blob, fname) setattr(self._blob, fname, value) if value != getattr(self._blob, fname): setattr(self._blob, fname, oldvalue) raise OverflowError("value too large for bitfield") setattr(CTypesStructOrUnion, fname, property(getter, setter)) # CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp)) for fname in fnames: if hasattr(CTypesPtr, fname): raise ValueError("the field name %r conflicts in " "the ctypes backend" % fname) def getter(self, fname=fname): return getattr(self[0], fname) def setter(self, value, fname=fname): setattr(self[0], fname, value) setattr(CTypesPtr, fname, property(getter, setter)) def new_function_type(self, BArgs, BResult, has_varargs): nameargs = [BArg._get_c_name() for BArg in BArgs] if has_varargs: nameargs.append('...') nameargs = ', '.join(nameargs) # class CTypesFunctionPtr(CTypesGenericPtr): __slots__ = ['_own_callback', '_name'] _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None), *[BArg._ctype for BArg in BArgs], use_errno=True) _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,)) def __init__(self, init, error=None): # create a callback to the Python callable init() import traceback assert not has_varargs, "varargs not supported for callbacks" if getattr(BResult, '_ctype', None) is not None: error = BResult._from_ctypes( BResult._create_ctype_obj(error)) else: error = None def callback(*args): args2 = [] for arg, BArg in zip(args, BArgs): args2.append(BArg._from_ctypes(arg)) try: res2 = init(*args2) res2 = BResult._to_ctypes(res2) except: traceback.print_exc() res2 = error if issubclass(BResult, CTypesGenericPtr): if res2: res2 = ctypes.cast(res2, ctypes.c_void_p).value # .value: http://bugs.python.org/issue1574593 else: res2 = None #print repr(res2) return res2 if issubclass(BResult, CTypesGenericPtr): # The only pointers callbacks can return are void*s: # http://bugs.python.org/issue5710 callback_ctype = ctypes.CFUNCTYPE( ctypes.c_void_p, *[BArg._ctype for BArg in BArgs], use_errno=True) else: callback_ctype = CTypesFunctionPtr._ctype self._as_ctype_ptr = callback_ctype(callback) self._address = ctypes.cast(self._as_ctype_ptr, ctypes.c_void_p).value self._own_callback = init @staticmethod def _initialize(ctypes_ptr, value): if value: raise NotImplementedError("ctypes backend: not supported: " "initializers for function pointers") def __repr__(self): c_name = getattr(self, '_name', None) if c_name: i = self._reftypename.index('(* &)') if self._reftypename[i-1] not in ' )*': c_name = ' ' + c_name c_name = self._reftypename.replace('(* &)', c_name) return CTypesData.__repr__(self, c_name) def _get_own_repr(self): if getattr(self, '_own_callback', None) is not None: return 'calling %r' % (self._own_callback,) return super(CTypesFunctionPtr, self)._get_own_repr() def __call__(self, *args): if has_varargs: assert len(args) >= len(BArgs) extraargs = args[len(BArgs):] args = args[:len(BArgs)] else: assert len(args) == len(BArgs) ctypes_args = [] for arg, BArg in zip(args, BArgs): ctypes_args.append(BArg._arg_to_ctypes(arg)) if has_varargs: for i, arg in enumerate(extraargs): if arg is None: ctypes_args.append(ctypes.c_void_p(0)) # NULL continue if not isinstance(arg, CTypesData): raise TypeError( "argument %d passed in the variadic part " "needs to be a cdata object (got %s)" % (1 + len(BArgs) + i, type(arg).__name__)) ctypes_args.append(arg._arg_to_ctypes(arg)) result = self._as_ctype_ptr(*ctypes_args) return BResult._from_ctypes(result) # CTypesFunctionPtr._fix_class() return CTypesFunctionPtr def new_enum_type(self, name, enumerators, enumvalues, CTypesInt): assert isinstance(name, str) reverse_mapping = dict(zip(reversed(enumvalues), reversed(enumerators))) # class CTypesEnum(CTypesInt): __slots__ = [] _reftypename = '%s &' % name def _get_own_repr(self): value = self._value try: return '%d: %s' % (value, reverse_mapping[value]) except KeyError: return str(value) def _to_string(self, maxlen): value = self._value try: return reverse_mapping[value] except KeyError: return str(value) # CTypesEnum._fix_class() return CTypesEnum def get_errno(self): return ctypes.get_errno() def set_errno(self, value): ctypes.set_errno(value) def string(self, b, maxlen=-1): return b._to_string(maxlen) def buffer(self, bptr, size=-1): raise NotImplementedError("buffer() with ctypes backend") def sizeof(self, cdata_or_BType): if isinstance(cdata_or_BType, CTypesData): return cdata_or_BType._get_size_of_instance() else: assert issubclass(cdata_or_BType, CTypesData) return cdata_or_BType._get_size() def alignof(self, BType): assert issubclass(BType, CTypesData) return BType._alignment() def newp(self, BType, source): if not issubclass(BType, CTypesData): raise TypeError return BType._newp(source) def cast(self, BType, source): return BType._cast_from(source) def callback(self, BType, source, error): return BType(source, error) typeof = type def getcname(self, BType, replace_with): return BType._get_c_name(replace_with) def typeoffsetof(self, BType, fieldname): if fieldname is not None and issubclass(BType, CTypesGenericPtr): BType = BType._BItem if not issubclass(BType, CTypesBaseStructOrUnion): raise TypeError("expected a struct or union ctype") if fieldname is None: return (BType, 0) else: BField = BType._bfield_types[fieldname] if BField is Ellipsis: raise TypeError("not supported for bitfields") return (BField, BType._offsetof(fieldname)) def rawaddressof(self, BTypePtr, cdata, offset): if isinstance(cdata, CTypesBaseStructOrUnion): ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) elif isinstance(cdata, CTypesGenericPtr): ptr = type(cdata)._to_ctypes(cdata) else: raise TypeError("expected a ") if offset != 0: ptr = ctypes.cast( ctypes.c_void_p( ctypes.cast(ptr, ctypes.c_void_p).value + offset), type(ptr)) return BTypePtr._from_ctypes(ptr) class CTypesLibrary(object): def __init__(self, backend, cdll): self.backend = backend self.cdll = cdll def load_function(self, BType, name): c_func = getattr(self.cdll, name) funcobj = BType._from_ctypes(c_func) funcobj._name = name return funcobj def read_variable(self, BType, name): try: ctypes_obj = BType._ctype.in_dll(self.cdll, name) except AttributeError as e: raise NotImplementedError(e) return BType._from_ctypes(ctypes_obj) def write_variable(self, BType, name, value): new_ctypes_obj = BType._to_ctypes(value) ctypes_obj = BType._ctype.in_dll(self.cdll, name) ctypes.memmove(ctypes.addressof(ctypes_obj), ctypes.addressof(new_ctypes_obj), ctypes.sizeof(BType._ctype)) cffi-0.8.2/cffi/gc_weakref.py0000644000076400001440000000111212237455272016276 0ustar arigousers00000000000000from weakref import ref class GcWeakrefs(object): # code copied and adapted from WeakKeyDictionary. def __init__(self, ffi): self.ffi = ffi self.data = data = {} def remove(k): destructor, cdata = data.pop(k) destructor(cdata) self.remove = remove def build(self, cdata, destructor): # make a new cdata of the same type as the original one new_cdata = self.ffi.cast(self.ffi._backend.typeof(cdata), cdata) self.data[ref(new_cdata, self.remove)] = destructor, cdata return new_cdata cffi-0.8.2/cffi/__init__.py0000644000076400001440000000040112306266214015731 0ustar arigousers00000000000000__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', 'FFIError'] from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing __version__ = "0.8.2" __version_info__ = (0, 8, 2) cffi-0.8.2/cffi/model.py0000644000076400001440000004206312306266214015304 0ustar arigousers00000000000000import types import weakref from .lock import allocate_lock class BaseTypeByIdentity(object): is_array_type = False is_raw_function = False def get_c_name(self, replace_with='', context='a C file'): result = self.c_name_with_marker assert result.count('&') == 1 # some logic duplication with ffi.getctype()... :-( replace_with = replace_with.strip() if replace_with: if replace_with.startswith('*') and '&[' in result: replace_with = '(%s)' % replace_with elif not replace_with[0] in '[(': replace_with = ' ' + replace_with result = result.replace('&', replace_with) if '$' in result: from .ffiplatform import VerificationError raise VerificationError( "cannot generate '%s' in %s: unknown type name" % (self._get_c_name(), context)) return result def _get_c_name(self): return self.c_name_with_marker.replace('&', '') def has_c_name(self): return '$' not in self._get_c_name() def get_cached_btype(self, ffi, finishlist, can_delay=False): try: BType = ffi._cached_btypes[self] except KeyError: BType = self.build_backend_type(ffi, finishlist) BType2 = ffi._cached_btypes.setdefault(self, BType) assert BType2 is BType return BType def __repr__(self): return '<%s>' % (self._get_c_name(),) def _get_items(self): return [(name, getattr(self, name)) for name in self._attrs_] class BaseType(BaseTypeByIdentity): def __eq__(self, other): return (self.__class__ == other.__class__ and self._get_items() == other._get_items()) def __ne__(self, other): return not self == other def __hash__(self): return hash((self.__class__, tuple(self._get_items()))) class VoidType(BaseType): _attrs_ = () def __init__(self): self.c_name_with_marker = 'void&' def build_backend_type(self, ffi, finishlist): return global_cache(self, ffi, 'new_void_type') void_type = VoidType() class PrimitiveType(BaseType): _attrs_ = ('name',) ALL_PRIMITIVE_TYPES = { 'char': 'c', 'short': 'i', 'int': 'i', 'long': 'i', 'long long': 'i', 'signed char': 'i', 'unsigned char': 'i', 'unsigned short': 'i', 'unsigned int': 'i', 'unsigned long': 'i', 'unsigned long long': 'i', 'float': 'f', 'double': 'f', 'long double': 'f', '_Bool': 'i', # the following types are not primitive in the C sense 'wchar_t': 'c', 'int8_t': 'i', 'uint8_t': 'i', 'int16_t': 'i', 'uint16_t': 'i', 'int32_t': 'i', 'uint32_t': 'i', 'int64_t': 'i', 'uint64_t': 'i', 'intptr_t': 'i', 'uintptr_t': 'i', 'ptrdiff_t': 'i', 'size_t': 'i', 'ssize_t': 'i', } def __init__(self, name): assert name in self.ALL_PRIMITIVE_TYPES self.name = name self.c_name_with_marker = name + '&' def is_char_type(self): return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' def is_integer_type(self): return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' def is_float_type(self): return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' def build_backend_type(self, ffi, finishlist): return global_cache(self, ffi, 'new_primitive_type', self.name) class BaseFunctionType(BaseType): _attrs_ = ('args', 'result', 'ellipsis') def __init__(self, args, result, ellipsis): self.args = args self.result = result self.ellipsis = ellipsis # reprargs = [arg._get_c_name() for arg in self.args] if self.ellipsis: reprargs.append('...') reprargs = reprargs or ['void'] replace_with = self._base_pattern % (', '.join(reprargs),) self.c_name_with_marker = ( self.result.c_name_with_marker.replace('&', replace_with)) class RawFunctionType(BaseFunctionType): # Corresponds to a C type like 'int(int)', which is the C type of # a function, but not a pointer-to-function. The backend has no # notion of such a type; it's used temporarily by parsing. _base_pattern = '(&)(%s)' is_raw_function = True def build_backend_type(self, ffi, finishlist): from . import api raise api.CDefError("cannot render the type %r: it is a function " "type, not a pointer-to-function type" % (self,)) def as_function_pointer(self): return FunctionPtrType(self.args, self.result, self.ellipsis) class FunctionPtrType(BaseFunctionType): _base_pattern = '(*&)(%s)' def build_backend_type(self, ffi, finishlist): result = self.result.get_cached_btype(ffi, finishlist) args = [] for tp in self.args: args.append(tp.get_cached_btype(ffi, finishlist)) return global_cache(self, ffi, 'new_function_type', tuple(args), result, self.ellipsis) class PointerType(BaseType): _attrs_ = ('totype',) _base_pattern = " *&" _base_pattern_array = "(*&)" def __init__(self, totype): self.totype = totype if totype.is_array_type: extra = self._base_pattern_array else: extra = self._base_pattern self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra) def build_backend_type(self, ffi, finishlist): BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True) return global_cache(self, ffi, 'new_pointer_type', BItem) voidp_type = PointerType(void_type) class ConstPointerType(PointerType): _base_pattern = " const *&" _base_pattern_array = "(const *&)" const_voidp_type = ConstPointerType(void_type) class NamedPointerType(PointerType): _attrs_ = ('totype', 'name') def __init__(self, totype, name): PointerType.__init__(self, totype) self.name = name self.c_name_with_marker = name + '&' class ArrayType(BaseType): _attrs_ = ('item', 'length') is_array_type = True def __init__(self, item, length): self.item = item self.length = length # if length is None: brackets = '&[]' elif length == '...': brackets = '&[/*...*/]' else: brackets = '&[%d]' % length self.c_name_with_marker = ( self.item.c_name_with_marker.replace('&', brackets)) def resolve_length(self, newlength): return ArrayType(self.item, newlength) def build_backend_type(self, ffi, finishlist): if self.length == '...': from . import api raise api.CDefError("cannot render the type %r: unknown length" % (self,)) self.item.get_cached_btype(ffi, finishlist) # force the item BType BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) class StructOrUnionOrEnum(BaseTypeByIdentity): _attrs_ = ('name',) forcename = None def build_c_name_with_marker(self): name = self.forcename or '%s %s' % (self.kind, self.name) self.c_name_with_marker = name + '&' def force_the_name(self, forcename): self.forcename = forcename self.build_c_name_with_marker() def get_official_name(self): assert self.c_name_with_marker.endswith('&') return self.c_name_with_marker[:-1] class StructOrUnion(StructOrUnionOrEnum): fixedlayout = None completed = False partial = False packed = False def __init__(self, name, fldnames, fldtypes, fldbitsize): self.name = name self.fldnames = fldnames self.fldtypes = fldtypes self.fldbitsize = fldbitsize self.build_c_name_with_marker() def enumfields(self): for name, type, bitsize in zip(self.fldnames, self.fldtypes, self.fldbitsize): if name == '' and isinstance(type, StructOrUnion): # nested anonymous struct/union for result in type.enumfields(): yield result else: yield (name, type, bitsize) def force_flatten(self): # force the struct or union to have a declaration that lists # directly all fields returned by enumfields(), flattening # nested anonymous structs/unions. names = [] types = [] bitsizes = [] for name, type, bitsize in self.enumfields(): names.append(name) types.append(type) bitsizes.append(bitsize) self.fldnames = tuple(names) self.fldtypes = tuple(types) self.fldbitsize = tuple(bitsizes) def get_cached_btype(self, ffi, finishlist, can_delay=False): BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist, can_delay) if not can_delay: self.finish_backend_type(ffi, finishlist) return BType def finish_backend_type(self, ffi, finishlist): if self.completed: if self.completed != 2: raise NotImplementedError("recursive structure declaration " "for '%s'" % (self.name,)) return BType = ffi._cached_btypes[self] if self.fldtypes is None: return # not completing it: it's an opaque struct # self.completed = 1 # if self.fixedlayout is None: fldtypes = [tp.get_cached_btype(ffi, finishlist) for tp in self.fldtypes] lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) sflags = 0 if self.packed: sflags = 8 # SF_PACKED ffi._backend.complete_struct_or_union(BType, lst, self, -1, -1, sflags) # else: fldtypes = [] fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout for i in range(len(self.fldnames)): fsize = fieldsize[i] ftype = self.fldtypes[i] # if isinstance(ftype, ArrayType) and ftype.length == '...': # fix the length to match the total size BItemType = ftype.item.get_cached_btype(ffi, finishlist) nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) if nrest != 0: self._verification_error( "field '%s.%s' has a bogus size?" % ( self.name, self.fldnames[i] or '{}')) ftype = ftype.resolve_length(nlen) self.fldtypes = (self.fldtypes[:i] + (ftype,) + self.fldtypes[i+1:]) # BFieldType = ftype.get_cached_btype(ffi, finishlist) if isinstance(ftype, ArrayType) and ftype.length is None: assert fsize == 0 else: bitemsize = ffi.sizeof(BFieldType) if bitemsize != fsize: self._verification_error( "field '%s.%s' is declared as %d bytes, but is " "really %d bytes" % (self.name, self.fldnames[i] or '{}', bitemsize, fsize)) fldtypes.append(BFieldType) # lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)) ffi._backend.complete_struct_or_union(BType, lst, self, totalsize, totalalignment) self.completed = 2 def _verification_error(self, msg): from .ffiplatform import VerificationError raise VerificationError(msg) def check_not_partial(self): if self.partial and self.fixedlayout is None: from . import ffiplatform raise ffiplatform.VerificationMissing(self._get_c_name()) def build_backend_type(self, ffi, finishlist): self.check_not_partial() finishlist.append(self) # return global_cache(self, ffi, 'new_%s_type' % self.kind, self.get_official_name(), key=self) class StructType(StructOrUnion): kind = 'struct' class UnionType(StructOrUnion): kind = 'union' class EnumType(StructOrUnionOrEnum): kind = 'enum' partial = False partial_resolved = False def __init__(self, name, enumerators, enumvalues, baseinttype=None): self.name = name self.enumerators = enumerators self.enumvalues = enumvalues self.baseinttype = baseinttype self.build_c_name_with_marker() def force_the_name(self, forcename): StructOrUnionOrEnum.force_the_name(self, forcename) if self.forcename is None: name = self.get_official_name() self.forcename = '$' + name.replace(' ', '_') def check_not_partial(self): if self.partial and not self.partial_resolved: from . import ffiplatform raise ffiplatform.VerificationMissing(self._get_c_name()) def build_backend_type(self, ffi, finishlist): self.check_not_partial() base_btype = self.build_baseinttype(ffi, finishlist) return global_cache(self, ffi, 'new_enum_type', self.get_official_name(), self.enumerators, self.enumvalues, base_btype, key=self) def build_baseinttype(self, ffi, finishlist): if self.baseinttype is not None: return self.baseinttype.get_cached_btype(ffi, finishlist) # if self.enumvalues: smallest_value = min(self.enumvalues) largest_value = max(self.enumvalues) else: smallest_value = 0 largest_value = 0 if smallest_value < 0: # needs a signed type sign = 1 candidate1 = PrimitiveType("int") candidate2 = PrimitiveType("long") else: sign = 0 candidate1 = PrimitiveType("unsigned int") candidate2 = PrimitiveType("unsigned long") btype1 = candidate1.get_cached_btype(ffi, finishlist) btype2 = candidate2.get_cached_btype(ffi, finishlist) size1 = ffi.sizeof(btype1) size2 = ffi.sizeof(btype2) if (smallest_value >= ((-1) << (8*size1-1)) and largest_value < (1 << (8*size1-sign))): return btype1 if (smallest_value >= ((-1) << (8*size2-1)) and largest_value < (1 << (8*size2-sign))): return btype2 raise api.CDefError("%s values don't all fit into either 'long' " "or 'unsigned long'" % self._get_c_name()) def unknown_type(name, structname=None): if structname is None: structname = '$%s' % name tp = StructType(structname, None, None, None) tp.force_the_name(name) return tp def unknown_ptr_type(name, structname=None): if structname is None: structname = '*$%s' % name tp = StructType(structname, None, None, None) return NamedPointerType(tp, name) global_lock = allocate_lock() def global_cache(srctype, ffi, funcname, *args, **kwds): key = kwds.pop('key', (funcname, args)) assert not kwds try: return ffi._backend.__typecache[key] except KeyError: pass except AttributeError: # initialize the __typecache attribute, either at the module level # if ffi._backend is a module, or at the class level if ffi._backend # is some instance. if isinstance(ffi._backend, types.ModuleType): ffi._backend.__typecache = weakref.WeakValueDictionary() else: type(ffi._backend).__typecache = weakref.WeakValueDictionary() try: res = getattr(ffi._backend, funcname)(*args) except NotImplementedError as e: raise NotImplementedError("%r: %s" % (srctype, e)) # note that setdefault() on WeakValueDictionary is not atomic # and contains a rare bug (http://bugs.python.org/issue19542); # we have to use a lock and do it ourselves cache = ffi._backend.__typecache with global_lock: res1 = cache.get(key) if res1 is None: cache[key] = res return res else: return res1 def pointer_cache(ffi, BType): return global_cache('?', ffi, 'new_pointer_type', BType) def attach_exception_info(e, name): if e.args and type(e.args[0]) is str: e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:] cffi-0.8.2/cffi/commontypes.py0000644000076400001440000001640612176750421016566 0ustar arigousers00000000000000import 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: if commontype == result: raise api.FFIError("Unsupported type: %r. Please file a bug " "if you think it should be." % (commontype,)) 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)) cffi-0.8.2/cffi/vengine_gen.py0000644000076400001440000005403212237663306016475 0ustar arigousers00000000000000import sys, os import types from . import model, ffiplatform class VGenericEngine(object): _class_key = 'g' _gen_python_module = False def __init__(self, verifier): self.verifier = verifier self.ffi = verifier.ffi self.export_symbols = [] self._struct_pending_verification = {} def patch_extension_kwds(self, kwds): # add 'export_symbols' to the dictionary. Note that we add the # list before filling it. When we fill it, it will thus also show # up in kwds['export_symbols']. kwds.setdefault('export_symbols', self.export_symbols) def find_module(self, module_name, path, so_suffixes): for so_suffix in so_suffixes: basename = module_name + so_suffix if path is None: path = sys.path for dirname in path: filename = os.path.join(dirname, basename) if os.path.isfile(filename): return filename def collect_types(self): pass # not needed in the generic engine def _prnt(self, what=''): self._f.write(what + '\n') def write_source_to_f(self): prnt = self._prnt # first paste some standard set of lines that are mostly '#include' prnt(cffimod_header) # then paste the C source given by the user, verbatim. prnt(self.verifier.preamble) # # call generate_gen_xxx_decl(), for every xxx found from # ffi._parser._declarations. This generates all the functions. self._generate('decl') # # on Windows, distutils insists on putting init_cffi_xyz in # 'export_symbols', so instead of fighting it, just give up and # give it one if sys.platform == 'win32': if sys.version_info >= (3,): prefix = 'PyInit_' else: prefix = 'init' modname = self.verifier.get_module_name() prnt("void %s%s(void) { }\n" % (prefix, modname)) def load_library(self): # import it with the CFFI backend backend = self.ffi._backend # needs to make a path that contains '/', on Posix filename = os.path.join(os.curdir, self.verifier.modulefilename) module = backend.load_library(filename) # # call loading_gen_struct() to get the struct layout inferred by # the C compiler self._load(module, 'loading') # build the FFILibrary class and instance, this is a module subclass # because modules are expected to have usually-constant-attributes and # in PyPy this means the JIT is able to treat attributes as constant, # which we want. class FFILibrary(types.ModuleType): _cffi_generic_module = module _cffi_ffi = self.ffi _cffi_dir = [] def __dir__(self): return FFILibrary._cffi_dir library = FFILibrary("") # # finally, call the loaded_gen_xxx() functions. This will set # up the 'library' object. self._load(module, 'loaded', library=library) return library def _get_declarations(self): return sorted(self.ffi._parser._declarations.items()) def _generate(self, step_name): for name, tp in self._get_declarations(): kind, realname = name.split(' ', 1) try: method = getattr(self, '_generate_gen_%s_%s' % (kind, step_name)) except AttributeError: raise ffiplatform.VerificationError( "not implemented in verify(): %r" % name) try: method(tp, realname) except Exception as e: model.attach_exception_info(e, name) raise def _load(self, module, step_name, **kwds): for name, tp in self._get_declarations(): kind, realname = name.split(' ', 1) method = getattr(self, '_%s_gen_%s' % (step_name, kind)) try: method(tp, realname, module, **kwds) except Exception as e: model.attach_exception_info(e, name) raise def _generate_nothing(self, tp, name): pass def _loaded_noop(self, tp, name, module, **kwds): pass # ---------- # typedefs: generates no code so far _generate_gen_typedef_decl = _generate_nothing _loading_gen_typedef = _loaded_noop _loaded_gen_typedef = _loaded_noop # ---------- # function declarations def _generate_gen_function_decl(self, tp, name): assert isinstance(tp, model.FunctionPtrType) if tp.ellipsis: # cannot support vararg functions better than this: check for its # exact type (including the fixed arguments), and build it as a # constant function pointer (no _cffi_f_%s wrapper) self._generate_gen_const(False, name, tp) return prnt = self._prnt numargs = len(tp.args) argnames = [] for i, type in enumerate(tp.args): indirection = '' if isinstance(type, model.StructOrUnion): indirection = '*' argnames.append('%sx%d' % (indirection, i)) context = 'argument of %s' % name arglist = [type.get_c_name(' %s' % arg, context) for type, arg in zip(tp.args, argnames)] arglist = ', '.join(arglist) or 'void' wrappername = '_cffi_f_%s' % name self.export_symbols.append(wrappername) funcdecl = ' %s(%s)' % (wrappername, arglist) context = 'result of %s' % name prnt(tp.result.get_c_name(funcdecl, context)) prnt('{') # if not isinstance(tp.result, model.VoidType): result_code = 'return ' else: result_code = '' prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames))) prnt('}') prnt() _loading_gen_function = _loaded_noop def _loaded_gen_function(self, tp, name, module, library): assert isinstance(tp, model.FunctionPtrType) if tp.ellipsis: newfunction = self._load_constant(False, tp, name, module) else: indirections = [] base_tp = tp if any(isinstance(typ, model.StructOrUnion) for typ in tp.args): indirect_args = [] for i, typ in enumerate(tp.args): if isinstance(typ, model.StructOrUnion): typ = model.PointerType(typ) indirections.append((i, typ)) indirect_args.append(typ) tp = model.FunctionPtrType(tuple(indirect_args), tp.result, tp.ellipsis) BFunc = self.ffi._get_cached_btype(tp) wrappername = '_cffi_f_%s' % name newfunction = module.load_function(BFunc, wrappername) for i, typ in indirections: newfunction = self._make_struct_wrapper(newfunction, i, typ, base_tp) setattr(library, name, newfunction) type(library)._cffi_dir.append(name) def _make_struct_wrapper(self, oldfunc, i, tp, base_tp): backend = self.ffi._backend BType = self.ffi._get_cached_btype(tp) def newfunc(*args): args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:] return oldfunc(*args) newfunc._cffi_base_type = base_tp return newfunc # ---------- # named structs def _generate_gen_struct_decl(self, tp, name): assert name == tp.name self._generate_struct_or_union_decl(tp, 'struct', name) def _loading_gen_struct(self, tp, name, module): self._loading_struct_or_union(tp, 'struct', name, module) def _loaded_gen_struct(self, tp, name, module, **kwds): self._loaded_struct_or_union(tp) def _generate_gen_union_decl(self, tp, name): assert name == tp.name self._generate_struct_or_union_decl(tp, 'union', name) def _loading_gen_union(self, tp, name, module): self._loading_struct_or_union(tp, 'union', name, module) def _loaded_gen_union(self, tp, name, module, **kwds): self._loaded_struct_or_union(tp) def _generate_struct_or_union_decl(self, tp, prefix, name): if tp.fldnames is None: return # nothing to do with opaque structs checkfuncname = '_cffi_check_%s_%s' % (prefix, name) layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) cname = ('%s %s' % (prefix, name)).strip() # prnt = self._prnt prnt('static void %s(%s *p)' % (checkfuncname, cname)) prnt('{') prnt(' /* only to generate compile-time warnings or errors */') for fname, ftype, fbitsize in tp.enumfields(): if (isinstance(ftype, model.PrimitiveType) and ftype.is_integer_type()) or fbitsize >= 0: # accept all integers, but complain on float or double prnt(' (void)((p->%s) << 1);' % fname) else: # only accept exactly the type declared. try: prnt(' { %s = &p->%s; (void)tmp; }' % ( ftype.get_c_name('*tmp', 'field %r'%fname), fname)) except ffiplatform.VerificationError as e: prnt(' /* %s */' % str(e)) # cannot verify it, ignore prnt('}') self.export_symbols.append(layoutfuncname) prnt('ssize_t %s(ssize_t i)' % (layoutfuncname,)) prnt('{') prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) prnt(' static ssize_t nums[] = {') prnt(' sizeof(%s),' % cname) prnt(' offsetof(struct _cffi_aligncheck, y),') for fname, ftype, fbitsize in tp.enumfields(): if fbitsize >= 0: continue # xxx ignore fbitsize for now prnt(' offsetof(%s, %s),' % (cname, fname)) if isinstance(ftype, model.ArrayType) and ftype.length is None: prnt(' 0, /* %s */' % ftype._get_c_name()) else: prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) prnt(' -1') prnt(' };') prnt(' return nums[i];') prnt(' /* the next line is not executed, but compiled */') prnt(' %s(0);' % (checkfuncname,)) prnt('}') prnt() def _loading_struct_or_union(self, tp, prefix, name, module): if tp.fldnames is None: return # nothing to do with opaque structs layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) # BFunc = self.ffi._typeof_locked("ssize_t(*)(ssize_t)")[0] function = module.load_function(BFunc, layoutfuncname) layout = [] num = 0 while True: x = function(num) if x < 0: break layout.append(x) num += 1 if isinstance(tp, model.StructOrUnion) and tp.partial: # use the function()'s sizes and offsets to guide the # layout of the struct totalsize = layout[0] totalalignment = layout[1] fieldofs = layout[2::2] fieldsize = layout[3::2] tp.force_flatten() assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment else: cname = ('%s %s' % (prefix, name)).strip() self._struct_pending_verification[tp] = layout, cname def _loaded_struct_or_union(self, tp): if tp.fldnames is None: return # nothing to do with opaque structs self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered if tp in self._struct_pending_verification: # check that the layout sizes and offsets match the real ones def check(realvalue, expectedvalue, msg): if realvalue != expectedvalue: raise ffiplatform.VerificationError( "%s (we have %d, but C compiler says %d)" % (msg, expectedvalue, realvalue)) ffi = self.ffi BStruct = ffi._get_cached_btype(tp) layout, cname = self._struct_pending_verification.pop(tp) check(layout[0], ffi.sizeof(BStruct), "wrong total size") check(layout[1], ffi.alignof(BStruct), "wrong total alignment") i = 2 for fname, ftype, fbitsize in tp.enumfields(): if fbitsize >= 0: continue # xxx ignore fbitsize for now check(layout[i], ffi.offsetof(BStruct, fname), "wrong offset for field %r" % (fname,)) if layout[i+1] != 0: BField = ffi._get_cached_btype(ftype) check(layout[i+1], ffi.sizeof(BField), "wrong size for field %r" % (fname,)) i += 2 assert i == len(layout) # ---------- # 'anonymous' declarations. These are produced for anonymous structs # or unions; the 'name' is obtained by a typedef. def _generate_gen_anonymous_decl(self, 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): 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): 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' def _generate_gen_const(self, is_int, name, tp=None, category='const'): prnt = self._prnt funcname = '_cffi_%s_%s' % (category, name) self.export_symbols.append(funcname) if is_int: assert category == 'const' prnt('int %s(long long *out_value)' % funcname) prnt('{') prnt(' *out_value = (long long)(%s);' % (name,)) prnt(' return (%s) <= 0;' % (name,)) prnt('}') else: assert tp is not None prnt(tp.get_c_name(' %s(void)' % funcname, name),) prnt('{') if category == 'var': ampersand = '&' else: ampersand = '' prnt(' return (%s%s);' % (ampersand, name)) prnt('}') prnt() def _generate_gen_constant_decl(self, tp, name): is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() self._generate_gen_const(is_int, name, tp) _loading_gen_constant = _loaded_noop def _load_constant(self, is_int, tp, name, module): funcname = '_cffi_const_%s' % name if is_int: BType = self.ffi._typeof_locked("long long*")[0] BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0] function = module.load_function(BFunc, funcname) p = self.ffi.new(BType) negative = function(p) value = int(p[0]) if value < 0 and not negative: BLongLong = self.ffi._typeof_locked("long long")[0] value += (1 << (8*self.ffi.sizeof(BLongLong))) else: BFunc = self.ffi._typeof_locked(tp.get_c_name('(*)(void)', name))[0] function = module.load_function(BFunc, funcname) value = function() return value def _loaded_gen_constant(self, tp, name, module, library): is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() value = self._load_constant(is_int, tp, name, module) setattr(library, name, value) type(library)._cffi_dir.append(name) # ---------- # enums 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_e_%s_%s' % (prefix, name) self.export_symbols.append(funcname) prnt = self._prnt prnt('int %s(char *out_error)' % funcname) prnt('{') for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): if enumvalue < 0: prnt(' if ((%s) >= 0 || (long)(%s) != %dL) {' % ( enumerator, enumerator, enumvalue)) else: prnt(' if ((%s) < 0 || (unsigned long)(%s) != %dUL) {' % ( enumerator, enumerator, enumvalue)) prnt(' char buf[64];') prnt(' if ((%s) < 0)' % enumerator) prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % enumerator) prnt(' else') prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % enumerator) prnt(' snprintf(out_error, 255,' ' "%s has the real value %s, not %s",') prnt(' "%s", buf, "%d");' % ( enumerator, enumvalue)) prnt(' return -1;') prnt(' }') prnt(' return 0;') prnt('}') prnt() 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.enumvalues = tuple(enumvalues) tp.partial_resolved = True else: BType = self.ffi._typeof_locked("char[]")[0] BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] funcname = '_cffi_e_%s_%s' % (prefix, name) function = module.load_function(BFunc, funcname) p = self.ffi.new(BType, 256) if function(p) < 0: error = self.ffi.string(p) if sys.version_info >= (3,): error = str(error, 'utf-8') raise ffiplatform.VerificationError(error) def _loaded_gen_enum(self, tp, name, module, library): for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): setattr(library, enumerator, enumvalue) type(library)._cffi_dir.append(enumerator) # ---------- # macros: for now only for integers def _generate_gen_macro_decl(self, tp, name): assert tp == '...' self._generate_gen_const(True, name) _loading_gen_macro = _loaded_noop def _loaded_gen_macro(self, tp, name, module, library): value = self._load_constant(True, tp, name, module) setattr(library, name, value) type(library)._cffi_dir.append(name) # ---------- # global variables def _generate_gen_variable_decl(self, tp, name): if isinstance(tp, model.ArrayType): if tp.length == '...': prnt = self._prnt funcname = '_cffi_sizeof_%s' % (name,) self.export_symbols.append(funcname) prnt("size_t %s(void)" % funcname) prnt("{") prnt(" return sizeof(%s);" % (name,)) prnt("}") tp_ptr = model.PointerType(tp.item) self._generate_gen_const(False, name, tp_ptr) else: tp_ptr = model.PointerType(tp) self._generate_gen_const(False, name, tp_ptr, category='var') _loading_gen_variable = _loaded_noop def _loaded_gen_variable(self, tp, name, module, library): if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the # sense that "a=..." is forbidden if tp.length == '...': funcname = '_cffi_sizeof_%s' % (name,) BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0] function = module.load_function(BFunc, funcname) size = function() BItemType = self.ffi._get_cached_btype(tp.item) length, rest = divmod(size, self.ffi.sizeof(BItemType)) if rest != 0: raise ffiplatform.VerificationError( "bad size: %r does not seem to be an array of %s" % (name, tp.item)) tp = tp.resolve_length(length) tp_ptr = model.PointerType(tp.item) value = self._load_constant(False, tp_ptr, name, module) # 'value' is a which we have to replace with # a if the N is actually known if tp.length is not None: BArray = self.ffi._get_cached_btype(tp) value = self.ffi.cast(BArray, value) setattr(library, name, value) type(library)._cffi_dir.append(name) return # remove ptr= from the library instance, and replace # it by a property on the class, which reads/writes into ptr[0]. funcname = '_cffi_var_%s' % name BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0] function = module.load_function(BFunc, funcname) ptr = function() def getter(library): return ptr[0] def setter(library, value): ptr[0] = value setattr(type(library), name, property(getter, setter)) type(library)._cffi_dir.append(name) cffimod_header = r''' #include #include #include #include #include /* XXX for ssize_t on some platforms */ #ifdef _WIN32 # include # define snprintf _snprintf typedef __int8 int8_t; typedef __int16 int16_t; typedef __int32 int32_t; typedef __int64 int64_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; typedef SSIZE_T ssize_t; typedef unsigned char _Bool; #else # include #endif '''