cffi-1.5.2/0000775000175000017500000000000012657646372012676 5ustar arigoarigo00000000000000cffi-1.5.2/setup.py0000664000175000017500000001566312657646311014414 0ustar arigoarigo00000000000000import 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 not in [errno.ENOENT, errno.EACCES]: raise else: t = p.stdout.read().decode().strip() p.stdout.close() 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 no_working_compiler_found(): sys.stderr.write(""" No working compiler found, or bogus compiler options passed to the compiler from Python's distutils module. See the error messages above. (If they are about -mno-fused-madd and you are on OS/X 10.8, see http://stackoverflow.com/questions/22313407/ .)\n""") sys.exit(1) def ask_supports_thread(): from distutils.core import Distribution from distutils.sysconfig import get_config_vars get_config_vars() # workaround for a bug of distutils, e.g. on OS/X config = Distribution().get_command_obj('config') ok = config.try_compile('__thread int some_threadlocal_variable_42;') if ok: define_macros.append(('USE__THREAD', None)) else: ok1 = config.try_compile('int some_regular_variable_42;') if not ok1: no_working_compiler_found() sys.stderr.write("Note: will not use '__thread' in the C code\n") sys.stderr.write("The above error message can be safely ignored\n") def use_pkg_config(): if sys.platform == 'darwin' and os.path.exists('/usr/local/bin/brew'): use_homebrew_for_libffi() _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') def use_homebrew_for_libffi(): # We can build by setting: # PKG_CONFIG_PATH = $(brew --prefix libffi)/lib/pkgconfig with os.popen('brew --prefix libffi') as brew_prefix_cmd: prefix = brew_prefix_cmd.read().strip() pkgconfig = os.path.join(prefix, 'lib', 'pkgconfig') os.environ['PKG_CONFIG_PATH'] = ( os.environ.get('PKG_CONFIG_PATH', '') + ':' + pkgconfig) 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 'freebsd' in sys.platform: include_dirs.append('/usr/local/include') if __name__ == '__main__': from setuptools import setup, Distribution, Extension class CFFIDistribution(Distribution): def has_ext_modules(self): # Event if we don't have extension modules (e.g. on PyPy) we want to # claim that we do so that wheels get properly tagged as Python # specific. (thanks dstufft!) return True # On PyPy, cffi is preinstalled and it is not possible, at least for now, # to install a different version. We work around it by making the setup() # arguments mostly empty in this case. cpython = ('_cffi_backend' not in sys.builtin_module_names) 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='1.5.2', packages=['cffi'] if cpython else [], package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', '_embedding.h']} if cpython else {}, zip_safe=False, url='http://cffi.readthedocs.org', author='Armin Rigo, Maciej Fijalkowski', author_email='python-cffi@googlegroups.com', license='MIT', distclass=CFFIDistribution, 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, )] if cpython else [], install_requires=[ 'pycparser', ] if cpython else [], entry_points = { "distutils.setup_keywords": [ "cffi_modules = cffi.setuptools_ext:cffi_modules", ], } if cpython else {}, 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', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], ) cffi-1.5.2/cffi.egg-info/0000775000175000017500000000000012657646372015277 5ustar arigoarigo00000000000000cffi-1.5.2/cffi.egg-info/not-zip-safe0000664000175000017500000000000112657646372017525 0ustar arigoarigo00000000000000 cffi-1.5.2/cffi.egg-info/top_level.txt0000664000175000017500000000002312657646372020024 0ustar arigoarigo00000000000000_cffi_backend cffi cffi-1.5.2/cffi.egg-info/dependency_links.txt0000664000175000017500000000000112657646372021345 0ustar arigoarigo00000000000000 cffi-1.5.2/cffi.egg-info/entry_points.txt0000664000175000017500000000011412657646372020571 0ustar arigoarigo00000000000000[distutils.setup_keywords] cffi_modules = cffi.setuptools_ext:cffi_modules cffi-1.5.2/cffi.egg-info/SOURCES.txt0000664000175000017500000001122412657646372017163 0ustar arigoarigo00000000000000AUTHORS LICENSE MANIFEST.in setup.py setup_base.py c/_cffi_backend.c c/call_python.c c/cdlopen.c c/cffi1_module.c c/cglob.c c/commontypes.c c/ffi_obj.c c/file_emulator.h c/lib_obj.c c/malloc_closure.h c/minibuffer.h c/misc_thread_common.h c/misc_thread_posix.h c/misc_win32.h c/parse_c_type.c c/realize_c_type.c c/test_c.py c/wchar_helper.h 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/_cffi_include.h cffi/_embedding.h cffi/api.py cffi/backend_ctypes.py cffi/cffi_opcode.py cffi/commontypes.py cffi/cparser.py cffi/ffiplatform.py cffi/gc_weakref.py cffi/lock.py cffi/model.py cffi/parse_c_type.h cffi/recompiler.py cffi/setuptools_ext.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/entry_points.txt cffi.egg-info/not-zip-safe cffi.egg-info/requires.txt cffi.egg-info/top_level.txt demo/_curses.py demo/_curses_build.py demo/_curses_setup.py demo/api.py demo/bsdopendirtype.py demo/bsdopendirtype_build.py demo/bsdopendirtype_setup.py demo/btrfs-snap.py demo/cffi-cocoa.py demo/embedding.py demo/embedding_test.c demo/extern_python.py demo/extern_python_varargs.py demo/fastcsv.py demo/gmp.py demo/gmp_build.py demo/manual.c demo/manual2.py demo/pwuid.py demo/pwuid_build.py demo/py.cleanup demo/pyobj.py demo/readdir.py demo/readdir2.py demo/readdir2_build.py demo/readdir2_setup.py demo/readdir_build.py demo/readdir_ctypes.py demo/readdir_setup.py demo/recopendirtype.py demo/recopendirtype_build.py demo/setup.py demo/setup_manual.py demo/winclipboard.py demo/winclipboard_build.py demo/xclient.py demo/xclient_build.py doc/Makefile doc/make.bat doc/misc/design.rst doc/misc/grant-cffi-1.0.rst doc/misc/parse_c_type.rst doc/source/cdef.rst doc/source/conf.py doc/source/embedding.rst doc/source/index.rst doc/source/installation.rst doc/source/overview.rst doc/source/using.rst doc/source/whatsnew.rst testing/__init__.py testing/support.py testing/udir.py testing/cffi0/__init__.py testing/cffi0/backend_tests.py testing/cffi0/callback_in_thread.py testing/cffi0/test_cdata.py testing/cffi0/test_ctypes.py testing/cffi0/test_ffi_backend.py testing/cffi0/test_function.py testing/cffi0/test_model.py testing/cffi0/test_ownlib.py testing/cffi0/test_parsing.py testing/cffi0/test_platform.py testing/cffi0/test_unicode_literals.py testing/cffi0/test_verify.py testing/cffi0/test_verify2.py testing/cffi0/test_version.py testing/cffi0/test_vgen.py testing/cffi0/test_vgen2.py testing/cffi0/test_zdistutils.py testing/cffi0/test_zintegration.py testing/cffi0/snippets/distutils_module/setup.py testing/cffi0/snippets/distutils_module/snip_basic_verify.py testing/cffi0/snippets/distutils_package_1/setup.py testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py testing/cffi0/snippets/distutils_package_2/setup.py testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py testing/cffi0/snippets/infrastructure/setup.py testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py testing/cffi0/snippets/setuptools_module/setup.py testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py testing/cffi0/snippets/setuptools_package_1/setup.py testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py testing/cffi0/snippets/setuptools_package_2/setup.py testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py testing/cffi1/__init__.py testing/cffi1/test_cffi_binary.py testing/cffi1/test_commontypes.py testing/cffi1/test_dlopen.py testing/cffi1/test_dlopen_unicode_literals.py testing/cffi1/test_ffi_obj.py testing/cffi1/test_new_ffi_1.py testing/cffi1/test_parse_c_type.py testing/cffi1/test_re_python.py testing/cffi1/test_realize_c_type.py testing/cffi1/test_recompiler.py testing/cffi1/test_unicode_literals.py testing/cffi1/test_verify1.py testing/cffi1/test_zdist.py testing/embedding/__init__.py testing/embedding/add1-test.c testing/embedding/add1.py testing/embedding/add2-test.c testing/embedding/add2.py testing/embedding/add3.py testing/embedding/add_recursive-test.c testing/embedding/add_recursive.py testing/embedding/perf-test.c testing/embedding/perf.py testing/embedding/test_basic.py testing/embedding/test_performance.py testing/embedding/test_recursive.py testing/embedding/test_thread.py testing/embedding/test_tlocal.py testing/embedding/thread-test.h testing/embedding/thread1-test.c testing/embedding/thread2-test.c testing/embedding/thread3-test.c testing/embedding/tlocal-test.c testing/embedding/tlocal.pycffi-1.5.2/cffi.egg-info/requires.txt0000664000175000017500000000001212657646372017670 0ustar arigoarigo00000000000000pycparser cffi-1.5.2/cffi.egg-info/PKG-INFO0000664000175000017500000000213112657646372016371 0ustar arigoarigo00000000000000Metadata-Version: 1.1 Name: cffi Version: 1.5.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 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy cffi-1.5.2/testing/0000775000175000017500000000000012657646372014353 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/0000775000175000017500000000000012657646372015342 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/test_zintegration.py0000664000175000017500000001253112657646311021463 0ustar arigoarigo00000000000000import py, os, sys, shutil import subprocess from testing.udir import udir if sys.platform == 'win32': py.test.skip('snippets do not run on win32') if sys.version_info < (2, 7): py.test.skip('fails e.g. on a Debian/Ubuntu which patches virtualenv' ' in a non-2.6-friendly way') 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 paths = "" 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 paths = [] for module in modules: target = __import__(module, None, None, []) if not hasattr(target, '__file__'): # for _cffi_backend on pypy continue src = os.path.abspath(target.__file__) for end in ['__init__.pyc', '__init__.pyo', '__init__.py']: if src.lower().endswith(end): src = src[:-len(end)-1] break paths.append(os.path.dirname(src)) paths = os.pathsep.join(paths) return tmpdir, paths SNIPPET_DIR = py.path.local(__file__).join('..', 'snippets') def really_run_setup_and_program(dirname, venv_dir_and_paths, python_snippet): venv_dir, paths = venv_dir_and_paths 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))) if os.name == 'nt': bindir = 'Scripts' else: bindir = 'bin' vp = str(venv_dir.join(bindir).join('python')) env = os.environ.copy() env['PYTHONPATH'] = paths subprocess.check_call((vp, 'setup.py', 'clean'), env=env) subprocess.check_call((vp, 'setup.py', 'install'), env=env) subprocess.check_call((vp, str(python_f)), env=env) 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'))) class TestZIntegration(object): def teardown_class(self): if udir.isdir(): udir.remove(ignore_errors=True) udir.ensure(dir=1) def test_infrastructure(self): run_setup_and_program('infrastructure', ''' import snip_infrastructure assert snip_infrastructure.func() == 42 ''') def test_distutils_module(self): 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(self): 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(self): 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(self): 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(self): 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(self): 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-1.5.2/testing/cffi0/test_function.py0000664000175000017500000004075712657646311020606 0ustar arigoarigo00000000000000import py from cffi import FFI, CDefError import math, os, sys import ctypes.util from cffi.backend_ctypes import CTypesBackend from testing.udir import udir from testing.support import FdWriteCapture try: from StringIO import StringIO except ImportError: from io import StringIO 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_dlopen_constant(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" #define FOOBAR 42 static const float baz = 42.5; /* not visible */ double sin(double x); """) m = ffi.dlopen(lib_m) assert m.FOOBAR == 42 py.test.raises(NotImplementedError, "m.baz") 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() 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() 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() 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 def test_windows_stdcall(self): if sys.platform != 'win32': py.test.skip("Windows-only test") if self.Backend is CTypesBackend: py.test.skip("not with the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef(""" BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency); """) m = ffi.dlopen("Kernel32.dll") p_freq = ffi.new("LONGLONG *") res = m.QueryPerformanceFrequency(p_freq) assert res != 0 assert p_freq[0] != 0 def test_explicit_cdecl_stdcall(self): if sys.platform != 'win32': py.test.skip("Windows-only test") if self.Backend is CTypesBackend: py.test.skip("not with the ctypes backend") win64 = (sys.maxsize > 2**32) # ffi = FFI(backend=self.Backend()) ffi.cdef(""" BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency); """) m = ffi.dlopen("Kernel32.dll") tp = ffi.typeof(m.QueryPerformanceFrequency) assert str(tp) == "" # ffi = FFI(backend=self.Backend()) ffi.cdef(""" BOOL __cdecl QueryPerformanceFrequency(LONGLONG *lpFrequency); """) m = ffi.dlopen("Kernel32.dll") tpc = ffi.typeof(m.QueryPerformanceFrequency) assert tpc is tp # ffi = FFI(backend=self.Backend()) ffi.cdef(""" BOOL WINAPI QueryPerformanceFrequency(LONGLONG *lpFrequency); """) m = ffi.dlopen("Kernel32.dll") tps = ffi.typeof(m.QueryPerformanceFrequency) if win64: assert tps is tpc else: assert tps is not tpc assert str(tps) == "" # ffi = FFI(backend=self.Backend()) ffi.cdef("typedef int (__cdecl *fnc_t)(int);") ffi.cdef("typedef int (__stdcall *fns_t)(int);") tpc = ffi.typeof("fnc_t") tps = ffi.typeof("fns_t") assert str(tpc) == "" if win64: assert tps is tpc else: assert str(tps) == "" # fnc = ffi.cast("fnc_t", 0) fns = ffi.cast("fns_t", 0) ffi.new("fnc_t[]", [fnc]) if not win64: py.test.raises(TypeError, ffi.new, "fnc_t[]", [fns]) py.test.raises(TypeError, ffi.new, "fns_t[]", [fnc]) ffi.new("fns_t[]", [fns]) def test_stdcall_only_on_windows(self): ffi = FFI(backend=self.Backend()) ffi.cdef("double __stdcall sin(double x);") # stdcall ignored m = ffi.dlopen(lib_m) if (sys.platform == 'win32' and sys.maxsize < 2**32 and self.Backend is not CTypesBackend): assert "double(__stdcall *)(double)" in str(ffi.typeof(m.sin)) else: assert "double(*)(double)" in str(ffi.typeof(m.sin)) x = m.sin(1.23) assert x == math.sin(1.23) def test_dir_on_dlopen_lib(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef enum { MYE1, MYE2 } myenum_t; double myfunc(double); double myvar; const double myconst; #define MYFOO 42 """) m = ffi.dlopen(lib_m) assert dir(m) == ['MYE1', 'MYE2', 'MYFOO', 'myconst', 'myfunc', 'myvar'] cffi-1.5.2/testing/cffi0/test_verify2.py0000664000175000017500000000047412657646311020337 0ustar arigoarigo00000000000000from .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-1.5.2/testing/cffi0/test_parsing.py0000664000175000017500000003477512657646311020427 0ustar arigoarigo00000000000000import 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_remove_line_continuation_comments(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" double // blah \\ more comments x(void); double // blah\\\\ y(void); double // blah\\ \ etc z(void); """) m = ffi.dlopen(lib_m) m.x m.y m.z def test_line_continuation_in_defines(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" #define ABC\\ 42 #define BCD \\ 43 """) m = ffi.dlopen(lib_m) assert m.ABC == 42 assert m.BCD == 43 def test_define_not_supported_for_now(): ffi = FFI(backend=FakeBackend()) e = py.test.raises(CDefError, ffi.cdef, '#define FOO "blah"') assert str(e.value) == ( 'only supports one of the following syntax:\n' ' #define FOO ... (literally dot-dot-dot)\n' ' #define FOO NUMBER (with NUMBER an integer' ' constant, decimal/hex/octal)\n' 'got:\n' ' #define FOO "blah"') 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 ffi = FFI() ffi.cdef("typedef int bool, *FILE;") assert repr(ffi.cast("bool", 123)) == "" assert re.match(r"", repr(ffi.cast("FILE", 123))) ffi = FFI() ffi.cdef("typedef bool (*fn_t)(bool, bool);") # "bool," but within "( )" def test_bool(): ffi = FFI() ffi.cdef("void f(bool);") # ffi = FFI() ffi.cdef("typedef _Bool bool; void f(bool);") def test_unknown_argument_type(): ffi = FFI() e = py.test.raises(CDefError, ffi.cdef, "void f(foobarbazzz);") assert str(e.value) == ("f arg 1: unknown type 'foobarbazzz' (if you meant" " to use the old C syntax of giving untyped" " arguments, it is not supported)") def test_void_renamed_as_only_arg(): ffi = FFI() ffi.cdef("typedef void void_t1;" "typedef void_t1 void_t;" "typedef int (*func_t)(void_t);") assert ffi.typeof("func_t").args == () def test_WPARAM_on_windows(): if sys.platform != 'win32': py.test.skip("Only for Windows") ffi = FFI() ffi.cdef("void f(WPARAM);") # # WPARAM -> UINT_PTR -> unsigned 32/64-bit integer ffi = FFI() value = int(ffi.cast("WPARAM", -42)) assert value == sys.maxsize * 2 - 40 def test__is_constant_globalvar(): 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), ]: ffi = FFI() ffi.cdef(input) declarations = ffi._parser._declarations assert ('constant a' in declarations) == expected_output assert ('variable a' in declarations) == (not expected_output) def test_restrict(): from cffi import model for input, expected_output in [ ("int a;", False), ("restrict int a;", True), ("int *a;", False), ]: ffi = FFI() ffi.cdef(input) tp, quals = ffi._parser._declarations['variable a'] assert bool(quals & model.Q_RESTRICT) == expected_output def test_different_const_funcptr_types(): lst = [] for input in [ "int(*)(int *a)", "int(*)(int const *a)", "int(*)(int * const a)", "int(*)(int const a[])"]: ffi = FFI(backend=FakeBackend()) lst.append(ffi._parser.parse_type(input)) assert lst[0] != lst[1] assert lst[0] == lst[2] assert lst[1] == lst[3] def test_const_pointer_to_pointer(): from cffi import model ffi = FFI(backend=FakeBackend()) # tp, qual = ffi._parser.parse_type_and_quals("char * * (* const)") assert (str(tp), qual) == ("", model.Q_CONST) tp, qual = ffi._parser.parse_type_and_quals("char * (* const (*))") assert (str(tp), qual) == ("", 0) tp, qual = ffi._parser.parse_type_and_quals("char (* const (* (*)))") assert (str(tp), qual) == ("", 0) tp, qual = ffi._parser.parse_type_and_quals("char const * * *") assert (str(tp), qual) == ("", 0) tp, qual = ffi._parser.parse_type_and_quals("const char * * *") assert (str(tp), qual) == ("", 0) # tp, qual = ffi._parser.parse_type_and_quals("char * * * const const") assert (str(tp), qual) == ("", model.Q_CONST) tp, qual = ffi._parser.parse_type_and_quals("char * * volatile *") assert (str(tp), qual) == ("", 0) tp, qual = ffi._parser.parse_type_and_quals("char * volatile restrict * *") assert (str(tp), qual) == ("", 0) tp, qual = ffi._parser.parse_type_and_quals("char const volatile * * *") assert (str(tp), qual) == ("", 0) tp, qual = ffi._parser.parse_type_and_quals("const char * * *") assert (str(tp), qual) == ("", 0) # tp, qual = ffi._parser.parse_type_and_quals( "int(char*const*, short****const*)") assert (str(tp), qual) == ( "", 0) tp, qual = ffi._parser.parse_type_and_quals( "char*const*(short*const****)") assert (str(tp), qual) == ( "", 0) def test_enum(): ffi = FFI() ffi.cdef(""" enum Enum { POS = +1, TWO = 2, NIL = 0, NEG = -1}; """) C = ffi.dlopen(None) assert C.POS == 1 assert C.TWO == 2 assert C.NIL == 0 assert C.NEG == -1 def test_stdcall(): ffi = FFI() tp = ffi.typeof("int(*)(int __stdcall x(int)," " long (__cdecl*y)(void)," " short(WINAPI *z)(short))") if sys.platform == 'win32' and sys.maxsize < 2**32: stdcall = '__stdcall ' else: stdcall = '' assert str(tp) == ( "" % (stdcall, stdcall)) def test_extern_python(): ffi = FFI() ffi.cdef(""" int bok(int, int); extern "Python" int foobar(int, int); int baz(int, int); """) assert sorted(ffi._parser._declarations) == [ 'extern_python foobar', 'function baz', 'function bok'] assert (ffi._parser._declarations['function bok'] == ffi._parser._declarations['extern_python foobar'] == ffi._parser._declarations['function baz']) def test_extern_python_group(): ffi = FFI() ffi.cdef(""" int bok(int); extern "Python" {int foobar(int, int);int bzrrr(int);} int baz(int, int); """) assert sorted(ffi._parser._declarations) == [ 'extern_python bzrrr', 'extern_python foobar', 'function baz', 'function bok'] assert (ffi._parser._declarations['function baz'] == ffi._parser._declarations['extern_python foobar'] != ffi._parser._declarations['function bok'] == ffi._parser._declarations['extern_python bzrrr']) cffi-1.5.2/testing/cffi0/backend_tests.py0000664000175000017500000020502312657646311020520 0ustar arigoarigo00000000000000import py import platform import sys, ctypes from cffi import FFI, CDefError, FFIError, VerificationMissing 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 { A2=0x1000, B2=0x2000 };") assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A2" assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B2" 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_enum_char_hex_oct(self): ffi = FFI(backend=self.Backend()) ffi.cdef(r"enum foo{A='!', B='\'', C=0x10, D=010, E=- 0x10, F=-010};") assert ffi.string(ffi.cast("enum foo", ord('!'))) == "A" assert ffi.string(ffi.cast("enum foo", ord("'"))) == "B" assert ffi.string(ffi.cast("enum foo", 16)) == "C" assert ffi.string(ffi.cast("enum foo", 8)) == "D" assert ffi.string(ffi.cast("enum foo", -16)) == "E" assert ffi.string(ffi.cast("enum foo", -8)) == "F" def test_enum_partial(self): ffi = FFI(backend=self.Backend()) ffi.cdef(r"enum foo {A, ...}; enum bar { B, C };") lib = ffi.dlopen(None) assert lib.B == 0 py.test.raises(VerificationMissing, getattr, lib, "A") assert lib.C == 1 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_offsetof_nested(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a, b, c; };" "struct bar { struct foo d, e; };") assert ffi.offsetof("struct bar", "e") == 12 py.test.raises(KeyError, ffi.offsetof, "struct bar", "e.a") assert ffi.offsetof("struct bar", "e", "a") == 12 assert ffi.offsetof("struct bar", "e", "b") == 16 assert ffi.offsetof("struct bar", "e", "c") == 20 def test_offsetof_array(self): ffi = FFI(backend=self.Backend()) assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int") assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int") ffi.cdef("struct bar { int a, b; int c[99]; };") assert ffi.offsetof("struct bar", "c") == 2 * ffi.sizeof("int") assert ffi.offsetof("struct bar", "c", 0) == 2 * ffi.sizeof("int") assert ffi.offsetof("struct bar", "c", 51) == 53 * ffi.sizeof("int") 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-1.5.2/testing/cffi0/test_model.py0000664000175000017500000001102012657646311020036 0ustar arigoarigo00000000000000from 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_qual_pointer_type(): ptr_type = PointerType(PrimitiveType("long long"), Q_RESTRICT) assert ptr_type.get_c_name("") == "long long __restrict *" assert const_voidp_type.get_c_name("") == "void const *" 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-1.5.2/testing/cffi0/test_verify.py0000664000175000017500000024131712657646311020260 0ustar arigoarigo00000000000000import py, re import sys, os, math, weakref from cffi import FFI, VerificationError, VerificationMissing, model, FFIError from testing.support import * 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'] pass # no obvious -Werror equivalent on MSVC else: if (sys.platform == 'darwin' and [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): # assume a standard clang or gcc extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] # special things for clang extra_compile_args.append('-Qunused-arguments') else: # assume a standard gcc extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] 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() # # check that no $ sign is produced in the C file; it used to be the # case that anonymous enums would produce '$enum_$1', which was # used as part of a function name. GCC accepts such names, but it's # apparently non-standard. _r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE) _r_string = re.compile(r'\".*?\"') def _write_source_and_check(self, file=None): base_write_source(self, file) if file is None: f = open(self.sourcefilename) data = f.read() f.close() data = _r_comment.sub(' ', data) data = _r_string.sub('"skipped"', data) assert '$' not in data base_write_source = cffi.verifier.Verifier._write_source cffi.verifier.Verifier._write_source = _write_source_and_check 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 _Wconversion(cdef, source, **kargs): if sys.platform in ('win32', 'darwin'): py.test.skip("needs GCC") ffi = FFI() ffi.cdef(cdef) py.test.raises(VerificationError, ffi.verify, source, **kargs) extra_compile_args_orig = extra_compile_args[:] extra_compile_args.remove('-Wconversion') try: lib = ffi.verify(source, **kargs) finally: extra_compile_args[:] = extra_compile_args_orig return lib def test_Wconversion_unsigned(): _Wconversion("unsigned foo(void);", "int foo(void) { return -1;}") def test_Wconversion_integer(): _Wconversion("short foo(void);", "long long foo(void) { return 1<", 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_Wconversion_float2int(): _Wconversion("int sinf(float);", "#include ", libraries=lib_m) def test_Wconversion_double2int(): _Wconversion("int sin(double);", "#include ", libraries=lib_m) def test_rounding_1(): ffi = FFI() ffi.cdef("double sinf(float x);") lib = ffi.verify('#include ', libraries=lib_m) res = lib.sinf(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(): lib = _Wconversion("int strlen(char *s);", "#include ") assert lib.strlen(b"hi there!") == 9 def test_return_approximate(): for typename in ['short', 'int', 'long', 'long long']: ffi = FFI() ffi.cdef("%s foo(signed char x);" % typename) lib = ffi.verify("signed char foo(signed char x) { return x;}") assert lib.foo(-128) == -128 assert lib.foo(+127) == +127 def test_strlen_array_of_char(): ffi = FFI() ffi.cdef("size_t 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('i486') or platform.machine().startswith('i586') or platform.machine().startswith('i686') 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 if sys.platform == 'win32': all_primitive_types = all_primitive_types.copy() del all_primitive_types['ssize_t'] 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 (%s)(x+1); }" % (tp, tp.replace(' ', '_'), tp, 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; }") 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) { (void)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) // blah\n" "#define CC (22*2) /* foobar */\n" "#define DD ((unsigned int)142) /* foo\nbar */\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" # assert ffi.typeof("enum ee").relements == {'EE1': 10, 'EE2': 11, 'EE3': -10} assert ffi.typeof("enum ee").elements == {10: 'EE1', 11: 'EE2', -10: 'EE3'} 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_nonfull_enum_bug3(): ffi = FFI() ffi.cdef("enum ee2 { EE4=..., EE5=... };") ffi.cdef("enum ee6 { EE7=10, EE8=..., EE9=... };") 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.601f; 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 (int)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);") lib = ffi.verify(""" struct foo_s { double b; long a; }; int foo1(struct foo_s s) { return (int)s.a - (int)s.b; } int (*foo)(struct foo_s s) = &foo1; """) e = py.test.raises(NotImplementedError, lib.foo, "?") msg = ("ctype 'struct foo_s' not supported as argument (it is a struct " 'declared with "...;", but the C calling convention may depend ' 'on the missing fields)') assert str(e.value) == msg 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(""" size_t 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 (int)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 (enum foo_e)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_argument(): ffi = FFI() ffi.cdef(""" typedef enum { AA, BB, ... } foo_t; int foo_func(foo_t); """) lib = ffi.verify(""" typedef enum { AA, CC, BB } foo_t; int foo_func(foo_t e) { return (int)e; } """) assert lib.foo_func(lib.BB) == lib.BB == 2 py.test.raises(TypeError, lib.foo_func, "BB") 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 (foo_t)x; } """) assert lib.foo_func(lib.BB) == lib.BB == 2 def test_function_typedef(): ffi = FFI() ffi.cdef(""" typedef double func_t(double); func_t sin; """) lib = ffi.verify('#include ', libraries=lib_m) assert lib.sin(1.23) == math.sin(1.23) def test_opaque_integer_as_function_result(): #import platform #if platform.machine().startswith('sparc'): # py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') #elif platform.machine() == 'mips64' and sys.maxsize > 2**32: # py.test.skip('Segfaults on mips64el') # 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_return_partial_struct(): ffi = FFI() ffi.cdef(""" typedef struct { int x; ...; } foo_t; foo_t foo(void); """) lib = ffi.verify(""" typedef struct { int y, x; } foo_t; foo_t foo(void) { foo_t r = { 45, 81 }; return r; } """) h = lib.foo() assert ffi.sizeof(h) == 2 * ffi.sizeof("int") assert h.x == 81 def test_take_and_return_partial_structs(): ffi = FFI() ffi.cdef(""" typedef struct { int x; ...; } foo_t; foo_t foo(foo_t, foo_t); """) lib = ffi.verify(""" typedef struct { int y, x; } foo_t; foo_t foo(foo_t a, foo_t b) { foo_t r = { 100, a.x * 5 + b.x * 7 }; return r; } """) args = ffi.new("foo_t[3]") args[0].x = 1000 args[2].x = -498 h = lib.foo(args[0], args[2]) assert ffi.sizeof(h) == 2 * ffi.sizeof("int") assert h.x == 1000 * 5 - 498 * 7 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 x) { }") 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 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_relative_to(): import tempfile, os from testing.udir import udir tmpdir = tempfile.mkdtemp(dir=str(udir)) ffi = FFI() ffi.cdef("int foo(int);") f = open(os.path.join(tmpdir, 'foo.h'), 'w') f.write("int foo(int a) { return a + 42; }\n") f.close() lib = ffi.verify('#include "foo.h"', include_dirs=['.'], relative_to=os.path.join(tmpdir, 'x')) 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 not sys.platform.startswith('linux'): py.test.skip("likely, we 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 myprintf11(const char *, int); FILE *myfile;") lib = ffi.verify(""" #include FILE *myfile; int myprintf11(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.myprintf11(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_named_pointer_as_argument(): ffi = FFI() ffi.cdef("typedef struct { int x; } *mystruct_p;\n" "mystruct_p ff5a(mystruct_p);") lib = ffi.verify("typedef struct { int x; } *mystruct_p;\n" "mystruct_p ff5a(mystruct_p p) { p->x += 40; return p; }") p = ffi.new("mystruct_p", [-2]) q = lib.ff5a(p) assert q == p assert p.x == 38 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); int (*const c_callback)(int,...); /* pass this ptr to C routines */ int some_c_function(int(*cb)(int,...)); """) 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((size_t)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); unsigned long long tf_bQ(signed char x, unsigned 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; typedef signed char b_t; typedef unsigned char B_t; typedef short h_t; typedef unsigned short H_t; typedef int i_t; typedef unsigned int I_t; typedef long l_t; typedef unsigned long L_t; typedef long long q_t; typedef unsigned long long Q_t; typedef float f_t; typedef double d_t; typedef long double D_t; #define S(letter) xvalue = (int)x; letter##value = (letter##_t)c; #define R(letter) return (letter##_t)rvalue; signed char tf_bb(signed char x, signed char c) { S(i) R(b) } unsigned char tf_bB(signed char x, unsigned char c) { S(i) R(B) } short tf_bh(signed char x, short c) { S(i) R(h) } unsigned short tf_bH(signed char x, unsigned short c) { S(i) R(H) } int tf_bi(signed char x, int c) { S(i) R(i) } unsigned int tf_bI(signed char x, unsigned int c) { S(i) R(I) } long tf_bl(signed char x, long c) { S(i) R(l) } unsigned long tf_bL(signed char x, unsigned long c) { S(i) R(L) } long long tf_bq(signed char x, long long c) { S(i) R(q) } unsigned long long tf_bQ(signed char x, unsigned long long c) { S(i) R(Q) } float tf_bf(signed char x, float c) { S(f) R(f) } double tf_bd(signed char x, double c) { S(d) R(d) } long double tf_bD(signed char x, long double c) { S(D) R(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'), ('Q', 'unsigned 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 def _run_in_multiple_threads(test1): test1() import sys try: import thread except ImportError: import _thread as thread errors = [] def wrapper(lock): try: test1() except: errors.append(sys.exc_info()) lock.release() locks = [] for i in range(10): _lock = thread.allocate_lock() _lock.acquire() thread.start_new_thread(wrapper, (_lock,)) locks.append(_lock) for _lock in locks: _lock.acquire() if errors: raise errors[0][1] def test_errno_working_even_with_pypys_jit(): ffi = FFI() ffi.cdef("int f(int);") lib = ffi.verify(""" #include int f(int x) { return (errno = errno + x); } """) @_run_in_multiple_threads def test1(): ffi.errno = 0 for i in range(10000): e = lib.f(1) assert e == i + 1 assert ffi.errno == e for i in range(10000): ffi.errno = i e = lib.f(42) assert e == i + 42 def test_getlasterror_working_even_with_pypys_jit(): if sys.platform != 'win32': py.test.skip("win32-only test") ffi = FFI() ffi.cdef("void SetLastError(DWORD);") lib = ffi.dlopen("Kernel32.dll") @_run_in_multiple_threads def test1(): for i in range(10000): n = (1 << 29) + i lib.SetLastError(n) assert ffi.getwinerror()[0] == n def test_verify_dlopen_flags(): # Careful with RTLD_GLOBAL. If by chance the FFI is not deleted # promptly, like on PyPy, then other tests may see the same # exported symbols as well. So we must not export a simple name # like 'foo'! ffi1 = FFI() ffi1.cdef("int foo_verify_dlopen_flags;") lib1 = ffi1.verify("int foo_verify_dlopen_flags;", flags=ffi1.RTLD_GLOBAL | ffi1.RTLD_LAZY) lib2 = get_second_lib() lib1.foo_verify_dlopen_flags = 42 assert lib2.foo_verify_dlopen_flags == 42 lib2.foo_verify_dlopen_flags += 1 assert lib1.foo_verify_dlopen_flags == 43 def get_second_lib(): # Hack, using modulename makes the test fail ffi2 = FFI() ffi2.cdef("int foo_verify_dlopen_flags;") lib2 = ffi2.verify("int foo_verify_dlopen_flags;", flags=ffi2.RTLD_GLOBAL | ffi2.RTLD_LAZY) return lib2 def test_consider_not_implemented_function_type(): ffi = FFI() ffi.cdef("typedef union { int a; float b; } Data;" "typedef struct { int a:2; } MyStr;" "typedef void (*foofunc_t)(Data);" "typedef Data (*bazfunc_t)(void);" "typedef MyStr (*barfunc_t)(void);") fooptr = ffi.cast("foofunc_t", 123) bazptr = ffi.cast("bazfunc_t", 123) barptr = ffi.cast("barfunc_t", 123) # assert did not crash so far e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *")) assert str(e.value) == ( "ctype 'Data' (size 4) not supported as argument") e = py.test.raises(NotImplementedError, bazptr) assert str(e.value) == ( "ctype 'Data' (size 4) not supported as return value") e = py.test.raises(NotImplementedError, barptr) assert str(e.value) == ( "ctype 'MyStr' not supported as return value " "(it is a struct with bit fields)") def test_verify_extra_arguments(): ffi = FFI() ffi.cdef("#define ABA ...") lib = ffi.verify("", define_macros=[('ABA', '42')]) assert lib.ABA == 42 def test_implicit_unicode_on_windows(): if sys.platform != 'win32': py.test.skip("win32-only test") ffi = FFI() e = py.test.raises(FFIError, ffi.cdef, "int foo(LPTSTR);") assert str(e.value) == ("The Windows type 'LPTSTR' is only available after" " you call ffi.set_unicode()") for with_unicode in [True, False]: ffi = FFI() ffi.set_unicode(with_unicode) ffi.cdef(""" DWORD GetModuleFileName(HMODULE hModule, LPTSTR lpFilename, DWORD nSize); """) lib = ffi.verify(""" #include """, libraries=['Kernel32']) outbuf = ffi.new("TCHAR[]", 200) n = lib.GetModuleFileName(ffi.NULL, outbuf, 500) assert 0 < n < 500 for i in range(n): #print repr(outbuf[i]) assert ord(outbuf[i]) != 0 assert ord(outbuf[n]) == 0 assert ord(outbuf[0]) < 128 # should be a letter, or '\' def test_use_local_dir(): ffi = FFI() lib = ffi.verify("", modulename="test_use_local_dir") this_dir = os.path.dirname(__file__) pycache_files = os.listdir(os.path.join(this_dir, '__pycache__')) assert any('test_use_local_dir' in s for s in pycache_files) def test_define_known_value(): ffi = FFI() ffi.cdef("#define FOO 0x123") lib = ffi.verify("#define FOO 0x123") assert lib.FOO == 0x123 def test_define_wrong_value(): ffi = FFI() ffi.cdef("#define FOO 123") e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124") assert str(e.value).endswith("FOO has the real value 124, not 123") def test_static_const_int_known_value(): ffi = FFI() ffi.cdef("static const int FOO = 0x123;") lib = ffi.verify("#define FOO 0x123") assert lib.FOO == 0x123 def test_static_const_int_wrong_value(): ffi = FFI() ffi.cdef("static const int FOO = 123;") e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124") assert str(e.value).endswith("FOO has the real value 124, not 123") def test_const_struct_global(): ffi = FFI() ffi.cdef("typedef struct { int x; ...; } T; const T myglob;") lib = ffi.verify("typedef struct { double y; int x; } T;" "const T myglob = { 0.1, 42 };") assert ffi.typeof(lib.myglob) == ffi.typeof("T") assert lib.myglob.x == 42 def test_dont_support_int_dotdotdot(): ffi = FFI() ffi.cdef("typedef int... t1;") e = py.test.raises(VerificationError, ffi.verify, "") assert str(e.value) == ("feature not supported with ffi.verify(), but only " "with ffi.set_source(): 'typedef int... t1'") ffi = FFI() ffi.cdef("typedef unsigned long... t1;") e = py.test.raises(VerificationError, ffi.verify, "") assert str(e.value) == ("feature not supported with ffi.verify(), but only " "with ffi.set_source(): 'typedef unsigned long... t1'") def test_const_fields(): ffi = FFI() ffi.cdef("""struct foo_s { const int a; void *const b; };""") ffi.verify("""struct foo_s { const int a; void *const b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int") assert foo_s.fields[1][0] == 'b' assert foo_s.fields[1][1].type is ffi.typeof("void *") def test_win32_calling_convention_0(): ffi = FFI() ffi.cdef(""" int call1(int(__cdecl *cb)(int)); int (*const call2)(int(__stdcall *cb)(int)); """) lib = ffi.verify(r""" #ifndef _MSC_VER # define __stdcall /* nothing */ #endif int call1(int(*cb)(int)) { int i, result = 0; //printf("call1: cb = %p\n", cb); for (i = 0; i < 1000; i++) result += cb(i); //printf("result = %d\n", result); return result; } int call2(int(__stdcall *cb)(int)) { int i, result = 0; //printf("call2: cb = %p\n", cb); for (i = 0; i < 1000; i++) result += cb(-i); //printf("result = %d\n", result); return result; } """) @ffi.callback("int(int)") def cb1(x): return x * 2 @ffi.callback("int __stdcall(int)") def cb2(x): return x * 3 #print 'cb1 =', cb1 res = lib.call1(cb1) assert res == 500*999*2 #print 'cb2 =', cb2 #print ffi.typeof(lib.call2) #print 'call2 =', lib.call2 res = lib.call2(cb2) #print '...' assert res == -500*999*3 #print 'done' if sys.platform == 'win32' and sys.maxsize < 2**32: assert '__stdcall' in str(ffi.typeof(cb2)) assert '__stdcall' not in str(ffi.typeof(cb1)) py.test.raises(TypeError, lib.call1, cb2) py.test.raises(TypeError, lib.call2, cb1) else: assert '__stdcall' not in str(ffi.typeof(cb2)) assert ffi.typeof(cb2) is ffi.typeof(cb1) def test_win32_calling_convention_1(): ffi = FFI() ffi.cdef(""" int __cdecl call1(int(__cdecl *cb)(int)); int __stdcall call2(int(__stdcall *cb)(int)); int (__cdecl *const cb1)(int); int (__stdcall *const cb2)(int); """) lib = ffi.verify(r""" #ifndef _MSC_VER # define __cdecl # define __stdcall #endif int __cdecl cb1(int x) { return x * 2; } int __stdcall cb2(int x) { return x * 3; } int __cdecl call1(int(__cdecl *cb)(int)) { int i, result = 0; //printf("here1\n"); //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); for (i = 0; i < 1000; i++) result += cb(i); //printf("result = %d\n", result); return result; } int __stdcall call2(int(__stdcall *cb)(int)) { int i, result = 0; //printf("here1\n"); //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); for (i = 0; i < 1000; i++) result += cb(-i); //printf("result = %d\n", result); return result; } """) assert lib.call1(lib.cb1) == 500*999*2 assert lib.call2(lib.cb2) == -500*999*3 def test_win32_calling_convention_2(): # any mistake in the declaration of plain function (including the # precise argument types and, here, the calling convention) are # automatically corrected. But this does not apply to the 'cb' # function pointer argument. ffi = FFI() ffi.cdef(""" int __stdcall call1(int(__cdecl *cb)(int)); int __cdecl call2(int(__stdcall *cb)(int)); int (__cdecl *const cb1)(int); int (__stdcall *const cb2)(int); """) lib = ffi.verify(r""" #ifndef _MSC_VER # define __cdecl # define __stdcall #endif int __cdecl call1(int(__cdecl *cb)(int)) { int i, result = 0; for (i = 0; i < 1000; i++) result += cb(i); return result; } int __stdcall call2(int(__stdcall *cb)(int)) { int i, result = 0; for (i = 0; i < 1000; i++) result += cb(-i); return result; } int __cdecl cb1(int x) { return x * 2; } int __stdcall cb2(int x) { return x * 3; } """) assert lib.call1(lib.cb1) == 500*999*2 assert lib.call2(lib.cb2) == -500*999*3 def test_win32_calling_convention_3(): ffi = FFI() ffi.cdef(""" struct point { int x, y; }; int (*const cb1)(struct point); int (__stdcall *const cb2)(struct point); struct point __stdcall call1(int(*cb)(struct point)); struct point call2(int(__stdcall *cb)(struct point)); """) lib = ffi.verify(r""" #ifndef _MSC_VER # define __cdecl # define __stdcall #endif struct point { int x, y; }; int cb1(struct point pt) { return pt.x + 10 * pt.y; } int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } struct point __stdcall call1(int(__cdecl *cb)(struct point)) { int i; struct point result = { 0, 0 }; //printf("here1\n"); //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); for (i = 0; i < 1000; i++) { struct point p = { i, -i }; int r = cb(p); result.x += r; result.y -= r; } return result; } struct point __cdecl call2(int(__stdcall *cb)(struct point)) { int i; struct point result = { 0, 0 }; for (i = 0; i < 1000; i++) { struct point p = { -i, i }; int r = cb(p); result.x += r; result.y -= r; } return result; } """) if sys.platform == 'win32' and sys.maxsize < 2**32: py.test.raises(TypeError, lib.call1, lib.cb2) py.test.raises(TypeError, lib.call2, lib.cb1) pt = lib.call1(lib.cb1) assert (pt.x, pt.y) == (-9*500*999, 9*500*999) pt = lib.call2(lib.cb2) assert (pt.x, pt.y) == (99*500*999, -99*500*999) cffi-1.5.2/testing/cffi0/test_vgen.py0000664000175000017500000000051012657646311017677 0ustar arigoarigo00000000000000import cffi.verifier from .test_verify import * def setup_module(): cffi.verifier.cleanup_tmpdir() cffi.verifier._FORCE_GENERIC_ENGINE = True # Runs all tests with _FORCE_GENERIC_ENGINE = True, to make sure we # also test vengine_gen.py. def teardown_module(): cffi.verifier._FORCE_GENERIC_ENGINE = False cffi-1.5.2/testing/cffi0/test_cdata.py0000664000175000017500000000163512657646311020025 0ustar arigoarigo00000000000000import 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 new_array_type(self, x, y): return FakeType('array-from-%r-len-%r' % (x, y)) 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-1.5.2/testing/cffi0/test_unicode_literals.py0000664000175000017500000000420212657646311022267 0ustar arigoarigo00000000000000# # ---------------------------------------------- # 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-1.5.2/testing/cffi0/test_ownlib.py0000664000175000017500000002052712657646311020244 0ustar arigoarigo00000000000000import py, sys import subprocess, weakref from cffi import FFI from cffi.backend_ctypes import CTypesBackend SOURCE = """\ #include #ifdef _WIN32 #define EXPORT __declspec(dllexport) #else #define EXPORT #endif EXPORT int test_getting_errno(void) { errno = 123; return -1; } EXPORT int test_setting_errno(void) { return errno; }; typedef struct { long x; long y; } POINT; typedef struct { long left; long top; long right; long bottom; } RECT; EXPORT int PointInRect(RECT *prc, POINT pt) { if (pt.x < prc->left) return 0; if (pt.x > prc->right) return 0; if (pt.y < prc->top) return 0; if (pt.y > prc->bottom) return 0; return 1; }; EXPORT long left = 10; EXPORT long top = 20; EXPORT long right = 30; EXPORT long bottom = 40; EXPORT RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, RECT *er, POINT fp, RECT gr) { /*Check input */ if (ar.left + br->left + dr.left + er->left + gr.left != left * 5) { ar.left = 100; return ar; } if (ar.right + br->right + dr.right + er->right + gr.right != right * 5) { ar.right = 100; return ar; } if (cp.x != fp.x) { ar.left = -100; } if (cp.y != fp.y) { ar.left = -200; } switch(i) { case 0: return ar; break; case 1: return dr; break; case 2: return gr; break; } return ar; } EXPORT int my_array[7] = {0, 1, 2, 3, 4, 5, 6}; """ class TestOwnLib(object): Backend = CTypesBackend def setup_class(cls): cls.module = None from testing.udir import udir udir.join('testownlib.c').write(SOURCE) if sys.platform == 'win32': import os # did we already build it? if os.path.exists(str(udir.join('testownlib.dll'))): cls.module = str(udir.join('testownlib.dll')) return # try (not too hard) to find the version used to compile this python # no mingw from distutils.msvc9compiler import get_build_version version = get_build_version() toolskey = "VS%0.f0COMNTOOLS" % version toolsdir = os.environ.get(toolskey, None) if toolsdir is None: return productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") productdir = os.path.abspath(productdir) vcvarsall = os.path.join(productdir, "vcvarsall.bat") # 64? arch = 'x86' if sys.maxsize > 2**32: arch = 'amd64' if os.path.isfile(vcvarsall): cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c ' \ ' /LD /Fetestownlib.dll' subprocess.check_call(cmd, cwd = str(udir), shell=True) cls.module = str(udir.join('testownlib.dll')) else: 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 self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") if sys.platform == 'win32': py.test.skip("fails, errno at multiple addresses") 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 self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") if sys.platform == 'win32': py.test.skip("fails, errno at multiple addresses") 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 self.module is None: 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 self.module is None: 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 self.module is None: 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 self.module is None: 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 if sys.platform != 'win32': # else, errno at multiple addresses assert ffi.errno == 123 def test_struct_by_value(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { long x; long y; } POINT; typedef struct { long left; long top; long right; long bottom; } RECT; long left, top, right, bottom; RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, RECT *er, POINT fp, RECT gr); """) ownlib = ffi.dlopen(self.module) rect = ffi.new('RECT[1]') pt = ffi.new('POINT[1]') pt[0].x = 15 pt[0].y = 25 rect[0].left = ownlib.left rect[0].right = ownlib.right rect[0].top = ownlib.top rect[0].bottom = ownlib.bottom for i in range(4): ret = ownlib.ReturnRect(i, rect[0], rect, pt[0], rect[0], rect, pt[0], rect[0]) assert ret.left == ownlib.left assert ret.right == ownlib.right assert ret.top == ownlib.top assert ret.bottom == ownlib.bottom cffi-1.5.2/testing/cffi0/test_version.py0000664000175000017500000000407212657646311020434 0ustar arigoarigo00000000000000import 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) # '0.8.4': '0.8.3', # did not change # } def test_version(): v = cffi.__version__ version_info = '.'.join(str(i) for i in cffi.__version_info__) version_info = version_info.replace('.beta.', 'b') version_info = version_info.replace('.plus', '+') assert v == version_info #v = BACKEND_VERSIONS.get(v, v) assert v == _cffi_backend.__version__ def test_doc_version(): parent = os.path.dirname(os.path.dirname(cffi.__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(cffi.__file__)) v = cffi.__version__.replace('+', '') p = os.path.join(parent, 'doc', 'source', 'installation.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(cffi.__file__)) p = os.path.join(parent, 'setup.py') content = open(p).read() # v = cffi.__version__.replace('+', '') assert ("version='%s'" % v) in content def test_c_version(): parent = os.path.dirname(os.path.dirname(cffi.__file__)) v = cffi.__version__ p = os.path.join(parent, 'c', 'test_c.py') content = open(p).read() #v = BACKEND_VERSIONS.get(v, v) assert (('assert __version__ == "%s"' % v) in content) def test_embedding_h(): parent = os.path.dirname(os.path.dirname(cffi.__file__)) v = cffi.__version__ p = os.path.join(parent, 'cffi', '_embedding.h') content = open(p).read() assert ('cffi version: %s"' % (v,)) in content cffi-1.5.2/testing/cffi0/__init__.py0000664000175000017500000000000012657646311017432 0ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/test_zdistutils.py0000664000175000017500000002676612657646311021203 0ustar arigoarigo00000000000000import 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 teardown_class(self): if udir.isdir(): udir.remove(ignore_errors=True) udir.ensure(dir=1) 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__) or \ 'setuptools.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__) or \ 'setuptools.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-1.5.2/testing/cffi0/snippets/0000775000175000017500000000000012657646372017207 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/setuptools_module/0000775000175000017500000000000012657646372022775 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/setuptools_module/setup.py0000664000175000017500000000031212657646311024474 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-1.5.2/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py0000664000175000017500000000065412657646311030203 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-1.5.2/testing/cffi0/snippets/distutils_package_2/0000775000175000017500000000000012657646372023127 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/distutils_package_2/setup.py0000664000175000017500000000032212657646311024627 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-1.5.2/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/0000775000175000017500000000000012657646372026707 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py0000664000175000017500000000072312657646311031013 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-1.5.2/testing/cffi0/snippets/distutils_module/0000775000175000017500000000000012657646372022600 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/distutils_module/setup.py0000664000175000017500000000025312657646311024303 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-1.5.2/testing/cffi0/snippets/distutils_module/snip_basic_verify.py0000664000175000017500000000065412657646311026646 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-1.5.2/testing/cffi0/snippets/setuptools_package_1/0000775000175000017500000000000012657646372023323 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/setuptools_package_1/setup.py0000664000175000017500000000031312657646311025023 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-1.5.2/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/0000775000175000017500000000000012657646372030242 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py0000664000175000017500000000065412657646311032351 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-1.5.2/testing/cffi0/snippets/infrastructure/0000775000175000017500000000000012657646372022267 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/infrastructure/setup.py0000664000175000017500000000014412657646311023771 0ustar arigoarigo00000000000000 from distutils.core import setup setup(packages=['snip_infrastructure'], requires=['cffi']) cffi-1.5.2/testing/cffi0/snippets/infrastructure/snip_infrastructure/0000775000175000017500000000000012657646372026400 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py0000664000175000017500000000003312657646311030476 0ustar arigoarigo00000000000000 def func(): return 42 cffi-1.5.2/testing/cffi0/snippets/distutils_package_1/0000775000175000017500000000000012657646372023126 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/distutils_package_1/setup.py0000664000175000017500000000025412657646311024632 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-1.5.2/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/0000775000175000017500000000000012657646372026705 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py0000664000175000017500000000065412657646311031014 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-1.5.2/testing/cffi0/snippets/setuptools_package_2/0000775000175000017500000000000012657646372023324 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/setuptools_package_2/setup.py0000664000175000017500000000036612657646311025034 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-1.5.2/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/0000775000175000017500000000000012657646372030244 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py0000664000175000017500000000073012657646311032346 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-1.5.2/testing/cffi0/test_platform.py0000664000175000017500000000153212657646311020571 0ustar arigoarigo00000000000000import 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-1.5.2/testing/cffi0/test_ctypes.py0000664000175000017500000000264712657646311020264 0ustar arigoarigo00000000000000import py, sys from testing.cffi0 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_field_offset_align(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-1.5.2/testing/cffi0/test_vgen2.py0000664000175000017500000000065112657646311017767 0ustar arigoarigo00000000000000import 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-1.5.2/testing/cffi0/test_ffi_backend.py0000664000175000017500000004013212657646311021157 0ustar arigoarigo00000000000000import py, sys, platform import pytest from testing.cffi0 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) == ("struct foo_s(*)(): " "callback with unsupported argument or return type or with '...'") 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) def test_callback_onerror(self): ffi = FFI(backend=self.Backend()) seen = [] def oops(*args): seen.append(args) def otherfunc(): raise LookupError def cb(n): otherfunc() a = ffi.callback("int(*)(int)", cb, error=42, onerror=oops) res = a(234) assert res == 42 assert len(seen) == 1 exc, val, tb = seen[0] assert exc is LookupError assert isinstance(val, LookupError) assert tb.tb_frame.f_code.co_name == 'cb' assert tb.tb_frame.f_locals['n'] == 234 def test_ffi_new_allocator_2(self): ffi = FFI(backend=self.Backend()) seen = [] def myalloc(size): seen.append(size) return ffi.new("char[]", b"X" * size) def myfree(raw): seen.append(raw) alloc1 = ffi.new_allocator(myalloc, myfree) alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree, should_clear_after_alloc=False) p1 = alloc1("int[10]") p2 = alloc2("int[]", 10) assert seen == [40, 40] assert ffi.typeof(p1) == ffi.typeof("int[10]") assert ffi.sizeof(p1) == 40 assert ffi.typeof(p2) == ffi.typeof("int[]") assert ffi.sizeof(p2) == 40 assert p1[5] == 0 assert p2[6] == ord('X') * 0x01010101 raw1 = ffi.cast("char *", p1) raw2 = ffi.cast("char *", p2) del p1, p2 retries = 0 while len(seen) != 4: retries += 1 assert retries <= 5 import gc; gc.collect() assert seen == [40, 40, raw1, raw2] assert repr(seen[2]) == "" assert repr(seen[3]) == "" def test_ffi_new_allocator_3(self): ffi = FFI(backend=self.Backend()) seen = [] def myalloc(size): seen.append(size) return ffi.new("char[]", b"X" * size) alloc1 = ffi.new_allocator(myalloc) # no 'free' p1 = alloc1("int[10]") assert seen == [40] assert ffi.typeof(p1) == ffi.typeof("int[10]") assert ffi.sizeof(p1) == 40 assert p1[5] == 0 def test_ffi_new_allocator_4(self): ffi = FFI(backend=self.Backend()) py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None) # def myalloc2(size): raise LookupError alloc2 = ffi.new_allocator(myalloc2) py.test.raises(LookupError, alloc2, "int[5]") # def myalloc3(size): return 42 alloc3 = ffi.new_allocator(myalloc3) e = py.test.raises(TypeError, alloc3, "int[5]") assert str(e.value) == "alloc() must return a cdata object (got int)" # def myalloc4(size): return ffi.cast("int", 42) alloc4 = ffi.new_allocator(myalloc4) e = py.test.raises(TypeError, alloc4, "int[5]") assert str(e.value) == "alloc() must return a cdata pointer, not 'int'" # def myalloc5(size): return ffi.NULL alloc5 = ffi.new_allocator(myalloc5) py.test.raises(MemoryError, alloc5, "int[5]") 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', 'aarch64'))") 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', 'aarch64'))") 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', 'aarch64'))") 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', 'aarch64'))") 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(" #include #include #ifdef PTEST_USE_THREAD # include static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; static int remaining; #endif extern int add1(int, int); static double time_delta(struct timeval *stop, struct timeval *start) { return (stop->tv_sec - start->tv_sec) + 1e-6 * (stop->tv_usec - start->tv_usec); } static double measure(void) { long long i, iterations; int result; struct timeval start, stop; double elapsed; add1(0, 0); /* prepare off-line */ i = 0; iterations = 1000; result = gettimeofday(&start, NULL); assert(result == 0); while (1) { for (; i < iterations; i++) { add1(((int)i) & 0xaaaaaa, ((int)i) & 0x555555); } result = gettimeofday(&stop, NULL); assert(result == 0); elapsed = time_delta(&stop, &start); assert(elapsed >= 0.0); if (elapsed > 2.5) break; iterations = iterations * 3 / 2; } return elapsed / (double)iterations; } static void *start_routine(void *arg) { double t = measure(); printf("time per call: %.3g\n", t); #ifdef PTEST_USE_THREAD pthread_mutex_lock(&mutex1); remaining -= 1; if (!remaining) pthread_cond_signal(&cond1); pthread_mutex_unlock(&mutex1); #endif return arg; } int main(void) { #ifndef PTEST_USE_THREAD start_routine(0); #else pthread_t th; int i, status; add1(0, 0); /* this is the main thread */ remaining = PTEST_USE_THREAD; for (i = 0; i < PTEST_USE_THREAD; i++) { status = pthread_create(&th, NULL, start_routine, NULL); assert(status == 0); } pthread_mutex_lock(&mutex1); while (remaining) pthread_cond_wait(&cond1, &mutex1); pthread_mutex_unlock(&mutex1); #endif return 0; } cffi-1.5.2/testing/embedding/thread2-test.c0000664000175000017500000000200412657646311020730 0ustar arigoarigo00000000000000#include #include #include "thread-test.h" extern int add1(int, int); extern int add2(int, int, int); static sem_t done; static void *start_routine_1(void *arg) { int x, status; x = add1(40, 2); assert(x == 42); status = sem_post(&done); assert(status == 0); return arg; } static void *start_routine_2(void *arg) { int x, status; #ifdef T2TEST_AGAIN_ADD1 add1(-1, -1); #endif x = add2(1000, 200, 30); assert(x == 1230); status = sem_post(&done); assert(status == 0); return arg; } int main(void) { pthread_t th; int i, status = sem_init(&done, 0, 0); assert(status == 0); printf("starting\n"); fflush(stdout); status = pthread_create(&th, NULL, start_routine_1, NULL); assert(status == 0); status = pthread_create(&th, NULL, start_routine_2, NULL); assert(status == 0); for (i = 0; i < 2; i++) { status = sem_wait(&done); assert(status == 0); } printf("done\n"); return 0; } cffi-1.5.2/testing/embedding/add1.py0000664000175000017500000000117712657646311017453 0ustar arigoarigo00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api(""" int add1(int, int); """) ffi.embedding_init_code(r""" import sys, time sys.stdout.write("preparing") for i in range(3): sys.stdout.flush() time.sleep(0.02) sys.stdout.write(".") sys.stdout.write("\n") from _add1_cffi import ffi int(ord("A")) # check that built-ins are there @ffi.def_extern() def add1(x, y): sys.stdout.write("adding %d and %d\n" % (x, y)) sys.stdout.flush() return x + y """) ffi.set_source("_add1_cffi", """ """) fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) cffi-1.5.2/testing/embedding/add_recursive-test.c0000664000175000017500000000073312657646311022225 0ustar arigoarigo00000000000000#include #ifdef _MSC_VER # define DLLIMPORT __declspec(dllimport) #else # define DLLIMPORT extern #endif DLLIMPORT int add_rec(int, int); DLLIMPORT int (*my_callback)(int); static int some_callback(int x) { printf("some_callback(%d)\n", x); fflush(stdout); return add_rec(x, 9); } int main(void) { int x, y; my_callback = some_callback; x = add_rec(40, 2); y = add_rec(100, -5); printf("got: %d %d\n", x, y); return 0; } cffi-1.5.2/testing/embedding/add1-test.c0000664000175000017500000000025612657646311020217 0ustar arigoarigo00000000000000#include extern int add1(int, int); int main(void) { int x, y; x = add1(40, 2); y = add1(100, -5); printf("got: %d %d\n", x, y); return 0; } cffi-1.5.2/testing/embedding/test_recursive.py0000664000175000017500000000116212657646311021702 0ustar arigoarigo00000000000000from testing.embedding.test_basic import EmbeddingTests class TestRecursive(EmbeddingTests): def test_recursive(self): add_recursive_cffi = self.prepare_module('add_recursive') self.compile('add_recursive-test', [add_recursive_cffi]) output = self.execute('add_recursive-test') assert output == ("preparing REC\n" "some_callback(400)\n" "adding 400 and 9\n" "<<< 409 >>>\n" "adding 40 and 2\n" "adding 100 and -5\n" "got: 42 95\n") cffi-1.5.2/testing/embedding/perf.py0000664000175000017500000000047112657646311017572 0ustar arigoarigo00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api(""" int add1(int, int); """) ffi.embedding_init_code(r""" from _perf_cffi import ffi @ffi.def_extern() def add1(x, y): return x + y """) ffi.set_source("_perf_cffi", """ """) fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) cffi-1.5.2/testing/embedding/tlocal-test.c0000664000175000017500000000150112657646311020656 0ustar arigoarigo00000000000000#include #include #include "thread-test.h" #define NTHREADS 10 extern int add1(int, int); static sem_t done; static void *start_routine(void *arg) { int i, x, expected, status; expected = add1(40, 2); assert((expected % 1000) == 42); for (i=0; i<10; i++) { x = add1(50, i); assert(x == expected + 8 + i); } status = sem_post(&done); assert(status == 0); return arg; } int main(void) { pthread_t th; int i, status = sem_init(&done, 0, 0); assert(status == 0); for (i = 0; i < NTHREADS; i++) { status = pthread_create(&th, NULL, start_routine, NULL); assert(status == 0); } for (i = 0; i < NTHREADS; i++) { status = sem_wait(&done); assert(status == 0); } printf("done\n"); return 0; } cffi-1.5.2/testing/embedding/test_basic.py0000664000175000017500000001500212657646311020752 0ustar arigoarigo00000000000000import py import sys, os, re import shutil, subprocess, time from testing.udir import udir import cffi local_dir = os.path.dirname(os.path.abspath(__file__)) _link_error = '?' def check_lib_python_found(tmpdir): global _link_error if _link_error == '?': ffi = cffi.FFI() kwds = {} ffi._apply_embedding_fix(kwds) ffi.set_source("_test_lib_python_found", "", **kwds) try: ffi.compile(tmpdir=tmpdir, verbose=True) except cffi.VerificationError as e: _link_error = e else: _link_error = None if _link_error: py.test.skip(str(_link_error)) def prefix_pythonpath(): cffi_base = os.path.dirname(os.path.dirname(local_dir)) pythonpath = os.environ.get('PYTHONPATH', '').split(os.pathsep) if cffi_base not in pythonpath: pythonpath.insert(0, cffi_base) return os.pathsep.join(pythonpath) class EmbeddingTests: _compiled_modules = {} def setup_method(self, meth): check_lib_python_found(str(udir.ensure('embedding', dir=1))) self._path = udir.join('embedding', meth.__name__) if sys.platform == "win32" or sys.platform == "darwin": self._compiled_modules.clear() # workaround def get_path(self): return str(self._path.ensure(dir=1)) def _run_base(self, args, env_extra={}, **kwds): print('RUNNING:', args, env_extra, kwds) env = os.environ.copy() env.update(env_extra) return subprocess.Popen(args, env=env, **kwds) def _run(self, args, env_extra={}): popen = self._run_base(args, env_extra, cwd=self.get_path(), stdout=subprocess.PIPE, universal_newlines=True) output = popen.stdout.read() err = popen.wait() if err: raise OSError("popen failed with exit code %r: %r" % ( err, args)) print(output.rstrip()) return output def prepare_module(self, name): if name not in self._compiled_modules: path = self.get_path() filename = '%s.py' % name # NOTE: if you have an .egg globally installed with an older # version of cffi, this will not work, because sys.path ends # up with the .egg before the PYTHONPATH entries. I didn't # find a solution to that: we could hack sys.path inside the # script run here, but we can't hack it in the same way in # execute(). env_extra = {'PYTHONPATH': prefix_pythonpath()} output = self._run([sys.executable, os.path.join(local_dir, filename)], env_extra=env_extra) match = re.compile(r"\bFILENAME: (.+)").search(output) assert match dynamic_lib_name = match.group(1) if sys.platform == 'win32': assert dynamic_lib_name.endswith('_cffi.dll') elif sys.platform == 'darwin': assert dynamic_lib_name.endswith('_cffi.dylib') else: assert dynamic_lib_name.endswith('_cffi.so') self._compiled_modules[name] = dynamic_lib_name return self._compiled_modules[name] def compile(self, name, modules, opt=False, threads=False, defines={}): path = self.get_path() filename = '%s.c' % name shutil.copy(os.path.join(local_dir, filename), path) shutil.copy(os.path.join(local_dir, 'thread-test.h'), path) import distutils.ccompiler curdir = os.getcwd() try: os.chdir(self.get_path()) c = distutils.ccompiler.new_compiler() print('compiling %s with %r' % (name, modules)) extra_preargs = [] debug = True if sys.platform == 'win32': libfiles = [] for m in modules: m = os.path.basename(m) assert m.endswith('.dll') libfiles.append('Release\\%s.lib' % m[:-4]) modules = libfiles extra_preargs.append('/MANIFEST') debug = False # you need to install extra stuff # for this to work elif threads: extra_preargs.append('-pthread') objects = c.compile([filename], macros=sorted(defines.items()), debug=debug) c.link_executable(objects + modules, name, extra_preargs=extra_preargs) finally: os.chdir(curdir) def execute(self, name): path = self.get_path() env_extra = {'PYTHONPATH': prefix_pythonpath()} if sys.platform == 'win32': _path = os.environ.get('PATH') # for libpypy-c.dll or Python27.dll _path = os.path.split(sys.executable)[0] + ';' + _path env_extra['PATH'] = _path else: libpath = os.environ.get('LD_LIBRARY_PATH') if libpath: libpath = path + ':' + libpath else: libpath = path env_extra['LD_LIBRARY_PATH'] = libpath print('running %r in %r' % (name, path)) executable_name = name if sys.platform == 'win32': executable_name = os.path.join(path, executable_name + '.exe') else: executable_name = os.path.join('.', executable_name) popen = self._run_base([executable_name], env_extra, cwd=path, stdout=subprocess.PIPE, universal_newlines=True) result = popen.stdout.read() err = popen.wait() if err: raise OSError("%r failed with exit code %r" % (name, err)) return result class TestBasic(EmbeddingTests): def test_basic(self): add1_cffi = self.prepare_module('add1') self.compile('add1-test', [add1_cffi]) output = self.execute('add1-test') assert output == ("preparing...\n" "adding 40 and 2\n" "adding 100 and -5\n" "got: 42 95\n") def test_two_modules(self): add1_cffi = self.prepare_module('add1') add2_cffi = self.prepare_module('add2') self.compile('add2-test', [add1_cffi, add2_cffi]) output = self.execute('add2-test') assert output == ("preparing...\n" "adding 40 and 2\n" "prepADD2\n" "adding 100 and -5 and -20\n" "got: 42 75\n") cffi-1.5.2/testing/embedding/add3.py0000664000175000017500000000067612657646311017460 0ustar arigoarigo00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api(""" int add3(int, int, int, int); """) ffi.embedding_init_code(r""" from _add3_cffi import ffi import sys @ffi.def_extern() def add3(x, y, z, t): sys.stdout.write("adding %d, %d, %d, %d\n" % (x, y, z, t)) sys.stdout.flush() return x + y + z + t """) ffi.set_source("_add3_cffi", """ """) fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) cffi-1.5.2/testing/embedding/test_performance.py0000664000175000017500000000337712657646311022206 0ustar arigoarigo00000000000000import sys from testing.embedding.test_basic import EmbeddingTests if sys.platform == 'win32': import py py.test.skip("written with POSIX functions") class TestPerformance(EmbeddingTests): def test_perf_single_threaded(self): perf_cffi = self.prepare_module('perf') self.compile('perf-test', [perf_cffi], opt=True) output = self.execute('perf-test') print('='*79) print(output.rstrip()) print('='*79) def test_perf_in_1_thread(self): perf_cffi = self.prepare_module('perf') self.compile('perf-test', [perf_cffi], opt=True, threads=True, defines={'PTEST_USE_THREAD': '1'}) output = self.execute('perf-test') print('='*79) print(output.rstrip()) print('='*79) def test_perf_in_2_threads(self): perf_cffi = self.prepare_module('perf') self.compile('perf-test', [perf_cffi], opt=True, threads=True, defines={'PTEST_USE_THREAD': '2'}) output = self.execute('perf-test') print('='*79) print(output.rstrip()) print('='*79) def test_perf_in_4_threads(self): perf_cffi = self.prepare_module('perf') self.compile('perf-test', [perf_cffi], opt=True, threads=True, defines={'PTEST_USE_THREAD': '4'}) output = self.execute('perf-test') print('='*79) print(output.rstrip()) print('='*79) def test_perf_in_8_threads(self): perf_cffi = self.prepare_module('perf') self.compile('perf-test', [perf_cffi], opt=True, threads=True, defines={'PTEST_USE_THREAD': '8'}) output = self.execute('perf-test') print('='*79) print(output.rstrip()) print('='*79) cffi-1.5.2/testing/embedding/tlocal.py0000664000175000017500000000123612657646311020114 0ustar arigoarigo00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api(""" int add1(int, int); """) ffi.embedding_init_code(r""" from _tlocal_cffi import ffi import itertools try: import thread g_seen = itertools.count().next except ImportError: import _thread as thread # py3 g_seen = itertools.count().__next__ tloc = thread._local() @ffi.def_extern() def add1(x, y): try: num = tloc.num except AttributeError: num = tloc.num = g_seen() * 1000 return x + y + num """) ffi.set_source("_tlocal_cffi", """ """) fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) cffi-1.5.2/testing/embedding/thread3-test.c0000664000175000017500000000203212657646311020732 0ustar arigoarigo00000000000000#include #include #include "thread-test.h" extern int add2(int, int, int); extern int add3(int, int, int, int); static sem_t done; static void *start_routine_2(void *arg) { int x, status; x = add2(40, 2, 100); assert(x == 142); status = sem_post(&done); assert(status == 0); return arg; } static void *start_routine_3(void *arg) { int x, status; x = add3(1000, 200, 30, 4); assert(x == 1234); status = sem_post(&done); assert(status == 0); return arg; } int main(void) { pthread_t th; int i, status = sem_init(&done, 0, 0); assert(status == 0); printf("starting\n"); fflush(stdout); for (i = 0; i < 10; i++) { status = pthread_create(&th, NULL, start_routine_2, NULL); assert(status == 0); status = pthread_create(&th, NULL, start_routine_3, NULL); assert(status == 0); } for (i = 0; i < 20; i++) { status = sem_wait(&done); assert(status == 0); } printf("done\n"); return 0; } cffi-1.5.2/testing/embedding/add_recursive.py0000664000175000017500000000133512657646311021455 0ustar arigoarigo00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api(""" int (*my_callback)(int); int add_rec(int, int); """) ffi.embedding_init_code(r""" from _add_recursive_cffi import ffi, lib import sys print("preparing REC") sys.stdout.flush() @ffi.def_extern() def add_rec(x, y): print("adding %d and %d" % (x, y)) sys.stdout.flush() return x + y x = lib.my_callback(400) print('<<< %d >>>' % (x,)) """) ffi.set_source("_add_recursive_cffi", """ /* use CFFI_DLLEXPORT: on windows, it expands to __declspec(dllexport), which is needed to export a variable from a dll */ CFFI_DLLEXPORT int (*my_callback)(int); """) fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) cffi-1.5.2/testing/embedding/__init__.py0000664000175000017500000000000012657646311020361 0ustar arigoarigo00000000000000cffi-1.5.2/testing/embedding/test_thread.py0000664000175000017500000000512112657646311021141 0ustar arigoarigo00000000000000from testing.embedding.test_basic import EmbeddingTests class TestThread(EmbeddingTests): def test_first_calls_in_parallel(self): add1_cffi = self.prepare_module('add1') self.compile('thread1-test', [add1_cffi], threads=True) for i in range(50): output = self.execute('thread1-test') assert output == ("starting\n" "preparing...\n" + "adding 40 and 2\n" * 10 + "done\n") def _take_out(self, text, content): assert content in text i = text.index(content) return text[:i] + text[i+len(content):] def test_init_different_modules_in_different_threads(self): add1_cffi = self.prepare_module('add1') add2_cffi = self.prepare_module('add2') self.compile('thread2-test', [add1_cffi, add2_cffi], threads=True) output = self.execute('thread2-test') output = self._take_out(output, "preparing") output = self._take_out(output, ".") output = self._take_out(output, ".") # at least the 3rd dot should be after everything from ADD2 assert output == ("starting\n" "prepADD2\n" "adding 1000 and 200 and 30\n" ".\n" "adding 40 and 2\n" "done\n") def test_alt_issue(self): add1_cffi = self.prepare_module('add1') add2_cffi = self.prepare_module('add2') self.compile('thread2-test', [add1_cffi, add2_cffi], threads=True, defines={'T2TEST_AGAIN_ADD1': '1'}) output = self.execute('thread2-test') output = self._take_out(output, "adding 40 and 2\n") assert output == ("starting\n" "preparing...\n" "adding -1 and -1\n" "prepADD2\n" "adding 1000 and 200 and 30\n" "done\n") def test_load_in_parallel_more(self): add2_cffi = self.prepare_module('add2') add3_cffi = self.prepare_module('add3') self.compile('thread3-test', [add2_cffi, add3_cffi], threads=True) for i in range(150): output = self.execute('thread3-test') for j in range(10): output = self._take_out(output, "adding 40 and 2 and 100\n") output = self._take_out(output, "adding 1000, 200, 30, 4\n") assert output == ("starting\n" "prepADD2\n" "done\n") cffi-1.5.2/testing/embedding/thread-test.h0000664000175000017500000000430512657646311020661 0ustar arigoarigo00000000000000/************************************************************/ #ifndef _MSC_VER /************************************************************/ #include /* don't include , it is not available on OS/X */ typedef struct { pthread_mutex_t mutex1; pthread_cond_t cond1; unsigned int value; } sem_t; static int sem_init(sem_t *sem, int pshared, unsigned int value) { assert(pshared == 0); sem->value = value; return (pthread_mutex_init(&sem->mutex1, NULL) || pthread_cond_init(&sem->cond1, NULL)); } static int sem_post(sem_t *sem) { pthread_mutex_lock(&sem->mutex1); sem->value += 1; pthread_cond_signal(&sem->cond1); pthread_mutex_unlock(&sem->mutex1); return 0; } static int sem_wait(sem_t *sem) { pthread_mutex_lock(&sem->mutex1); while (sem->value == 0) pthread_cond_wait(&sem->cond1, &sem->mutex1); sem->value -= 1; pthread_mutex_unlock(&sem->mutex1); return 0; } /************************************************************/ #else /************************************************************/ /* Very quick and dirty, just what I need for these tests. Don't use directly in any real code! */ #include #include typedef HANDLE sem_t; typedef HANDLE pthread_t; static int sem_init(sem_t *sem, int pshared, unsigned int value) { assert(pshared == 0); assert(value == 0); *sem = CreateSemaphore(NULL, 0, 999, NULL); return *sem ? 0 : -1; } static int sem_post(sem_t *sem) { return ReleaseSemaphore(*sem, 1, NULL) ? 0 : -1; } static int sem_wait(sem_t *sem) { WaitForSingleObject(*sem, INFINITE); return 0; } static DWORD WINAPI myThreadProc(LPVOID lpParameter) { void *(* start_routine)(void *) = (void *(*)(void *))lpParameter; start_routine(NULL); return 0; } static int pthread_create(pthread_t *thread, void *attr, void *start_routine(void *), void *arg) { assert(arg == NULL); *thread = CreateThread(NULL, 0, myThreadProc, start_routine, 0, NULL); return *thread ? 0 : -1; } /************************************************************/ #endif /************************************************************/ cffi-1.5.2/testing/embedding/add2-test.c0000664000175000017500000000032312657646311020213 0ustar arigoarigo00000000000000#include extern int add1(int, int); extern int add2(int, int, int); int main(void) { int x, y; x = add1(40, 2); y = add2(100, -5, -20); printf("got: %d %d\n", x, y); return 0; } cffi-1.5.2/testing/embedding/add2.py0000664000175000017500000000105612657646311017450 0ustar arigoarigo00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api(""" int add2(int, int, int); """) ffi.embedding_init_code(r""" import sys sys.stdout.write("prepADD2\n") assert '_add2_cffi' in sys.modules m = sys.modules['_add2_cffi'] import _add2_cffi ffi = _add2_cffi.ffi @ffi.def_extern() def add2(x, y, z): sys.stdout.write("adding %d and %d and %d\n" % (x, y, z)) sys.stdout.flush() return x + y + z """) ffi.set_source("_add2_cffi", """ """) fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) cffi-1.5.2/testing/embedding/thread1-test.c0000664000175000017500000000135012657646311020732 0ustar arigoarigo00000000000000#include #include #include "thread-test.h" #define NTHREADS 10 extern int add1(int, int); static sem_t done; static void *start_routine(void *arg) { int x, status; x = add1(40, 2); assert(x == 42); status = sem_post(&done); assert(status == 0); return arg; } int main(void) { pthread_t th; int i, status = sem_init(&done, 0, 0); assert(status == 0); printf("starting\n"); fflush(stdout); for (i = 0; i < NTHREADS; i++) { status = pthread_create(&th, NULL, start_routine, NULL); assert(status == 0); } for (i = 0; i < NTHREADS; i++) { status = sem_wait(&done); assert(status == 0); } printf("done\n"); return 0; } cffi-1.5.2/testing/support.py0000664000175000017500000000342712657646311016440 0ustar arigoarigo00000000000000import 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() long = long # for further "from testing.support import long" 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 class StdErrCapture(object): """Capture writes to sys.stderr (not to the underlying file descriptor).""" def __enter__(self): try: from StringIO import StringIO except ImportError: from io import StringIO self.old_stderr = sys.stderr sys.stderr = f = StringIO() return f def __exit__(self, *args): sys.stderr = self.old_stderr class FdWriteCapture(object): """xxx limited to capture at most 512 bytes of output, according to the Posix manual.""" def __init__(self, capture_fd=2): # stderr by default if sys.platform == 'win32': import py py.test.skip("seems not to work, too bad") self.capture_fd = capture_fd def __enter__(self): import os 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): import os 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 cffi-1.5.2/testing/cffi1/0000775000175000017500000000000012657646372015343 5ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi1/test_cffi_binary.py0000664000175000017500000000132012657646311021214 0ustar arigoarigo00000000000000import py, sys, os import _cffi_backend def test_no_unknown_exported_symbols(): if not hasattr(_cffi_backend, '__file__'): py.test.skip("_cffi_backend module is built-in") if not sys.platform.startswith('linux'): py.test.skip("linux-only") g = os.popen("objdump -T '%s'" % _cffi_backend.__file__, 'r') for line in g: if not line.startswith('0'): continue if '*UND*' in line: continue name = line.split()[-1] if name.startswith('_') or name.startswith('.'): continue if name not in ('init_cffi_backend', 'PyInit__cffi_backend'): raise Exception("Unexpected exported name %r" % (name,)) g.close() cffi-1.5.2/testing/cffi1/test_commontypes.py0000664000175000017500000000162612657646311021327 0ustar arigoarigo00000000000000import py, os, cffi, re import _cffi_backend def getlines(): try: f = open(os.path.join(os.path.dirname(cffi.__file__), '..', 'c', 'commontypes.c')) except IOError: py.test.skip("cannot find ../c/commontypes.c") lines = [line for line in f.readlines() if line.strip().startswith('EQ(')] f.close() return lines def test_alphabetical_order(): lines = getlines() assert lines == sorted(lines) def test_dependencies(): r = re.compile(r'EQ[(]"([^"]+)",(?:\s*"([A-Z0-9_]+)\s*[*]*"[)])?') lines = getlines() d = {} for line in lines: match = r.search(line) if match is not None: d[match.group(1)] = match.group(2) for value in d.values(): if value: assert value in d def test_get_common_types(): d = {} _cffi_backend._get_common_types(d) assert d["bool"] == "_Bool" cffi-1.5.2/testing/cffi1/test_realize_c_type.py0000664000175000017500000000466412657646311021755 0ustar arigoarigo00000000000000import py, sys from cffi import cffi_opcode def check(input, expected_output=None, expected_ffi_error=False): import _cffi_backend ffi = _cffi_backend.FFI() if not expected_ffi_error: ct = ffi.typeof(input) assert isinstance(ct, ffi.CType) assert ct.cname == (expected_output or input) else: e = py.test.raises(ffi.error, ffi.typeof, input) if isinstance(expected_ffi_error, str): assert str(e.value) == expected_ffi_error def test_void(): check("void", "void") check(" void ", "void") def test_int_star(): check("int") check("int *") check("int*", "int *") check("long int", "long") check("long") def test_noop(): check("int(*)", "int *") def test_array(): check("int[6]") def test_funcptr(): check("int(*)(long)") check("int(long)", expected_ffi_error="the type 'int(long)' is a" " function type, not a pointer-to-function type") check("int(void)", expected_ffi_error="the type 'int()' is a" " function type, not a pointer-to-function type") def test_funcptr_rewrite_args(): check("int(*)(int(int))", "int(*)(int(*)(int))") check("int(*)(long[])", "int(*)(long *)") check("int(*)(long[5])", "int(*)(long *)") def test_all_primitives(): for name in cffi_opcode.PRIMITIVE_TO_INDEX: check(name, name) def check_func(input, expected_output=None): import _cffi_backend ffi = _cffi_backend.FFI() ct = ffi.typeof(ffi.callback(input, lambda: None)) assert isinstance(ct, ffi.CType) if sys.platform != 'win32' or sys.maxsize > 2**32: expected_output = expected_output.replace('__stdcall *', '*') assert ct.cname == expected_output def test_funcptr_stdcall(): check_func("int(int)", "int(*)(int)") check_func("int foobar(int)", "int(*)(int)") check_func("int __stdcall(int)", "int(__stdcall *)(int)") check_func("int __stdcall foobar(int)", "int(__stdcall *)(int)") check_func("void __cdecl(void)", "void(*)()") check_func("void __cdecl foobar(void)", "void(*)()") check_func("void __stdcall(void)", "void(__stdcall *)()") check_func("void __stdcall foobar(long, short)", "void(__stdcall *)(long, short)") check_func("void(void __cdecl(void), void __stdcall(void))", "void(*)(void(*)(), void(__stdcall *)())") def test_variadic_overrides_stdcall(): check("void (__stdcall*)(int, ...)", "void(*)(int, ...)") cffi-1.5.2/testing/cffi1/test_re_python.py0000664000175000017500000001526412657646311020764 0ustar arigoarigo00000000000000import sys import py from cffi import FFI from cffi import recompiler, ffiplatform, VerificationMissing from testing.udir import udir def setup_module(mod): SRC = """ #include #define FOOBAR (-42) static const int FOOBAZ = -43; #define BIGPOS 420000000000L #define BIGNEG -420000000000L int add42(int x) { return x + 42; } int add43(int x, ...) { return x; } int globalvar42 = 1234; const int globalconst42 = 4321; const char *const globalconsthello = "hello"; struct foo_s; typedef struct bar_s { int x; signed char a[]; } bar_t; enum foo_e { AA, BB, CC }; void init_test_re_python(void) { } /* windows hack */ void PyInit__test_re_python(void) { } /* windows hack */ """ tmpdir = udir.join('test_re_python') tmpdir.ensure(dir=1) c_file = tmpdir.join('_test_re_python.c') c_file.write(SRC) ext = ffiplatform.get_extension( str(c_file), '_test_re_python', export_symbols=['add42', 'add43', 'globalvar42', 'globalconst42', 'globalconsthello'] ) outputfilename = ffiplatform.compile(str(tmpdir), ext) mod.extmod = outputfilename mod.tmpdir = tmpdir # ffi = FFI() ffi.cdef(""" #define FOOBAR -42 static const int FOOBAZ = -43; #define BIGPOS 420000000000L #define BIGNEG -420000000000L int add42(int); int add43(int, ...); int globalvar42; const int globalconst42; const char *const globalconsthello = "hello"; int no_such_function(int); int no_such_globalvar; struct foo_s; typedef struct bar_s { int x; signed char a[]; } bar_t; enum foo_e { AA, BB, CC }; int strlen(const char *); """) ffi.set_source('re_python_pysrc', None) ffi.emit_python_code(str(tmpdir.join('re_python_pysrc.py'))) mod.original_ffi = ffi # sys.path.insert(0, str(tmpdir)) def test_constant(): from re_python_pysrc import ffi assert ffi.integer_const('FOOBAR') == -42 assert ffi.integer_const('FOOBAZ') == -43 def test_large_constant(): from re_python_pysrc import ffi assert ffi.integer_const('BIGPOS') == 420000000000 assert ffi.integer_const('BIGNEG') == -420000000000 def test_function(): import _cffi_backend from re_python_pysrc import ffi lib = ffi.dlopen(extmod) assert lib.add42(-10) == 32 assert type(lib.add42) is _cffi_backend.FFI.CData def test_function_with_varargs(): import _cffi_backend from re_python_pysrc import ffi lib = ffi.dlopen(extmod, 0) assert lib.add43(45, ffi.cast("int", -5)) == 45 assert type(lib.add43) is _cffi_backend.FFI.CData def test_dlopen_none(): import _cffi_backend from re_python_pysrc import ffi name = None if sys.platform == 'win32': import ctypes.util name = ctypes.util.find_msvcrt() lib = ffi.dlopen(name) assert lib.strlen(b"hello") == 5 def test_dlclose(): import _cffi_backend from re_python_pysrc import ffi lib = ffi.dlopen(extmod) ffi.dlclose(lib) e = py.test.raises(ffi.error, ffi.dlclose, lib) assert str(e.value).startswith( "library '%s' is already closed" % (extmod,)) e = py.test.raises(ffi.error, getattr, lib, 'add42') assert str(e.value) == ( "library '%s' has been closed" % (extmod,)) def test_constant_via_lib(): from re_python_pysrc import ffi lib = ffi.dlopen(extmod) assert lib.FOOBAR == -42 assert lib.FOOBAZ == -43 def test_opaque_struct(): from re_python_pysrc import ffi ffi.cast("struct foo_s *", 0) py.test.raises(TypeError, ffi.new, "struct foo_s *") def test_nonopaque_struct(): from re_python_pysrc import ffi for p in [ffi.new("struct bar_s *", [5, b"foobar"]), ffi.new("bar_t *", [5, b"foobar"])]: assert p.x == 5 assert p.a[0] == ord('f') assert p.a[5] == ord('r') def test_enum(): from re_python_pysrc import ffi assert ffi.integer_const("BB") == 1 e = ffi.cast("enum foo_e", 2) assert ffi.string(e) == "CC" def test_include_1(): sub_ffi = FFI() sub_ffi.cdef("static const int k2 = 121212;") sub_ffi.include(original_ffi) assert 'macro FOOBAR' in original_ffi._parser._declarations assert 'macro FOOBAZ' in original_ffi._parser._declarations sub_ffi.set_source('re_python_pysrc', None) sub_ffi.emit_python_code(str(tmpdir.join('_re_include_1.py'))) # if sys.version_info[:2] >= (3, 3): import importlib importlib.invalidate_caches() # issue 197 (but can't reproduce myself) # from _re_include_1 import ffi assert ffi.integer_const('FOOBAR') == -42 assert ffi.integer_const('FOOBAZ') == -43 assert ffi.integer_const('k2') == 121212 lib = ffi.dlopen(extmod) # <- a random unrelated library would be fine assert lib.FOOBAR == -42 assert lib.FOOBAZ == -43 assert lib.k2 == 121212 # p = ffi.new("bar_t *", [5, b"foobar"]) assert p.a[4] == ord('a') def test_global_var(): from re_python_pysrc import ffi lib = ffi.dlopen(extmod) assert lib.globalvar42 == 1234 p = ffi.addressof(lib, 'globalvar42') lib.globalvar42 += 5 assert p[0] == 1239 p[0] -= 1 assert lib.globalvar42 == 1238 def test_global_const_int(): from re_python_pysrc import ffi lib = ffi.dlopen(extmod) assert lib.globalconst42 == 4321 py.test.raises(AttributeError, ffi.addressof, lib, 'globalconst42') def test_global_const_nonint(): from re_python_pysrc import ffi lib = ffi.dlopen(extmod) assert ffi.string(lib.globalconsthello, 8) == b"hello" py.test.raises(AttributeError, ffi.addressof, lib, 'globalconsthello') def test_rtld_constants(): from re_python_pysrc import ffi ffi.RTLD_NOW # check that we have the attributes ffi.RTLD_LAZY ffi.RTLD_GLOBAL def test_no_such_function_or_global_var(): from re_python_pysrc import ffi lib = ffi.dlopen(extmod) e = py.test.raises(ffi.error, getattr, lib, 'no_such_function') assert str(e.value).startswith( "symbol 'no_such_function' not found in library '") e = py.test.raises(ffi.error, getattr, lib, 'no_such_globalvar') assert str(e.value).startswith( "symbol 'no_such_globalvar' not found in library '") def test_check_version(): import _cffi_backend e = py.test.raises(ImportError, _cffi_backend.FFI, "foobar", _version=0x2594) assert str(e.value).startswith( "cffi out-of-line Python module 'foobar' has unknown version") def test_partial_enum(): ffi = FFI() ffi.cdef("enum foo { A, B, ... };") ffi.set_source('test_partial_enum', None) py.test.raises(VerificationMissing, ffi.emit_python_code, str(tmpdir.join('test_partial_enum.py'))) cffi-1.5.2/testing/cffi1/test_verify1.py0000664000175000017500000022615212657646311020342 0ustar arigoarigo00000000000000import os, sys, math, py from cffi import FFI, FFIError, VerificationError, VerificationMissing, model from cffi import recompiler from testing.support import * import _cffi_backend 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'] extra_compile_args = [] # no obvious -Werror equivalent on MSVC else: if (sys.platform == 'darwin' and [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): # assume a standard clang or gcc extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] # special things for clang extra_compile_args.append('-Qunused-arguments') else: # assume a standard gcc extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] class FFI(FFI): error = _cffi_backend.FFI.error _extra_compile_args = extra_compile_args _verify_counter = 0 def verify(self, preamble='', *args, **kwds): # HACK to reuse the tests from ../cffi0/test_verify.py FFI._verify_counter += 1 module_name = 'verify%d' % FFI._verify_counter try: del self._assigned_source except AttributeError: pass self.set_source(module_name, preamble) return recompiler._verify(self, module_name, preamble, *args, extra_compile_args=self._extra_compile_args, **kwds) class FFI_warnings_not_error(FFI): _extra_compile_args = [] 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, ImportError): 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 test_missing_function(ffi=FFI_warnings_not_error()) 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 _Wconversion(cdef, source, **kargs): if sys.platform in ('win32', 'darwin'): py.test.skip("needs GCC") ffi = FFI() ffi.cdef(cdef) py.test.raises(VerificationError, ffi.verify, source, **kargs) extra_compile_args_orig = extra_compile_args[:] extra_compile_args.remove('-Wconversion') try: lib = ffi.verify(source, **kargs) finally: extra_compile_args[:] = extra_compile_args_orig return lib def test_Wconversion_unsigned(): _Wconversion("unsigned foo(void);", "int foo(void) { return -1;}") def test_Wconversion_integer(): _Wconversion("short foo(void);", "long long foo(void) { return 1<", 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_Wconversion_float2int(): _Wconversion("int sinf(float);", "#include ", libraries=lib_m) def test_Wconversion_double2int(): _Wconversion("int sin(double);", "#include ", libraries=lib_m) def test_rounding_1(): ffi = FFI() ffi.cdef("double sinf(float x);") lib = ffi.verify('#include ', libraries=lib_m) res = lib.sinf(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(): lib = _Wconversion("int strlen(char *s);", "#include ") assert lib.strlen(b"hi there!") == 9 def test_return_approximate(): for typename in ['short', 'int', 'long', 'long long']: ffi = FFI() ffi.cdef("%s foo(signed char x);" % typename) lib = ffi.verify("signed char foo(signed char x) { return x;}") assert lib.foo(-128) == -128 assert lib.foo(+127) == +127 def test_strlen_array_of_char(): ffi = FFI() ffi.cdef("size_t 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('i486') or platform.machine().startswith('i586') or platform.machine().startswith('i686') 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 if sys.platform == 'win32': all_primitive_types = all_primitive_types.copy() del all_primitive_types['ssize_t'] 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 (%s)(x+1); }" % (tp, tp.replace(' ', '_'), tp, 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; }") 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) { (void)f; return 42; }\n") assert lib.bar(ffi.NULL) == 42 def test_ffi_full_struct(): def check(verified_code): ffi = FFI() ffi.cdef("struct foo_s { char x; int y; long *z; };") ffi.verify(verified_code) ffi.new("struct foo_s *", {}) check("struct foo_s { char x; int y; long *z; };") # if sys.platform != 'win32': # XXX fixme: only gives warnings py.test.raises(VerificationError, check, "struct foo_s { char x; int y; int *z; };") # py.test.raises(VerificationError, check, "struct foo_s { int y; long *z; };") # cdef'ed field x is missing # e = py.test.raises(FFI.error, check, "struct foo_s { int y; char x; long *z; };") assert str(e.value).startswith( "struct foo_s: wrong offset for field 'x'" " (cdef says 0, but C compiler says 4)") # e = py.test.raises(FFI.error, check, "struct foo_s { char x; int y; long *z; char extra; };") assert str(e.value).startswith( "struct foo_s: wrong total size" " (cdef says %d, but C compiler says %d)" % ( 8 + FFI().sizeof('long *'), 8 + FFI().sizeof('long *') * 2)) # # 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 check("struct foo_s { char x, extra; int y; long *z; };") # e = py.test.raises(FFI.error, check, "struct foo_s { char x; short pad; short y; long *z; };") assert str(e.value).startswith( "struct foo_s: wrong size for field 'y'" " (cdef says 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) ffi.new("struct foo_s *", []) # because some mismatches show up lazily except (VerificationError, ffi.error): 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 = 1 assert s.f == 1 if int(ffi.cast("foo_e", -1)) < 0: two = -2 else: two = 2 s.f = two assert s.f == two 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) // blah\n" "#define CC (22*2) /* foobar */\n" "#define DD ((unsigned int)142) /* foo\nbar */\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" # assert ffi.typeof("enum ee").relements == {'EE1': 10, 'EE2': 11, 'EE3': -10} assert ffi.typeof("enum ee").elements == {10: 'EE1', 11: 'EE2', -10: 'EE3'} 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 };") # disabled: for now, we always accept and fix transparently constant values #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.601f; 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 (int)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);") lib = ffi.verify(""" struct foo_s { double b; long a; }; int foo1(struct foo_s s) { return (int)s.a - (int)s.b; } int (*foo)(struct foo_s s) = &foo1; """) e = py.test.raises(NotImplementedError, lib.foo, "?") msg = ("ctype 'struct foo_s' not supported as argument (it is a struct " 'declared with "...;", but the C calling convention may depend ' 'on the missing fields)') assert str(e.value) == msg 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(""" size_t 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 (int)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 (enum foo_e)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(): # xxx this is broken in old cffis, but works with recompiler.py ffi = FFI() ffi.cdef("typedef enum { AA, BB } enum1_t;") lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;") assert lib.AA == 0 assert lib.BB == 2 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_argument(): ffi = FFI() ffi.cdef(""" typedef enum { AA, BB, ... } foo_t; int foo_func(foo_t); """) lib = ffi.verify(""" typedef enum { AA, CC, BB } foo_t; int foo_func(foo_t e) { return (int)e; } """) assert lib.foo_func(lib.BB) == lib.BB == 2 py.test.raises(TypeError, lib.foo_func, "BB") 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 (foo_t)x; } """) assert lib.foo_func(lib.BB) == lib.BB == 2 def test_function_typedef(): ffi = FFI() ffi.cdef(""" typedef double func_t(double); func_t sin; """) lib = ffi.verify('#include ', libraries=lib_m) assert lib.sin(1.23) == math.sin(1.23) def test_opaque_integer_as_function_result(): #import platform #if platform.machine().startswith('sparc'): # py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') #elif platform.machine() == 'mips64' and sys.maxsize > 2**32: # py.test.skip('Segfaults on mips64el') # 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_return_partial_struct(): ffi = FFI() ffi.cdef(""" typedef struct { int x; ...; } foo_t; foo_t foo(void); """) lib = ffi.verify(""" typedef struct { int y, x; } foo_t; foo_t foo(void) { foo_t r = { 45, 81 }; return r; } """) h = lib.foo() assert ffi.sizeof(h) == 2 * ffi.sizeof("int") assert h.x == 81 def test_take_and_return_partial_structs(): ffi = FFI() ffi.cdef(""" typedef struct { int x; ...; } foo_t; foo_t foo(foo_t, foo_t); """) lib = ffi.verify(""" typedef struct { int y, x; } foo_t; foo_t foo(foo_t a, foo_t b) { foo_t r = { 100, a.x * 5 + b.x * 7 }; return r; } """) args = ffi.new("foo_t[3]") args[0].x = 1000 args[2].x = -498 h = lib.foo(args[0], args[2]) assert ffi.sizeof(h) == 2 * ffi.sizeof("int") assert h.x == 1000 * 5 - 498 * 7 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 x) { }") 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; }; }; """) assert ffi.offsetof("struct foo_s", "c") == 2 * ffi.sizeof("int") assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") 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; }; }; """) # works fine now #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 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_relative_to(): py.test.skip("not available") import tempfile, os from testing.udir import udir tmpdir = tempfile.mkdtemp(dir=str(udir)) ffi = FFI() ffi.cdef("int foo(int);") f = open(os.path.join(tmpdir, 'foo.h'), 'w') f.write("int foo(int a) { return a + 42; }\n") f.close() lib = ffi.verify('#include "foo.h"', include_dirs=['.'], relative_to=os.path.join(tmpdir, 'x')) 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_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(): py.test.xfail("adapt or remove") 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(): py.test.xfail("adapt or remove") 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(): py.test.xfail("adapt or remove") 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 not sys.platform.startswith('linux'): py.test.skip("likely, we 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 myprintf11(const char *, int); FILE *myfile;") lib = ffi.verify(""" #include FILE *myfile; int myprintf11(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.myprintf11(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_named_pointer_as_argument(): ffi = FFI() ffi.cdef("typedef struct { int x; } *mystruct_p;\n" "mystruct_p ff5a(mystruct_p);") lib = ffi.verify("typedef struct { int x; } *mystruct_p;\n" "mystruct_p ff5a(mystruct_p p) { p->x += 40; return p; }") p = ffi.new("mystruct_p", [-2]) q = lib.ff5a(p) assert q == p assert p.x == 38 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 if sys.platform != 'win32': 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 c2, c2c in [(-1, ''), (-1, ''), (0xffffffff, 'U'), (maxulong, 'UL'), (-int(maxulong / 3), 'L')]: if c2c and sys.platform == 'win32': continue # enums may always be signed with MSVC ffi = FFI() ffi.cdef("enum foo_e { AA };") lib = ffi.verify("enum foo_e { AA=%s%s };" % (c2, c2c)) assert lib.AA == c2 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); int (*const c_callback)(int,...); /* pass this ptr to C routines */ int some_c_function(int(*cb)(int,...)); """) 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((size_t)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(): 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); unsigned long long tf_bQ(signed char x, unsigned 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; typedef signed char b_t; typedef unsigned char B_t; typedef short h_t; typedef unsigned short H_t; typedef int i_t; typedef unsigned int I_t; typedef long l_t; typedef unsigned long L_t; typedef long long q_t; typedef unsigned long long Q_t; typedef float f_t; typedef double d_t; typedef long double D_t; #define S(letter) xvalue = (int)x; letter##value = (letter##_t)c; #define R(letter) return (letter##_t)rvalue; signed char tf_bb(signed char x, signed char c) { S(i) R(b) } unsigned char tf_bB(signed char x, unsigned char c) { S(i) R(B) } short tf_bh(signed char x, short c) { S(i) R(h) } unsigned short tf_bH(signed char x, unsigned short c) { S(i) R(H) } int tf_bi(signed char x, int c) { S(i) R(i) } unsigned int tf_bI(signed char x, unsigned int c) { S(i) R(I) } long tf_bl(signed char x, long c) { S(i) R(l) } unsigned long tf_bL(signed char x, unsigned long c) { S(i) R(L) } long long tf_bq(signed char x, long long c) { S(i) R(q) } unsigned long long tf_bQ(signed char x, unsigned long long c) { S(i) R(Q) } float tf_bf(signed char x, float c) { S(f) R(f) } double tf_bd(signed char x, double c) { S(d) R(d) } long double tf_bD(signed char x, long double c) { S(D) R(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'), ('Q', 'unsigned 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 def _run_in_multiple_threads(test1): test1() import sys try: import thread except ImportError: import _thread as thread errors = [] def wrapper(lock): try: test1() except: errors.append(sys.exc_info()) lock.release() locks = [] for i in range(10): _lock = thread.allocate_lock() _lock.acquire() thread.start_new_thread(wrapper, (_lock,)) locks.append(_lock) for _lock in locks: _lock.acquire() if errors: raise errors[0][1] def test_errno_working_even_with_pypys_jit(): ffi = FFI() ffi.cdef("int f(int);") lib = ffi.verify(""" #include int f(int x) { return (errno = errno + x); } """) @_run_in_multiple_threads def test1(): ffi.errno = 0 for i in range(10000): e = lib.f(1) assert e == i + 1 assert ffi.errno == e for i in range(10000): ffi.errno = i e = lib.f(42) assert e == i + 42 def test_getlasterror_working_even_with_pypys_jit(): if sys.platform != 'win32': py.test.skip("win32-only test") ffi = FFI() ffi.cdef("void SetLastError(DWORD);") lib = ffi.dlopen("Kernel32.dll") @_run_in_multiple_threads def test1(): for i in range(10000): n = (1 << 29) + i lib.SetLastError(n) assert ffi.getwinerror()[0] == n def test_verify_dlopen_flags(): if not hasattr(sys, 'setdlopenflags'): py.test.skip("requires sys.setdlopenflags()") # Careful with RTLD_GLOBAL. If by chance the FFI is not deleted # promptly, like on PyPy, then other tests may see the same # exported symbols as well. So we must not export a simple name # like 'foo'! old = sys.getdlopenflags() try: ffi1 = FFI() ffi1.cdef("int foo_verify_dlopen_flags_1;") sys.setdlopenflags(ffi1.RTLD_GLOBAL | ffi1.RTLD_NOW) lib1 = ffi1.verify("int foo_verify_dlopen_flags_1;") finally: sys.setdlopenflags(old) ffi2 = FFI() ffi2.cdef("int *getptr(void);") lib2 = ffi2.verify(""" extern int foo_verify_dlopen_flags_1; static int *getptr(void) { return &foo_verify_dlopen_flags_1; } """) p = lib2.getptr() assert ffi1.addressof(lib1, 'foo_verify_dlopen_flags_1') == p def test_consider_not_implemented_function_type(): ffi = FFI() ffi.cdef("typedef union { int a; float b; } Data;" "typedef struct { int a:2; } MyStr;" "typedef void (*foofunc_t)(Data);" "typedef Data (*bazfunc_t)(void);" "typedef MyStr (*barfunc_t)(void);") fooptr = ffi.cast("foofunc_t", 123) bazptr = ffi.cast("bazfunc_t", 123) barptr = ffi.cast("barfunc_t", 123) # assert did not crash so far e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *")) assert str(e.value) == ( "ctype 'Data' (size 4) not supported as argument") e = py.test.raises(NotImplementedError, bazptr) assert str(e.value) == ( "ctype 'Data' (size 4) not supported as return value") e = py.test.raises(NotImplementedError, barptr) assert str(e.value) == ( "ctype 'MyStr' not supported as return value " "(it is a struct with bit fields)") def test_verify_extra_arguments(): ffi = FFI() ffi.cdef("#define ABA ...") lib = ffi.verify("", define_macros=[('ABA', '42')]) assert lib.ABA == 42 def test_implicit_unicode_on_windows(): from cffi import FFIError if sys.platform != 'win32': py.test.skip("win32-only test") ffi = FFI() e = py.test.raises(FFIError, ffi.cdef, "int foo(LPTSTR);") assert str(e.value) == ("The Windows type 'LPTSTR' is only available after" " you call ffi.set_unicode()") for with_unicode in [True, False]: ffi = FFI() ffi.set_unicode(with_unicode) ffi.cdef(""" DWORD GetModuleFileName(HMODULE hModule, LPTSTR lpFilename, DWORD nSize); """) lib = ffi.verify(""" #include """, libraries=['Kernel32']) outbuf = ffi.new("TCHAR[]", 200) n = lib.GetModuleFileName(ffi.NULL, outbuf, 500) assert 0 < n < 500 for i in range(n): #print repr(outbuf[i]) assert ord(outbuf[i]) != 0 assert ord(outbuf[n]) == 0 assert ord(outbuf[0]) < 128 # should be a letter, or '\' def test_define_known_value(): ffi = FFI() ffi.cdef("#define FOO 0x123") lib = ffi.verify("#define FOO 0x123") assert lib.FOO == 0x123 def test_define_wrong_value(): ffi = FFI() ffi.cdef("#define FOO 123") lib = ffi.verify("#define FOO 124") # used to complain e = py.test.raises(ffi.error, "lib.FOO") assert str(e.value) == ("the C compiler says 'FOO' is equal to 124 (0x7c)," " but the cdef disagrees") def test_some_integer_type_for_issue73(): ffi = FFI() ffi.cdef(""" typedef int... AnIntegerWith32Bits; typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void); AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger); """) lib = ffi.verify(""" #ifdef __LP64__ typedef int AnIntegerWith32Bits; #else typedef long AnIntegerWith32Bits; #endif typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void); AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger f) { return f(); } """) @ffi.callback("AFunctionReturningInteger") def add(): return 3 + 4 x = lib.InvokeFunction(add) assert x == 7 def test_unsupported_some_primitive_types(): ffi = FFI() py.test.raises(FFIError, ffi.cdef, """typedef void... foo_t;""") # ffi.cdef("typedef int... foo_t;") py.test.raises(VerificationError, ffi.verify, "typedef float foo_t;") def test_windows_dllimport_data(): if sys.platform != 'win32': py.test.skip("Windows only") from testing.udir import udir tmpfile = udir.join('dllimport_data.c') tmpfile.write('int my_value = 42;\n') ffi = FFI() ffi.cdef("int my_value;") lib = ffi.verify("extern __declspec(dllimport) int my_value;", sources = [str(tmpfile)]) assert lib.my_value == 42 def test_macro_var(): ffi = FFI() ffi.cdef("int myarray[50], my_value;") lib = ffi.verify(""" int myarray[50]; int *get_my_value(void) { static int index = 0; return &myarray[index++]; } #define my_value (*get_my_value()) """) assert lib.my_value == 0 # [0] lib.my_value = 42 # [1] assert lib.myarray[1] == 42 assert lib.my_value == 0 # [2] lib.myarray[3] = 63 assert lib.my_value == 63 # [3] p = ffi.addressof(lib, 'my_value') # [4] assert p[-1] == 63 assert p[0] == 0 assert p == lib.myarray + 4 p[1] = 82 assert lib.my_value == 82 # [5] def test_const_pointer_to_pointer(): ffi = FFI() ffi.cdef("struct s { char *const *a; };") ffi.verify("struct s { char *const *a; };") def test_share_FILE(): ffi1 = FFI() ffi1.cdef("void do_stuff(FILE *);") lib1 = ffi1.verify("void do_stuff(FILE *f) { (void)f; }") ffi2 = FFI() ffi2.cdef("FILE *barize(void);") lib2 = ffi2.verify("FILE *barize(void) { return NULL; }") lib1.do_stuff(lib2.barize()) def test_win_common_types(): if sys.platform != 'win32': py.test.skip("Windows only") ffi = FFI() ffi.set_unicode(True) ffi.verify("") assert ffi.typeof("PBYTE") is ffi.typeof("unsigned char *") if sys.maxsize > 2**32: expected = "unsigned long long" else: expected = "unsigned int" assert ffi.typeof("UINT_PTR") is ffi.typeof(expected) assert ffi.typeof("PTSTR") is ffi.typeof("wchar_t *") cffi-1.5.2/testing/cffi1/test_new_ffi_1.py0000664000175000017500000017474212657646311020621 0ustar arigoarigo00000000000000import py import platform, imp import sys, os, ctypes import cffi from testing.udir import udir from testing.support import * from cffi.recompiler import recompile from cffi.cffi_opcode import PRIMITIVE_TO_INDEX 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) def setup_module(): global ffi, construction_params ffi1 = cffi.FFI() DEFS = r""" struct repr { short a, b, c; }; struct simple { int a; short b, c; }; struct array { int a[2]; char b[3]; }; struct recursive { int value; struct recursive *next; }; union simple_u { int a; short b, c; }; union init_u { char a; int b; }; struct four_s { int a; short b, c, d; }; union four_u { int a; short b, c, d; }; struct string { const char *name; }; struct ustring { const wchar_t *name; }; struct voidp { void *p; int *q; short *r; }; struct ab { int a, b; }; struct abc { int a, b, c; }; /* don't use A0, B0, CC0, D0 because termios.h might be included and it has its own #defines for these names */ enum foq { cffiA0, cffiB0, cffiCC0, cffiD0 }; enum bar { A1, B1=-2, CC1, D1, E1 }; enum baz { A2=0x1000, B2=0x2000 }; enum foo2 { A3, B3, C3, D3 }; struct bar_with_e { enum foo2 e; }; enum noncont { A4, B4=42, C4 }; enum etypes {A5='!', B5='\'', C5=0x10, D5=010, E5=- 0x10, F5=-010}; typedef enum { Value0 = 0 } e_t, *pe_t; enum e_noninj { AA3=0, BB3=0, CC3=0, DD3=0 }; enum e_prev { AA4, BB4=2, CC4=4, DD4=BB4, EE4, FF4=CC4, GG4=FF4 }; struct nesting { struct abc d, e; }; struct array2 { int a, b; int c[99]; }; struct align { char a; short b; char c; }; struct bitfield { int a:10, b:20, c:3; }; typedef enum { AA2, BB2, CC2 } foo_e_t; typedef struct { foo_e_t f:2; } bfenum_t; typedef struct { int a; } anon_foo_t; typedef struct { char b, c; } anon_bar_t; typedef struct named_foo_s { int a; } named_foo_t, *named_foo_p; typedef struct { int a; } unnamed_foo_t, *unnamed_foo_p; struct nonpacked { char a; int b; }; struct array0 { int len; short data[0]; }; struct array_no_length { int x; int a[]; }; struct nested_anon { struct { int a, b; }; union { int c, d; }; }; struct nested_field_ofs_s { struct { int a; char b; }; union { char c; }; }; union nested_anon_u { struct { int a, b; }; union { int c, d; }; }; struct abc50 { int a, b; int c[50]; }; struct ints_and_bitfield { int a,b,c,d,e; int x:1; }; """ DEFS_PACKED = """ struct is_packed { char a; int b; } /*here*/; """ if sys.platform == "win32": DEFS = DEFS.replace('data[0]', 'data[1]') # not supported CCODE = (DEFS + "\n#pragma pack(push,1)\n" + DEFS_PACKED + "\n#pragma pack(pop)\n") else: CCODE = (DEFS + DEFS_PACKED.replace('/*here*/', '__attribute__((packed))')) ffi1.cdef(DEFS) ffi1.cdef(DEFS_PACKED, packed=True) ffi1.set_source("test_new_ffi_1", CCODE) outputfilename = recompile(ffi1, "test_new_ffi_1", CCODE, tmpdir=str(udir)) module = imp.load_dynamic("test_new_ffi_1", outputfilename) ffi = module.ffi construction_params = (ffi1, CCODE) class TestNewFFI1: def test_integer_ranges(self): 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): 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): 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): 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): p = ffi.new("int[10]") # the object was zero-initialized: for i in range(10): assert p[i] == 0 def test_array_indexing(self): 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): # 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): 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): 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): 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): 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): 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 = "" 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 repr *") assert repr(p) == "" % ( 3*SIZE_OF_SHORT) assert repr(ffi.typeof(p)) == typerepr % "struct repr *" # 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): 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): 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): s = ffi.new("struct simple*") 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 simple*", [-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 simple*", [1, 2, 3, 4]) def test_constructor_struct_from_dict(self): s = ffi.new("struct simple*", {'b': 123, 'c': 456}) assert s.a == 0 assert s.b == 123 assert s.c == 456 py.test.raises(KeyError, ffi.new, "struct simple*", {'d': 456}) def test_struct_pointer(self): s = ffi.new("struct simple*") 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): py.test.raises(ffi.error, ffi.new, "struct baz*") # should 'ffi.new("struct baz **") work? it used to, but it was # not particularly useful... py.test.raises(ffi.error, ffi.new, "struct baz**") def test_pointer_to_struct(self): s = ffi.new("struct simple *") s.a = -42 assert s[0].a == -42 p = ffi.new("struct simple **", 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): s = ffi.new("struct array *", [[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): s = ffi.new("struct recursive*") t = ffi.new("struct recursive*") s.value = 123 s.next = t t.value = 456 assert s.value == 123 assert s.next.value == 456 def test_union_simple(self): u = ffi.new("union simple_u*") 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 simple_u*", [-2]) assert u.a == -2 py.test.raises((AttributeError, TypeError), "del u.a") assert repr(u) == "" % ( SIZE_OF_INT,) def test_union_opaque(self): py.test.raises(ffi.error, ffi.new, "union baz*") # should 'ffi.new("union baz **") work? it used to, but it was # not particularly useful... py.test.raises(ffi.error, ffi.new, "union baz**") def test_union_initializer(self): py.test.raises(TypeError, ffi.new, "union init_u*", b'A') py.test.raises(TypeError, ffi.new, "union init_u*", 5) py.test.raises(ValueError, ffi.new, "union init_u*", [b'A', 5]) u = ffi.new("union init_u*", [b'A']) assert u.a == b'A' py.test.raises(TypeError, ffi.new, "union init_u*", [1005]) u = ffi.new("union init_u*", {'b': 12345}) assert u.b == 12345 u = ffi.new("union init_u*", []) assert u.a == b'\x00' assert u.b == 0 def test_sizeof_type(self): for c_type, expected_size in [ ('char', 1), ('unsigned int', 4), ('char *', SIZE_OF_PTR), ('int[5]', 20), ('struct four_s', 12), ('union four_u', 4), ]: size = ffi.sizeof(c_type) assert size == expected_size, (size, expected_size, ctype) def test_sizeof_cdata(self): 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): 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): 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): 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): 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, in the declaration of 'struct string' t = ffi.new("const char[]", b"testing") s = ffi.new("struct string*", [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 self.check_wchar_t(ffi) t = ffi.new("const wchar_t[]", u+"testing") s = ffi.new("struct ustring*", [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): 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) # s = ffi.new("struct voidp *") 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): 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("" % "int(*(*)(int))(int)" def test_functionptr_voidptr_return(self): def cb(): return ffi.NULL p = ffi.callback("void*(*)()", cb) res = p() assert res is not None assert res == ffi.NULL int_ptr = ffi.new('int*') void_ptr = ffi.cast('void*', int_ptr) def cb(): return void_ptr p = ffi.callback("void*(*)()", cb) res = p() assert res == void_ptr def test_functionptr_intptr_return(self): def cb(): return ffi.NULL p = ffi.callback("int*(*)()", cb) res = p() assert res == ffi.NULL int_ptr = ffi.new('int*') def cb(): return int_ptr p = ffi.callback("int*(*)()", cb) res = p() assert repr(res).startswith("" assert repr(ffi.cast("enum foq", -1)) == ( # enums are unsigned, if "") or ( # they contain no neg value sys.platform == "win32") # (but not on msvc) # enum baz { A2=0x1000, B2=0x2000 }; assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A2" assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B2" def test_enum_in_struct(self): # enum foo2 { A3, B3, C3, D3 }; # struct bar_with_e { enum foo2 e; }; s = ffi.new("struct bar_with_e *") 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 foo2", -1) assert s.e in (4294967295, -1) # two choices assert s[0].e in (4294967295, -1) s.e = s.e py.test.raises(TypeError, "s.e = 'B3'") 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): # enum noncont { A4, B4=42, C4 }; assert ffi.string(ffi.cast("enum noncont", 0)) == "A4" assert ffi.string(ffi.cast("enum noncont", 42)) == "B4" assert ffi.string(ffi.cast("enum noncont", 43)) == "C4" invalid_value = ffi.cast("enum noncont", 2) assert int(invalid_value) == 2 assert ffi.string(invalid_value) == "2" def test_enum_char_hex_oct(self): # enum etypes {A5='!', B5='\'', C5=0x10, D5=010, E5=- 0x10, F5=-010}; assert ffi.string(ffi.cast("enum etypes", ord('!'))) == "A5" assert ffi.string(ffi.cast("enum etypes", ord("'"))) == "B5" assert ffi.string(ffi.cast("enum etypes", 16)) == "C5" assert ffi.string(ffi.cast("enum etypes", 8)) == "D5" assert ffi.string(ffi.cast("enum etypes", -16)) == "E5" assert ffi.string(ffi.cast("enum etypes", -8)) == "F5" def test_array_of_struct(self): s = ffi.new("struct ab[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): p = ffi.new("int(**)[5]") assert repr(p) == "" % SIZE_OF_PTR def test_iterate_array(self): 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): # struct abc { int a, b, c; }; assert ffi.offsetof("struct abc", "a") == 0 assert ffi.offsetof("struct abc", "b") == 4 assert ffi.offsetof("struct abc", "c") == 8 def test_offsetof_nested(self): # struct nesting { struct abc d, e; }; assert ffi.offsetof("struct nesting", "e") == 12 py.test.raises(KeyError, ffi.offsetof, "struct nesting", "e.a") assert ffi.offsetof("struct nesting", "e", "a") == 12 assert ffi.offsetof("struct nesting", "e", "b") == 16 assert ffi.offsetof("struct nesting", "e", "c") == 20 def test_offsetof_array(self): assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int") assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int") # struct array2 { int a, b; int c[99]; }; assert ffi.offsetof("struct array2", "c") == 2 * ffi.sizeof("int") assert ffi.offsetof("struct array2", "c", 0) == 2 * ffi.sizeof("int") assert ffi.offsetof("struct array2", "c", 51) == 53 * ffi.sizeof("int") def test_alignof(self): # struct align { char a; short b; char c; }; assert ffi.alignof("int") == 4 assert ffi.alignof("double") in (4, 8) assert ffi.alignof("struct align") == 2 def test_bitfield(self): # struct bitfield { int a:10, b:20, c:3; }; assert ffi.sizeof("struct bitfield") == 8 s = ffi.new("struct bitfield *") 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): # typedef enum { AA1, BB1, CC1 } foo_e_t; # typedef struct { foo_e_t f:2; } bfenum_t; if sys.platform == "win32": py.test.skip("enums are not unsigned") s = ffi.new("bfenum_t *") s.f = 2 assert s.f == 2 def test_anonymous_struct(self): # typedef struct { int a; } anon_foo_t; # typedef struct { char b, c; } anon_bar_t; f = ffi.new("anon_foo_t *", [12345]) b = ffi.new("anon_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): 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): 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): 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): 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): 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): 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): 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): # struct array { int a[2]; char b[3]; }; p = ffi.new("struct array *") p.a[1] = 5 assert p.a[1] == 5 assert repr(p.a).startswith("') result.append(i) return result def parsex(input): result = parse(input) def str_if_int(x): if isinstance(x, str): return x return '%d,%d' % (x & 255, x >> 8) return ' '.join(map(str_if_int, result)) def parse_error(input, expected_msg, expected_location): e = py.test.raises(ParseError, parse, input) assert e.value.args[0] == expected_msg assert e.value.args[1] == expected_location def make_getter(name): opcode = getattr(lib, '_CFFI_OP_' + name) def getter(value): return opcode | (value << 8) return getter Prim = make_getter('PRIMITIVE') Pointer = make_getter('POINTER') Array = make_getter('ARRAY') OpenArray = make_getter('OPEN_ARRAY') NoOp = make_getter('NOOP') Func = make_getter('FUNCTION') FuncEnd = make_getter('FUNCTION_END') Struct = make_getter('STRUCT_UNION') Enum = make_getter('ENUM') Typename = make_getter('TYPENAME') def test_simple(): for simple_type, expected in [ ("int", lib._CFFI_PRIM_INT), ("signed int", lib._CFFI_PRIM_INT), (" long ", lib._CFFI_PRIM_LONG), ("long int", lib._CFFI_PRIM_LONG), ("unsigned short", lib._CFFI_PRIM_USHORT), ("long double", lib._CFFI_PRIM_LONGDOUBLE), ]: assert parse(simple_type) == ['->', Prim(expected)] def test_array(): assert parse("int[5]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5] assert parse("int[]") == [Prim(lib._CFFI_PRIM_INT), '->', OpenArray(0)] assert parse("int[5][8]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(3), 5, Array(0), 8] assert parse("int[][8]") == [Prim(lib._CFFI_PRIM_INT), '->', OpenArray(2), Array(0), 8] def test_pointer(): assert parse("int*") == [Prim(lib._CFFI_PRIM_INT), '->', Pointer(0)] assert parse("int***") == [Prim(lib._CFFI_PRIM_INT), Pointer(0), Pointer(1), '->', Pointer(2)] def test_grouping(): assert parse("int*[]") == [Prim(lib._CFFI_PRIM_INT), Pointer(0), '->', OpenArray(1)] assert parse("int**[][8]") == [Prim(lib._CFFI_PRIM_INT), Pointer(0), Pointer(1), '->', OpenArray(4), Array(2), 8] assert parse("int(*)[]") == [Prim(lib._CFFI_PRIM_INT), NoOp(3), '->', Pointer(1), OpenArray(0)] assert parse("int(*)[][8]") == [Prim(lib._CFFI_PRIM_INT), NoOp(3), '->', Pointer(1), OpenArray(4), Array(0), 8] assert parse("int**(**)") == [Prim(lib._CFFI_PRIM_INT), Pointer(0), Pointer(1), NoOp(2), Pointer(3), '->', Pointer(4)] assert parse("int**(**)[]") == [Prim(lib._CFFI_PRIM_INT), Pointer(0), Pointer(1), NoOp(6), Pointer(3), '->', Pointer(4), OpenArray(2)] def test_simple_function(): assert parse("int()") == [Prim(lib._CFFI_PRIM_INT), '->', Func(0), FuncEnd(0), 0] assert parse("int(int)") == [Prim(lib._CFFI_PRIM_INT), '->', Func(0), NoOp(4), FuncEnd(0), Prim(lib._CFFI_PRIM_INT)] assert parse("int(long, char)") == [ Prim(lib._CFFI_PRIM_INT), '->', Func(0), NoOp(5), NoOp(6), FuncEnd(0), Prim(lib._CFFI_PRIM_LONG), Prim(lib._CFFI_PRIM_CHAR)] assert parse("int(int*)") == [Prim(lib._CFFI_PRIM_INT), '->', Func(0), NoOp(5), FuncEnd(0), Prim(lib._CFFI_PRIM_INT), Pointer(4)] assert parse("int*(void)") == [Prim(lib._CFFI_PRIM_INT), Pointer(0), '->', Func(1), FuncEnd(0), 0] assert parse("int(int, ...)") == [Prim(lib._CFFI_PRIM_INT), '->', Func(0), NoOp(5), FuncEnd(1), 0, Prim(lib._CFFI_PRIM_INT)] def test_internal_function(): assert parse("int(*)()") == [Prim(lib._CFFI_PRIM_INT), NoOp(3), '->', Pointer(1), Func(0), FuncEnd(0), 0] assert parse("int(*())[]") == [Prim(lib._CFFI_PRIM_INT), NoOp(6), Pointer(1), '->', Func(2), FuncEnd(0), 0, OpenArray(0)] assert parse("int(char(*)(long, short))") == [ Prim(lib._CFFI_PRIM_INT), '->', Func(0), NoOp(6), FuncEnd(0), Prim(lib._CFFI_PRIM_CHAR), NoOp(7), Pointer(5), Func(4), NoOp(11), NoOp(12), FuncEnd(0), Prim(lib._CFFI_PRIM_LONG), Prim(lib._CFFI_PRIM_SHORT)] def test_fix_arg_types(): assert parse("int(char(long, short))") == [ Prim(lib._CFFI_PRIM_INT), '->', Func(0), Pointer(5), FuncEnd(0), Prim(lib._CFFI_PRIM_CHAR), Func(4), NoOp(9), NoOp(10), FuncEnd(0), Prim(lib._CFFI_PRIM_LONG), Prim(lib._CFFI_PRIM_SHORT)] assert parse("int(char[])") == [ Prim(lib._CFFI_PRIM_INT), '->', Func(0), Pointer(4), FuncEnd(0), Prim(lib._CFFI_PRIM_CHAR), OpenArray(4)] def test_enum(): for i in range(len(enum_names)): assert parse("enum %s" % (enum_names[i],)) == ['->', Enum(i)] assert parse("enum %s*" % (enum_names[i],)) == [Enum(i), '->', Pointer(0)] def test_error(): parse_error("short short int", "'short' after another 'short' or 'long'", 6) parse_error("long long long", "'long long long' is too long", 10) parse_error("short long", "'long' after 'short'", 6) parse_error("signed unsigned int", "multiple 'signed' or 'unsigned'", 7) parse_error("unsigned signed int", "multiple 'signed' or 'unsigned'", 9) parse_error("long char", "invalid combination of types", 5) parse_error("short char", "invalid combination of types", 6) parse_error("signed void", "invalid combination of types", 7) parse_error("unsigned struct", "invalid combination of types", 9) # parse_error("", "identifier expected", 0) parse_error("]", "identifier expected", 0) parse_error("*", "identifier expected", 0) parse_error("int ]**", "unexpected symbol", 4) parse_error("char char", "unexpected symbol", 5) parse_error("int(int]", "expected ')'", 7) parse_error("int(*]", "expected ')'", 5) parse_error("int(]", "identifier expected", 4) parse_error("int[?]", "expected a positive integer constant", 4) parse_error("int[24)", "expected ']'", 6) parse_error("struct", "struct or union name expected", 6) parse_error("struct 24", "struct or union name expected", 7) parse_error("int[5](*)", "unexpected symbol", 6) parse_error("int a(*)", "identifier expected", 6) parse_error("int[123456789012345678901234567890]", "number too large", 4) def test_number_too_large(): num_max = sys.maxsize assert parse("char[%d]" % num_max) == [Prim(lib._CFFI_PRIM_CHAR), '->', Array(0), num_max] parse_error("char[%d]" % (num_max + 1), "number too large", 5) def test_complexity_limit(): parse_error("int" + "[]" * 2500, "internal type complexity limit reached", 202) def test_struct(): for i in range(len(struct_names)): if i == 3: tag = "union" else: tag = "struct" assert parse("%s %s" % (tag, struct_names[i])) == ['->', Struct(i)] assert parse("%s %s*" % (tag, struct_names[i])) == [Struct(i), '->', Pointer(0)] def test_exchanging_struct_union(): parse_error("union %s" % (struct_names[0],), "wrong kind of tag: struct vs union", 6) parse_error("struct %s" % (struct_names[3],), "wrong kind of tag: struct vs union", 7) def test_identifier(): for i in range(len(identifier_names)): assert parse("%s" % (identifier_names[i])) == ['->', Typename(i)] assert parse("%s*" % (identifier_names[i])) == [Typename(i), '->', Pointer(0)] def test_cffi_opcode_sync(): import cffi.model for name in dir(lib): if name.startswith('_CFFI_'): assert getattr(cffi_opcode, name[6:]) == getattr(lib, name) assert sorted(cffi_opcode.PRIMITIVE_TO_INDEX.keys()) == ( sorted(cffi.model.PrimitiveType.ALL_PRIMITIVE_TYPES.keys())) def test_array_length_from_constant(): parse_error("int[UNKNOWN]", "expected a positive integer constant", 4) assert parse("int[FIVE]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5] assert parse("int[ZERO]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 0] parse_error("int[NEG]", "expected a positive integer constant", 4) def test_various_constant_exprs(): def array(n): return [Prim(lib._CFFI_PRIM_CHAR), '->', Array(0), n] assert parse("char[21]") == array(21) assert parse("char[0x10]") == array(16) assert parse("char[0X21]") == array(33) assert parse("char[0Xb]") == array(11) assert parse("char[0x1C]") == array(0x1C) assert parse("char[0xc6]") == array(0xC6) assert parse("char[010]") == array(8) assert parse("char[021]") == array(17) parse_error("char[08]", "invalid number", 5) parse_error("char[1C]", "invalid number", 5) parse_error("char[0C]", "invalid number", 5) # not supported (really obscure): # "char[+5]" # "char['A']" def test_stdcall_cdecl(): assert parse("int __stdcall(int)") == [Prim(lib._CFFI_PRIM_INT), '->', Func(0), NoOp(4), FuncEnd(2), Prim(lib._CFFI_PRIM_INT)] assert parse("int __stdcall func(int)") == parse("int __stdcall(int)") assert parse("int (__stdcall *)()") == [Prim(lib._CFFI_PRIM_INT), NoOp(3), '->', Pointer(1), Func(0), FuncEnd(2), 0] assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()") parse_error("__stdcall int", "identifier expected", 0) parse_error("__cdecl int", "identifier expected", 0) parse_error("int __stdcall", "expected '('", 13) parse_error("int __cdecl", "expected '('", 11) cffi-1.5.2/testing/cffi1/test_dlopen.py0000664000175000017500000001572312657646311020236 0ustar arigoarigo00000000000000import py from cffi import FFI, VerificationError, CDefError from cffi.recompiler import make_py_source from testing.udir import udir def test_simple(): ffi = FFI() ffi.cdef("int close(int); static const int BB = 42; int somevar;") target = udir.join('test_simple.py') make_py_source(ffi, 'test_simple', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_simple', _version = 0x2601, _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F', _globals = (b'\xFF\xFF\xFF\x1FBB',42,b'\x00\x00\x00\x23close',0,b'\x00\x00\x01\x21somevar',0), ) """ def test_global_constant(): ffi = FFI() ffi.cdef("static const long BB; static const float BF = 12;") target = udir.join('test_valid_global_constant.py') make_py_source(ffi, 'test_valid_global_constant', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_valid_global_constant', _version = 0x2601, _types = b'\x00\x00\x0D\x01\x00\x00\x09\x01', _globals = (b'\x00\x00\x01\x25BB',0,b'\x00\x00\x00\x25BF',0), ) """ def test_invalid_global_constant_3(): ffi = FFI() e = py.test.raises(CDefError, ffi.cdef, "#define BB 12.34") assert str(e.value).startswith( "only supports one of the following syntax:") def test_invalid_dotdotdot_in_macro(): ffi = FFI() ffi.cdef("#define FOO ...") target = udir.join('test_invalid_dotdotdot_in_macro.py') e = py.test.raises(VerificationError, make_py_source, ffi, 'test_invalid_dotdotdot_in_macro', str(target)) assert str(e.value) == ("macro FOO: cannot use the syntax '...' in " "'#define FOO ...' when using the ABI mode") def test_typename(): ffi = FFI() ffi.cdef("typedef int foobar_t;") target = udir.join('test_typename.py') make_py_source(ffi, 'test_typename', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_typename', _version = 0x2601, _types = b'\x00\x00\x07\x01', _typenames = (b'\x00\x00\x00\x00foobar_t',), ) """ def test_enum(): ffi = FFI() ffi.cdef("enum myenum_e { AA, BB, CC=-42 };") target = udir.join('test_enum.py') make_py_source(ffi, 'test_enum', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_enum', _version = 0x2601, _types = b'\x00\x00\x00\x0B', _globals = (b'\xFF\xFF\xFF\x0BAA',0,b'\xFF\xFF\xFF\x0BBB',1,b'\xFF\xFF\xFF\x0BCC',-42), _enums = (b'\x00\x00\x00\x00\x00\x00\x00\x15myenum_e\x00AA,BB,CC',), ) """ def test_struct(): ffi = FFI() ffi.cdef("struct foo_s { int a; signed char b[]; }; struct bar_s;") target = udir.join('test_struct.py') make_py_source(ffi, 'test_struct', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_struct', _version = 0x2601, _types = b'\x00\x00\x07\x01\x00\x00\x03\x01\x00\x00\x01\x07\x00\x00\x00\x09\x00\x00\x01\x09', _struct_unions = ((b'\x00\x00\x00\x03\x00\x00\x00\x10bar_s',),(b'\x00\x00\x00\x04\x00\x00\x00\x02foo_s',b'\x00\x00\x00\x11a',b'\x00\x00\x02\x11b')), ) """ def test_include(): ffi = FFI() ffi.cdef("#define ABC 123") ffi.set_source('test_include', None) target = udir.join('test_include.py') make_py_source(ffi, 'test_include', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_include', _version = 0x2601, _types = b'', _globals = (b'\xFF\xFF\xFF\x1FABC',123,), ) """ # ffi2 = FFI() ffi2.include(ffi) target2 = udir.join('test2_include.py') make_py_source(ffi2, 'test2_include', str(target2)) assert target2.read() == r"""# auto-generated file import _cffi_backend from test_include import ffi as _ffi0 ffi = _cffi_backend.FFI('test2_include', _version = 0x2601, _types = b'', _includes = (_ffi0,), ) """ def test_negative_constant(): ffi = FFI() ffi.cdef("static const int BB = -42;") target = udir.join('test_negative_constant.py') make_py_source(ffi, 'test_negative_constant', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_negative_constant', _version = 0x2601, _types = b'', _globals = (b'\xFF\xFF\xFF\x1FBB',-42,), ) """ def test_struct_included(): baseffi = FFI() baseffi.cdef("struct foo_s { int x; };") baseffi.set_source('test_struct_included_base', None) # ffi = FFI() ffi.include(baseffi) target = udir.join('test_struct_included.py') make_py_source(ffi, 'test_struct_included', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend from test_struct_included_base import ffi as _ffi0 ffi = _cffi_backend.FFI('test_struct_included', _version = 0x2601, _types = b'\x00\x00\x00\x09', _struct_unions = ((b'\x00\x00\x00\x00\x00\x00\x00\x08foo_s',),), _includes = (_ffi0,), ) """ def test_no_cross_include(): baseffi = FFI() baseffi.set_source('test_no_cross_include_base', "..source..") # ffi = FFI() ffi.include(baseffi) target = udir.join('test_no_cross_include.py') py.test.raises(VerificationError, make_py_source, ffi, 'test_no_cross_include', str(target)) def test_array(): ffi = FFI() ffi.cdef("typedef int32_t my_array_t[42];") target = udir.join('test_array.py') make_py_source(ffi, 'test_array', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_array', _version = 0x2601, _types = b'\x00\x00\x15\x01\x00\x00\x00\x05\x00\x00\x00\x2A', _typenames = (b'\x00\x00\x00\x01my_array_t',), ) """ def test_array_overflow(): ffi = FFI() ffi.cdef("typedef int32_t my_array_t[3000000000];") target = udir.join('test_array_overflow.py') py.test.raises(OverflowError, make_py_source, ffi, 'test_array_overflow', str(target)) def test_global_var(): ffi = FFI() ffi.cdef("int myglob;") target = udir.join('test_global_var.py') make_py_source(ffi, 'test_global_var', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_global_var', _version = 0x2601, _types = b'\x00\x00\x07\x01', _globals = (b'\x00\x00\x00\x21myglob',0,), ) """ def test_bitfield(): ffi = FFI() ffi.cdef("struct foo_s { int y:10; short x:5; };") target = udir.join('test_bitfield.py') make_py_source(ffi, 'test_bitfield', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_bitfield', _version = 0x2601, _types = b'\x00\x00\x07\x01\x00\x00\x05\x01\x00\x00\x00\x09', _struct_unions = ((b'\x00\x00\x00\x02\x00\x00\x00\x02foo_s',b'\x00\x00\x00\x13\x00\x00\x00\x0Ay',b'\x00\x00\x01\x13\x00\x00\x00\x05x'),), ) """ cffi-1.5.2/testing/cffi1/__init__.py0000664000175000017500000000000012657646311017433 0ustar arigoarigo00000000000000cffi-1.5.2/testing/cffi1/test_ffi_obj.py0000664000175000017500000003743112657646311020353 0ustar arigoarigo00000000000000import py, sys import _cffi_backend as _cffi1_backend def test_ffi_new(): ffi = _cffi1_backend.FFI() p = ffi.new("int *") p[0] = -42 assert p[0] == -42 assert type(ffi) is ffi.__class__ is _cffi1_backend.FFI def test_ffi_subclass(): class FOO(_cffi1_backend.FFI): def __init__(self, x): self.x = x foo = FOO(42) assert foo.x == 42 p = foo.new("int *") assert p[0] == 0 assert type(foo) is foo.__class__ is FOO def test_ffi_no_argument(): py.test.raises(TypeError, _cffi1_backend.FFI, 42) def test_ffi_cache_type(): ffi = _cffi1_backend.FFI() t1 = ffi.typeof("int **") t2 = ffi.typeof("int *") assert t2.item is t1.item.item assert t2 is t1.item assert ffi.typeof("int[][10]") is ffi.typeof("int[][10]") assert ffi.typeof("int(*)()") is ffi.typeof("int(*)()") def test_ffi_type_not_immortal(): import weakref, gc ffi = _cffi1_backend.FFI() t1 = ffi.typeof("int **") t2 = ffi.typeof("int *") w1 = weakref.ref(t1) w2 = weakref.ref(t2) del t1, ffi gc.collect() assert w1() is None assert w2() is t2 ffi = _cffi1_backend.FFI() assert ffi.typeof(ffi.new("int **")[0]) is t2 # ffi = _cffi1_backend.FFI() t1 = ffi.typeof("int ***") t2 = ffi.typeof("int **") w1 = weakref.ref(t1) w2 = weakref.ref(t2) del t2, ffi gc.collect() assert w1() is t1 assert w2() is not None # kept alive by t1 ffi = _cffi1_backend.FFI() assert ffi.typeof("int * *") is t1.item def test_ffi_cache_type_globally(): ffi1 = _cffi1_backend.FFI() ffi2 = _cffi1_backend.FFI() t1 = ffi1.typeof("int *") t2 = ffi2.typeof("int *") assert t1 is t2 def test_ffi_invalid(): ffi = _cffi1_backend.FFI() # array of 10 times an "int[]" is invalid py.test.raises(ValueError, ffi.typeof, "int[10][]") def test_ffi_docstrings(): # check that all methods of the FFI class have a docstring. check_type = type(_cffi1_backend.FFI.new) for methname in dir(_cffi1_backend.FFI): if not methname.startswith('_'): method = getattr(_cffi1_backend.FFI, methname) if isinstance(method, check_type): assert method.__doc__, "method FFI.%s() has no docstring" % ( methname,) def test_ffi_NULL(): NULL = _cffi1_backend.FFI.NULL assert _cffi1_backend.FFI().typeof(NULL).cname == "void *" def test_ffi_no_attr(): ffi = _cffi1_backend.FFI() py.test.raises(AttributeError, "ffi.no_such_name") py.test.raises(AttributeError, "ffi.no_such_name = 42") py.test.raises(AttributeError, "del ffi.no_such_name") def test_ffi_string(): ffi = _cffi1_backend.FFI() p = ffi.new("char[]", init=b"foobar\x00baz") assert ffi.string(p) == b"foobar" assert ffi.string(cdata=p, maxlen=3) == b"foo" def test_ffi_errno(): # xxx not really checking errno, just checking that we can read/write it ffi = _cffi1_backend.FFI() ffi.errno = 42 assert ffi.errno == 42 def test_ffi_alignof(): ffi = _cffi1_backend.FFI() assert ffi.alignof("int") == 4 assert ffi.alignof("int[]") == 4 assert ffi.alignof("int[41]") == 4 assert ffi.alignof("short[41]") == 2 assert ffi.alignof(ffi.new("int[41]")) == 4 assert ffi.alignof(ffi.new("int[]", 41)) == 4 def test_ffi_sizeof(): ffi = _cffi1_backend.FFI() assert ffi.sizeof("int") == 4 py.test.raises(ffi.error, ffi.sizeof, "int[]") assert ffi.sizeof("int[41]") == 41 * 4 assert ffi.sizeof(ffi.new("int[41]")) == 41 * 4 assert ffi.sizeof(ffi.new("int[]", 41)) == 41 * 4 def test_ffi_callback(): ffi = _cffi1_backend.FFI() assert ffi.callback("int(int)", lambda x: x + 42)(10) == 52 assert ffi.callback("int(*)(int)", lambda x: x + 42)(10) == 52 assert ffi.callback("int(int)", lambda x: x + "", -66)(10) == -66 assert ffi.callback("int(int)", lambda x: x + "", error=-66)(10) == -66 def test_ffi_callback_decorator(): ffi = _cffi1_backend.FFI() assert ffi.callback(ffi.typeof("int(*)(int)"))(lambda x: x + 42)(10) == 52 deco = ffi.callback("int(int)", error=-66) assert deco(lambda x: x + "")(10) == -66 assert deco(lambda x: x + 42)(10) == 52 def test_ffi_callback_onerror(): ffi = _cffi1_backend.FFI() seen = [] def oops(*args): seen.append(args) @ffi.callback("int(int)", onerror=oops) def fn1(x): return x + "" assert fn1(10) == 0 @ffi.callback("int(int)", onerror=oops, error=-66) def fn2(x): return x + "" assert fn2(10) == -66 assert len(seen) == 2 exc, val, tb = seen[0] assert exc is TypeError assert isinstance(val, TypeError) assert tb.tb_frame.f_code.co_name == "fn1" exc, val, tb = seen[1] assert exc is TypeError assert isinstance(val, TypeError) assert tb.tb_frame.f_code.co_name == "fn2" # py.test.raises(TypeError, ffi.callback, "int(int)", lambda x: x, onerror=42) # <- not callable def test_ffi_getctype(): ffi = _cffi1_backend.FFI() assert ffi.getctype("int") == "int" assert ffi.getctype("int", 'x') == "int x" assert ffi.getctype("int*") == "int *" assert ffi.getctype("int*", '') == "int *" assert ffi.getctype("int*", 'x') == "int * x" assert ffi.getctype("int", '*') == "int *" assert ffi.getctype("int", replace_with=' * x ') == "int * x" assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *" assert ffi.getctype("int", '[5]') == "int[5]" assert ffi.getctype("int[5]", '[6]') == "int[6][5]" assert ffi.getctype("int[5]", '(*)') == "int(*)[5]" # special-case for convenience: automatically put '()' around '*' assert ffi.getctype("int[5]", '*') == "int(*)[5]" assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]" assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]" def test_addressof(): ffi = _cffi1_backend.FFI() a = ffi.new("int[10]") b = ffi.addressof(a, 5) b[2] = -123 assert a[7] == -123 def test_handle(): ffi = _cffi1_backend.FFI() x = [2, 4, 6] xp = ffi.new_handle(x) assert ffi.typeof(xp) == ffi.typeof("void *") assert ffi.from_handle(xp) is x yp = ffi.new_handle([6, 4, 2]) assert ffi.from_handle(yp) == [6, 4, 2] def test_handle_unique(): ffi = _cffi1_backend.FFI() assert ffi.new_handle(None) is not ffi.new_handle(None) assert ffi.new_handle(None) != ffi.new_handle(None) def test_ffi_cast(): ffi = _cffi1_backend.FFI() assert ffi.cast("int(*)(int)", 0) == ffi.NULL ffi.callback("int(int)") # side-effect of registering this string py.test.raises(ffi.error, ffi.cast, "int(int)", 0) def test_ffi_invalid_type(): ffi = _cffi1_backend.FFI() e = py.test.raises(ffi.error, ffi.cast, "", 0) assert str(e.value) == ("identifier expected\n" "\n" "^") e = py.test.raises(ffi.error, ffi.cast, "struct struct", 0) assert str(e.value) == ("struct or union name expected\n" "struct struct\n" " ^") e = py.test.raises(ffi.error, ffi.cast, "struct never_heard_of_s", 0) assert str(e.value) == ("undefined struct/union name\n" "struct never_heard_of_s\n" " ^") e = py.test.raises(ffi.error, ffi.cast, "\t\n\x01\x1f~\x7f\x80\xff", 0) marks = "?" if sys.version_info < (3,) else "??" assert str(e.value) == ("identifier expected\n" " ??~?%s%s\n" " ^" % (marks, marks)) e = py.test.raises(ffi.error, ffi.cast, "X" * 600, 0) assert str(e.value) == ("undefined type name") def test_ffi_buffer(): ffi = _cffi1_backend.FFI() a = ffi.new("signed char[]", [5, 6, 7]) assert ffi.buffer(a)[:] == b'\x05\x06\x07' assert ffi.buffer(cdata=a, size=2)[:] == b'\x05\x06' def test_ffi_from_buffer(): import array ffi = _cffi1_backend.FFI() a = array.array('H', [10000, 20000, 30000]) c = ffi.from_buffer(a) assert ffi.typeof(c) is ffi.typeof("char[]") ffi.cast("unsigned short *", c)[1] += 500 assert list(a) == [10000, 20500, 30000] def test_memmove(): ffi = _cffi1_backend.FFI() p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678]) ffi.memmove(p, p + 1, 4) assert list(p) == [-2345, -3456, -3456, -4567, -5678] p[2] = 999 ffi.memmove(p + 2, p, 6) assert list(p) == [-2345, -3456, -2345, -3456, 999] ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2) if sys.byteorder == 'little': assert list(p) == [-2345, -3456, -2345, -3456, 0x7271] else: assert list(p) == [-2345, -3456, -2345, -3456, 0x7172] def test_memmove_buffer(): import array ffi = _cffi1_backend.FFI() a = array.array('H', [10000, 20000, 30000]) p = ffi.new("short[]", 5) ffi.memmove(p, a, 6) assert list(p) == [10000, 20000, 30000, 0, 0] ffi.memmove(p + 1, a, 6) assert list(p) == [10000, 10000, 20000, 30000, 0] b = array.array('h', [-1000, -2000, -3000]) ffi.memmove(b, a, 4) assert b.tolist() == [10000, 20000, -3000] assert a.tolist() == [10000, 20000, 30000] p[0] = 999 p[1] = 998 p[2] = 997 p[3] = 996 p[4] = 995 ffi.memmove(b, p, 2) assert b.tolist() == [999, 20000, -3000] ffi.memmove(b, p + 2, 4) assert b.tolist() == [997, 996, -3000] p[2] = -p[2] p[3] = -p[3] ffi.memmove(b, p + 2, 6) assert b.tolist() == [-997, -996, 995] def test_memmove_readonly_readwrite(): ffi = _cffi1_backend.FFI() p = ffi.new("signed char[]", 5) ffi.memmove(p, b"abcde", 3) assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0] ffi.memmove(p, bytearray(b"ABCDE"), 2) assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0] py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3) ba = bytearray(b"xxxxx") ffi.memmove(dest=ba, src=p, n=3) assert ba == bytearray(b"ABcxx") def test_ffi_types(): CData = _cffi1_backend.FFI.CData CType = _cffi1_backend.FFI.CType ffi = _cffi1_backend.FFI() assert isinstance(ffi.cast("int", 42), CData) assert isinstance(ffi.typeof("int"), CType) def test_ffi_getwinerror(): if sys.platform != "win32": py.test.skip("for windows") ffi = _cffi1_backend.FFI() n = (1 << 29) + 42 code, message = ffi.getwinerror(code=n) assert code == n def test_ffi_new_allocator_1(): ffi = _cffi1_backend.FFI() alloc1 = ffi.new_allocator() alloc2 = ffi.new_allocator(should_clear_after_alloc=False) for retry in range(100): p1 = alloc1("int[10]") p2 = alloc2("int[10]") combination = 0 for i in range(10): assert p1[i] == 0 combination |= p2[i] p1[i] = -42 p2[i] = -43 if combination != 0: break del p1, p2 import gc; gc.collect() else: raise AssertionError("cannot seem to get an int[10] not " "completely cleared") def test_ffi_new_allocator_2(): ffi = _cffi1_backend.FFI() seen = [] def myalloc(size): seen.append(size) return ffi.new("char[]", b"X" * size) def myfree(raw): seen.append(raw) alloc1 = ffi.new_allocator(myalloc, myfree) alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree, should_clear_after_alloc=False) p1 = alloc1("int[10]") p2 = alloc2("int[]", 10) assert seen == [40, 40] assert ffi.typeof(p1) == ffi.typeof("int[10]") assert ffi.sizeof(p1) == 40 assert ffi.typeof(p2) == ffi.typeof("int[]") assert ffi.sizeof(p2) == 40 assert p1[5] == 0 assert p2[6] == ord('X') * 0x01010101 raw1 = ffi.cast("char *", p1) raw2 = ffi.cast("char *", p2) del p1, p2 retries = 0 while len(seen) != 4: retries += 1 assert retries <= 5 import gc; gc.collect() assert seen == [40, 40, raw1, raw2] assert repr(seen[2]) == "" assert repr(seen[3]) == "" def test_ffi_new_allocator_3(): ffi = _cffi1_backend.FFI() seen = [] def myalloc(size): seen.append(size) return ffi.new("char[]", b"X" * size) alloc1 = ffi.new_allocator(myalloc) # no 'free' p1 = alloc1("int[10]") assert seen == [40] assert ffi.typeof(p1) == ffi.typeof("int[10]") assert ffi.sizeof(p1) == 40 assert p1[5] == 0 def test_ffi_new_allocator_4(): ffi = _cffi1_backend.FFI() py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None) # def myalloc2(size): raise LookupError alloc2 = ffi.new_allocator(myalloc2) py.test.raises(LookupError, alloc2, "int[5]") # def myalloc3(size): return 42 alloc3 = ffi.new_allocator(myalloc3) e = py.test.raises(TypeError, alloc3, "int[5]") assert str(e.value) == "alloc() must return a cdata object (got int)" # def myalloc4(size): return ffi.cast("int", 42) alloc4 = ffi.new_allocator(myalloc4) e = py.test.raises(TypeError, alloc4, "int[5]") assert str(e.value) == "alloc() must return a cdata pointer, not 'int'" # def myalloc5(size): return ffi.NULL alloc5 = ffi.new_allocator(myalloc5) py.test.raises(MemoryError, alloc5, "int[5]") def test_bool_issue228(): ffi = _cffi1_backend.FFI() fntype = ffi.typeof("int(*callback)(bool is_valid)") assert repr(fntype.args[0]) == "" def test_FILE_issue228(): fntype1 = _cffi1_backend.FFI().typeof("FILE *") fntype2 = _cffi1_backend.FFI().typeof("FILE *") assert repr(fntype1) == "" assert fntype1 is fntype2 def test_cast_from_int_type_to_bool(): ffi = _cffi1_backend.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_init_once(): def do_init(): seen.append(1) return 42 ffi = _cffi1_backend.FFI() seen = [] for i in range(3): res = ffi.init_once(do_init, "tag1") assert res == 42 assert seen == [1] for i in range(3): res = ffi.init_once(do_init, "tag2") assert res == 42 assert seen == [1, 1] def test_init_once_multithread(): if sys.version_info < (3,): import thread else: import _thread as thread import time # def do_init(): print('init!') seen.append('init!') time.sleep(1) seen.append('init done') print('init done') return 7 ffi = _cffi1_backend.FFI() seen = [] for i in range(6): def f(): res = ffi.init_once(do_init, "tag") seen.append(res) thread.start_new_thread(f, ()) time.sleep(1.5) assert seen == ['init!', 'init done'] + 6 * [7] def test_init_once_failure(): def do_init(): seen.append(1) raise ValueError ffi = _cffi1_backend.FFI() seen = [] for i in range(5): py.test.raises(ValueError, ffi.init_once, do_init, "tag") assert seen == [1] * (i + 1) def test_init_once_multithread_failure(): if sys.version_info < (3,): import thread else: import _thread as thread import time def do_init(): seen.append('init!') time.sleep(1) seen.append('oops') raise ValueError ffi = _cffi1_backend.FFI() seen = [] for i in range(3): def f(): py.test.raises(ValueError, ffi.init_once, do_init, "tag") thread.start_new_thread(f, ()) i = 0 while len(seen) < 6: i += 1 assert i < 20 time.sleep(0.51) assert seen == ['init!', 'oops'] * 3 cffi-1.5.2/testing/cffi1/test_recompiler.py0000664000175000017500000017563512657646311021127 0ustar arigoarigo00000000000000 import sys, os, py from cffi import FFI, VerificationError, FFIError from cffi import recompiler from testing.udir import udir from testing.support import u, long from testing.support import FdWriteCapture, StdErrCapture def check_type_table(input, expected_output, included=None): ffi = FFI() if included: ffi1 = FFI() ffi1.cdef(included) ffi.include(ffi1) ffi.cdef(input) recomp = recompiler.Recompiler(ffi, 'testmod') recomp.collect_type_table() assert ''.join(map(str, recomp.cffi_types)) == expected_output def verify(ffi, module_name, source, *args, **kwds): kwds.setdefault('undef_macros', ['NDEBUG']) module_name = '_CFFI_' + module_name ffi.set_source(module_name, source) if not os.environ.get('NO_CPP'): # test the .cpp mode too kwds.setdefault('source_extension', '.cpp') source = 'extern "C" {\n%s\n}' % (source,) else: kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) + ['-Werror']) return recompiler._verify(ffi, module_name, source, *args, **kwds) def test_type_table_func(): check_type_table("double sin(double);", "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)") check_type_table("float sin(double);", "(FUNCTION 3)(PRIMITIVE 14)(FUNCTION_END 0)(PRIMITIVE 13)") check_type_table("float sin(void);", "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 13)") check_type_table("double sin(float); double cos(float);", "(FUNCTION 3)(PRIMITIVE 13)(FUNCTION_END 0)(PRIMITIVE 14)") check_type_table("double sin(float); double cos(double);", "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)" # cos "(FUNCTION 1)(PRIMITIVE 13)(FUNCTION_END 0)") # sin check_type_table("float sin(double); float cos(float);", "(FUNCTION 4)(PRIMITIVE 14)(FUNCTION_END 0)" # sin "(FUNCTION 4)(PRIMITIVE 13)(FUNCTION_END 0)") # cos def test_type_table_use_noop_for_repeated_args(): check_type_table("double sin(double *, double *);", "(FUNCTION 4)(POINTER 4)(NOOP 1)(FUNCTION_END 0)" "(PRIMITIVE 14)") check_type_table("double sin(double *, double *, double);", "(FUNCTION 3)(POINTER 3)(NOOP 1)(PRIMITIVE 14)" "(FUNCTION_END 0)") def test_type_table_dont_use_noop_for_primitives(): check_type_table("double sin(double, double);", "(FUNCTION 1)(PRIMITIVE 14)(PRIMITIVE 14)(FUNCTION_END 0)") def test_type_table_funcptr_as_argument(): check_type_table("int sin(double(float));", "(FUNCTION 6)(PRIMITIVE 13)(FUNCTION_END 0)" "(FUNCTION 7)(POINTER 0)(FUNCTION_END 0)" "(PRIMITIVE 14)(PRIMITIVE 7)") def test_type_table_variadic_function(): check_type_table("int sin(int, ...);", "(FUNCTION 1)(PRIMITIVE 7)(FUNCTION_END 1)(POINTER 0)") def test_type_table_array(): check_type_table("int a[100];", "(PRIMITIVE 7)(ARRAY 0)(None 100)") def test_type_table_typedef(): check_type_table("typedef int foo_t;", "(PRIMITIVE 7)") def test_type_table_prebuilt_type(): check_type_table("int32_t f(void);", "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 21)") def test_type_table_struct_opaque(): check_type_table("struct foo_s;", "(STRUCT_UNION 0)") def test_type_table_struct(): check_type_table("struct foo_s { int a; long b; };", "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") def test_type_table_union(): check_type_table("union foo_u { int a; long b; };", "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") def test_type_table_struct_used(): check_type_table("struct foo_s { int a; long b; }; int f(struct foo_s*);", "(FUNCTION 3)(POINTER 5)(FUNCTION_END 0)" "(PRIMITIVE 7)(PRIMITIVE 9)" "(STRUCT_UNION 0)") def test_type_table_anonymous_struct_with_typedef(): check_type_table("typedef struct { int a; long b; } foo_t;", "(STRUCT_UNION 0)(PRIMITIVE 7)(PRIMITIVE 9)") def test_type_table_enum(): check_type_table("enum foo_e { AA, BB, ... };", "(ENUM 0)") def test_type_table_include_1(): check_type_table("foo_t sin(foo_t);", "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)", included="typedef double foo_t;") def test_type_table_include_2(): check_type_table("struct foo_s *sin(struct foo_s *);", "(FUNCTION 1)(POINTER 3)(FUNCTION_END 0)(STRUCT_UNION 0)", included="struct foo_s { int x, y; };") def test_math_sin(): import math ffi = FFI() ffi.cdef("float sin(double); double cos(double);") lib = verify(ffi, 'test_math_sin', '#include ') assert lib.cos(1.43) == math.cos(1.43) def test_repr_lib(): ffi = FFI() lib = verify(ffi, 'test_repr_lib', '') assert repr(lib) == "" def test_funcarg_ptr(): ffi = FFI() ffi.cdef("int foo(int *);") lib = verify(ffi, 'test_funcarg_ptr', 'int foo(int *p) { return *p; }') assert lib.foo([-12345]) == -12345 def test_funcres_ptr(): ffi = FFI() ffi.cdef("int *foo(void);") lib = verify(ffi, 'test_funcres_ptr', 'int *foo(void) { static int x=-12345; return &x; }') assert lib.foo()[0] == -12345 def test_global_var_array(): ffi = FFI() ffi.cdef("int a[100];") lib = verify(ffi, 'test_global_var_array', 'int a[100] = { 9999 };') lib.a[42] = 123456 assert lib.a[42] == 123456 assert lib.a[0] == 9999 def test_verify_typedef(): ffi = FFI() ffi.cdef("typedef int **foo_t;") lib = verify(ffi, 'test_verify_typedef', 'typedef int **foo_t;') assert ffi.sizeof("foo_t") == ffi.sizeof("void *") def test_verify_typedef_dotdotdot(): ffi = FFI() ffi.cdef("typedef ... foo_t;") verify(ffi, 'test_verify_typedef_dotdotdot', 'typedef int **foo_t;') def test_verify_typedef_star_dotdotdot(): ffi = FFI() ffi.cdef("typedef ... *foo_t;") verify(ffi, 'test_verify_typedef_star_dotdotdot', 'typedef int **foo_t;') def test_global_var_int(): ffi = FFI() ffi.cdef("int a, b, c;") lib = verify(ffi, 'test_global_var_int', 'int a = 999, b, c;') assert lib.a == 999 lib.a -= 1001 assert lib.a == -2 lib.a = -2147483648 assert lib.a == -2147483648 py.test.raises(OverflowError, "lib.a = 2147483648") py.test.raises(OverflowError, "lib.a = -2147483649") lib.b = 525 # try with the first access being in setattr, too assert lib.b == 525 py.test.raises(AttributeError, "del lib.a") py.test.raises(AttributeError, "del lib.c") py.test.raises(AttributeError, "del lib.foobarbaz") def test_macro(): ffi = FFI() ffi.cdef("#define FOOBAR ...") lib = verify(ffi, 'test_macro', "#define FOOBAR (-6912)") assert lib.FOOBAR == -6912 py.test.raises(AttributeError, "lib.FOOBAR = 2") def test_macro_check_value(): # the value '-0x80000000' in C sources does not have a clear meaning # to me; it appears to have a different effect than '-2147483648'... # Moreover, on 32-bits, -2147483648 is actually equal to # -2147483648U, which in turn is equal to 2147483648U and so positive. vals = ['42', '-42', '0x80000000', '-2147483648', '0', '9223372036854775809ULL', '-9223372036854775807LL'] if sys.maxsize <= 2**32 or sys.platform == 'win32': vals.remove('-2147483648') ffi = FFI() cdef_lines = ['#define FOO_%d_%d %s' % (i, j, vals[i]) for i in range(len(vals)) for j in range(len(vals))] ffi.cdef('\n'.join(cdef_lines)) verify_lines = ['#define FOO_%d_%d %s' % (i, j, vals[j]) # [j], not [i] for i in range(len(vals)) for j in range(len(vals))] lib = verify(ffi, 'test_macro_check_value_ok', '\n'.join(verify_lines)) # for j in range(len(vals)): c_got = int(vals[j].replace('U', '').replace('L', ''), 0) c_compiler_msg = str(c_got) if c_got > 0: c_compiler_msg += ' (0x%x)' % (c_got,) # for i in range(len(vals)): attrname = 'FOO_%d_%d' % (i, j) if i == j: x = getattr(lib, attrname) assert x == c_got else: e = py.test.raises(ffi.error, getattr, lib, attrname) assert str(e.value) == ( "the C compiler says '%s' is equal to " "%s, but the cdef disagrees" % (attrname, c_compiler_msg)) def test_constant(): ffi = FFI() ffi.cdef("static const int FOOBAR;") lib = verify(ffi, 'test_constant', "#define FOOBAR (-6912)") assert lib.FOOBAR == -6912 py.test.raises(AttributeError, "lib.FOOBAR = 2") def test_check_value_of_static_const(): ffi = FFI() ffi.cdef("static const int FOOBAR = 042;") lib = verify(ffi, 'test_check_value_of_static_const', "#define FOOBAR (-6912)") e = py.test.raises(ffi.error, getattr, lib, 'FOOBAR') assert str(e.value) == ( "the C compiler says 'FOOBAR' is equal to -6912, but the cdef disagrees") def test_constant_nonint(): ffi = FFI() ffi.cdef("static const double FOOBAR;") lib = verify(ffi, 'test_constant_nonint', "#define FOOBAR (-6912.5)") assert lib.FOOBAR == -6912.5 py.test.raises(AttributeError, "lib.FOOBAR = 2") def test_constant_ptr(): ffi = FFI() ffi.cdef("static double *const FOOBAR;") lib = verify(ffi, 'test_constant_ptr', "#define FOOBAR NULL") assert lib.FOOBAR == ffi.NULL assert ffi.typeof(lib.FOOBAR) == ffi.typeof("double *") def test_dir(): ffi = FFI() ffi.cdef("int ff(int); int aa; static const int my_constant;") lib = verify(ffi, 'test_dir', """ #define my_constant (-45) int aa; int ff(int x) { return x+aa; } """) lib.aa = 5 assert dir(lib) == ['aa', 'ff', 'my_constant'] # aaobj = lib.__dict__['aa'] assert not isinstance(aaobj, int) # some internal object instead assert lib.__dict__ == { 'ff': lib.ff, 'aa': aaobj, 'my_constant': -45} lib.__dict__['ff'] = "??" assert lib.ff(10) == 15 def test_verify_opaque_struct(): ffi = FFI() ffi.cdef("struct foo_s;") lib = verify(ffi, 'test_verify_opaque_struct', "struct foo_s;") assert ffi.typeof("struct foo_s").cname == "struct foo_s" def test_verify_opaque_union(): ffi = FFI() ffi.cdef("union foo_s;") lib = verify(ffi, 'test_verify_opaque_union', "union foo_s;") assert ffi.typeof("union foo_s").cname == "union foo_s" def test_verify_struct(): ffi = FFI() ffi.cdef("""struct foo_s { int b; short a; ...; }; struct bar_s { struct foo_s *f; };""") lib = verify(ffi, 'test_verify_struct', """struct foo_s { short a; int b; }; struct bar_s { struct foo_s *f; };""") ffi.typeof("struct bar_s *") p = ffi.new("struct foo_s *", {'a': -32768, 'b': -2147483648}) assert p.a == -32768 assert p.b == -2147483648 py.test.raises(OverflowError, "p.a -= 1") py.test.raises(OverflowError, "p.b -= 1") q = ffi.new("struct bar_s *", {'f': p}) assert q.f == p # assert ffi.offsetof("struct foo_s", "a") == 0 assert ffi.offsetof("struct foo_s", "b") == 4 assert ffi.offsetof(u+"struct foo_s", u+"b") == 4 # py.test.raises(TypeError, ffi.addressof, p) assert ffi.addressof(p[0]) == p assert ffi.typeof(ffi.addressof(p[0])) is ffi.typeof("struct foo_s *") assert ffi.typeof(ffi.addressof(p, "b")) is ffi.typeof("int *") assert ffi.addressof(p, "b")[0] == p.b def test_verify_exact_field_offset(): ffi = FFI() ffi.cdef("""struct foo_s { int b; short a; };""") lib = verify(ffi, 'test_verify_exact_field_offset', """struct foo_s { short a; int b; };""") e = py.test.raises(ffi.error, ffi.new, "struct foo_s *", []) # lazily assert str(e.value) == ("struct foo_s: wrong offset for field 'b' (cdef " 'says 0, but C compiler says 4). fix it or use "...;" ' "in the cdef for struct foo_s to make it flexible") def test_type_caching(): ffi1 = FFI(); ffi1.cdef("struct foo_s;") ffi2 = FFI(); ffi2.cdef("struct foo_s;") # different one! lib1 = verify(ffi1, 'test_type_caching_1', 'struct foo_s;') lib2 = verify(ffi2, 'test_type_caching_2', 'struct foo_s;') # shared types assert ffi1.typeof("long") is ffi2.typeof("long") assert ffi1.typeof("long**") is ffi2.typeof("long * *") assert ffi1.typeof("long(*)(int, ...)") is ffi2.typeof("long(*)(int, ...)") # non-shared types assert ffi1.typeof("struct foo_s") is not ffi2.typeof("struct foo_s") assert ffi1.typeof("struct foo_s *") is not ffi2.typeof("struct foo_s *") assert ffi1.typeof("struct foo_s*(*)()") is not ( ffi2.typeof("struct foo_s*(*)()")) assert ffi1.typeof("void(*)(struct foo_s*)") is not ( ffi2.typeof("void(*)(struct foo_s*)")) def test_verify_enum(): ffi = FFI() ffi.cdef("""enum e1 { B1, A1, ... }; enum e2 { B2, A2, ... };""") lib = verify(ffi, 'test_verify_enum', "enum e1 { A1, B1, C1=%d };" % sys.maxsize + "enum e2 { A2, B2, C2 };") ffi.typeof("enum e1") ffi.typeof("enum e2") assert lib.A1 == 0 assert lib.B1 == 1 assert lib.A2 == 0 assert lib.B2 == 1 assert ffi.sizeof("enum e1") == ffi.sizeof("long") assert ffi.sizeof("enum e2") == ffi.sizeof("int") assert repr(ffi.cast("enum e1", 0)) == "" def test_duplicate_enum(): ffi = FFI() ffi.cdef("enum e1 { A1, ... }; enum e2 { A1, ... };") py.test.raises(VerificationError, verify, ffi, 'test_duplicate_enum', "enum e1 { A1 }; enum e2 { B1 };") def test_dotdotdot_length_of_array_field(): ffi = FFI() ffi.cdef("struct foo_s { int a[...]; int b[...]; };") verify(ffi, 'test_dotdotdot_length_of_array_field', "struct foo_s { int a[42]; int b[11]; };") assert ffi.sizeof("struct foo_s") == (42 + 11) * 4 p = ffi.new("struct foo_s *") assert p.a[41] == p.b[10] == 0 py.test.raises(IndexError, "p.a[42]") py.test.raises(IndexError, "p.b[11]") def test_dotdotdot_global_array(): ffi = FFI() ffi.cdef("int aa[...]; int bb[...];") lib = verify(ffi, 'test_dotdotdot_global_array', "int aa[41]; int bb[12];") assert ffi.sizeof(lib.aa) == 41 * 4 assert ffi.sizeof(lib.bb) == 12 * 4 assert lib.aa[40] == lib.bb[11] == 0 py.test.raises(IndexError, "lib.aa[41]") py.test.raises(IndexError, "lib.bb[12]") def test_misdeclared_field_1(): ffi = FFI() ffi.cdef("struct foo_s { int a[5]; };") try: verify(ffi, 'test_misdeclared_field_1', "struct foo_s { int a[6]; };") except VerificationError: pass # ok, fail during compilation already (e.g. C++) else: assert ffi.sizeof("struct foo_s") == 24 # found by the actual C code p = ffi.new("struct foo_s *") # lazily build the fields and boom: e = py.test.raises(ffi.error, "p.a") assert str(e.value).startswith("struct foo_s: wrong size for field 'a' " "(cdef says 20, but C compiler says 24)") def test_open_array_in_struct(): ffi = FFI() ffi.cdef("struct foo_s { int b; int a[]; };") verify(ffi, 'test_open_array_in_struct', "struct foo_s { int b; int a[]; };") assert ffi.sizeof("struct foo_s") == 4 p = ffi.new("struct foo_s *", [5, [10, 20, 30]]) assert p.a[2] == 30 def test_math_sin_type(): ffi = FFI() ffi.cdef("double sin(double);") lib = verify(ffi, 'test_math_sin_type', '#include ') # 'lib.sin' is typed as a object on lib assert ffi.typeof(lib.sin).cname == "double(*)(double)" # 'x' is another object on lib, made very indirectly x = type(lib).__dir__.__get__(lib) py.test.raises(TypeError, ffi.typeof, x) # # present on built-in functions on CPython; must be emulated on PyPy: assert lib.sin.__name__ == 'sin' assert lib.sin.__module__ == '_CFFI_test_math_sin_type' assert lib.sin.__doc__ == 'direct call to the C function of the same name' def test_verify_anonymous_struct_with_typedef(): ffi = FFI() ffi.cdef("typedef struct { int a; long b; ...; } foo_t;") verify(ffi, 'test_verify_anonymous_struct_with_typedef', "typedef struct { long b; int hidden, a; } foo_t;") p = ffi.new("foo_t *", {'b': 42}) assert p.b == 42 assert repr(p).startswith("" # ffi = FFI() ffi.cdef("typedef enum { AA=%d } e1;" % sys.maxsize) lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef2', "typedef enum { AA=%d } e1;" % sys.maxsize) assert lib.AA == int(ffi.cast("long", sys.maxsize)) assert ffi.sizeof("e1") == ffi.sizeof("long") def test_unique_types(): CDEF = "struct foo_s; union foo_u; enum foo_e { AA };" ffi1 = FFI(); ffi1.cdef(CDEF); verify(ffi1, "test_unique_types_1", CDEF) ffi2 = FFI(); ffi2.cdef(CDEF); verify(ffi2, "test_unique_types_2", CDEF) # assert ffi1.typeof("char") is ffi2.typeof("char ") assert ffi1.typeof("long") is ffi2.typeof("signed long int") assert ffi1.typeof("double *") is ffi2.typeof("double*") assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *") assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]") assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]") assert ffi1.typeof("void") is ffi2.typeof("void") assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)") # # these depend on user-defined data, so should not be shared for name in ["struct foo_s", "union foo_u *", "enum foo_e", "struct foo_s *(*)()", "void(*)(struct foo_s *)", "struct foo_s *(*[5])[8]", ]: assert ffi1.typeof(name) is not ffi2.typeof(name) # sanity check: twice 'ffi1' assert ffi1.typeof("struct foo_s*") is ffi1.typeof("struct foo_s *") def test_module_name_in_package(): ffi = FFI() ffi.cdef("int foo(int);") recompiler.recompile(ffi, "test_module_name_in_package.mymod", "int foo(int x) { return x + 32; }", tmpdir=str(udir)) old_sys_path = sys.path[:] try: package_dir = udir.join('test_module_name_in_package') for name in os.listdir(str(udir)): assert not name.startswith('test_module_name_in_package.') assert os.path.isdir(str(package_dir)) assert len(os.listdir(str(package_dir))) > 0 assert os.path.exists(str(package_dir.join('mymod.c'))) package_dir.join('__init__.py').write('') # sys.path.insert(0, str(udir)) import test_module_name_in_package.mymod assert test_module_name_in_package.mymod.lib.foo(10) == 42 assert test_module_name_in_package.mymod.__name__ == ( 'test_module_name_in_package.mymod') finally: sys.path[:] = old_sys_path def test_bad_size_of_global_1(): ffi = FFI() ffi.cdef("short glob;") py.test.raises(VerificationError, verify, ffi, "test_bad_size_of_global_1", "long glob;") def test_bad_size_of_global_2(): ffi = FFI() ffi.cdef("int glob[10];") py.test.raises(VerificationError, verify, ffi, "test_bad_size_of_global_2", "int glob[9];") def test_unspecified_size_of_global_1(): ffi = FFI() ffi.cdef("int glob[];") lib = verify(ffi, "test_unspecified_size_of_global_1", "int glob[10];") assert ffi.typeof(lib.glob) == ffi.typeof("int *") def test_unspecified_size_of_global_2(): ffi = FFI() ffi.cdef("int glob[][5];") lib = verify(ffi, "test_unspecified_size_of_global_2", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") def test_unspecified_size_of_global_3(): ffi = FFI() ffi.cdef("int glob[][...];") lib = verify(ffi, "test_unspecified_size_of_global_3", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") def test_unspecified_size_of_global_4(): ffi = FFI() ffi.cdef("int glob[...][...];") lib = verify(ffi, "test_unspecified_size_of_global_4", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int[10][5]") def test_include_1(): ffi1 = FFI() ffi1.cdef("typedef double foo_t;") verify(ffi1, "test_include_1_parent", "typedef double foo_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("foo_t ff1(foo_t);") lib = verify(ffi, "test_include_1", "double ff1(double x) { return 42.5; }") assert lib.ff1(0) == 42.5 assert ffi1.typeof("foo_t") is ffi.typeof("foo_t") is ffi.typeof("double") def test_include_1b(): ffi1 = FFI() ffi1.cdef("int foo1(int);") lib1 = verify(ffi1, "test_include_1b_parent", "int foo1(int x) { return x + 10; }") ffi = FFI() ffi.include(ffi1) ffi.cdef("int foo2(int);") lib = verify(ffi, "test_include_1b", "int foo2(int x) { return x - 5; }") assert lib.foo2(42) == 37 assert lib.foo1(42) == 52 assert lib.foo1 is lib1.foo1 def test_include_2(): ffi1 = FFI() ffi1.cdef("struct foo_s { int x, y; };") verify(ffi1, "test_include_2_parent", "struct foo_s { int x, y; };") ffi = FFI() ffi.include(ffi1) ffi.cdef("struct foo_s *ff2(struct foo_s *);") lib = verify(ffi, "test_include_2", "struct foo_s { int x, y; }; //usually from a #include\n" "struct foo_s *ff2(struct foo_s *p) { p->y++; return p; }") p = ffi.new("struct foo_s *") p.y = 41 q = lib.ff2(p) assert q == p assert p.y == 42 assert ffi1.typeof("struct foo_s") is ffi.typeof("struct foo_s") def test_include_3(): ffi1 = FFI() ffi1.cdef("typedef short sshort_t;") verify(ffi1, "test_include_3_parent", "typedef short sshort_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("sshort_t ff3(sshort_t);") lib = verify(ffi, "test_include_3", "typedef short sshort_t; //usually from a #include\n" "sshort_t ff3(sshort_t x) { return x + 42; }") assert lib.ff3(10) == 52 assert ffi.typeof(ffi.cast("sshort_t", 42)) is ffi.typeof("short") assert ffi1.typeof("sshort_t") is ffi.typeof("sshort_t") def test_include_4(): ffi1 = FFI() ffi1.cdef("typedef struct { int x; } mystruct_t;") verify(ffi1, "test_include_4_parent", "typedef struct { int x; } mystruct_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_t *ff4(mystruct_t *);") lib = verify(ffi, "test_include_4", "typedef struct {int x; } mystruct_t; //usually from a #include\n" "mystruct_t *ff4(mystruct_t *p) { p->x += 42; return p; }") p = ffi.new("mystruct_t *", [10]) q = lib.ff4(p) assert q == p assert p.x == 52 assert ffi1.typeof("mystruct_t") is ffi.typeof("mystruct_t") def test_include_5(): ffi1 = FFI() ffi1.cdef("typedef struct { int x[2]; int y; } *mystruct_p;") verify(ffi1, "test_include_5_parent", "typedef struct { int x[2]; int y; } *mystruct_p;") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_p ff5(mystruct_p);") lib = verify(ffi, "test_include_5", "typedef struct {int x[2]; int y; } *mystruct_p; //usually #include\n" "mystruct_p ff5(mystruct_p p) { p->x[1] += 42; return p; }") assert ffi.alignof(ffi.typeof("mystruct_p").item) == 4 assert ffi1.typeof("mystruct_p") is ffi.typeof("mystruct_p") p = ffi.new("mystruct_p", [[5, 10], -17]) q = lib.ff5(p) assert q == p assert p.x[0] == 5 assert p.x[1] == 52 assert p.y == -17 assert ffi.alignof(ffi.typeof(p[0])) == 4 def test_include_6(): ffi1 = FFI() ffi1.cdef("typedef ... mystruct_t;") verify(ffi1, "test_include_6_parent", "typedef struct _mystruct_s mystruct_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_t *ff6(void); int ff6b(mystruct_t *);") lib = verify(ffi, "test_include_6", "typedef struct _mystruct_s mystruct_t; //usually from a #include\n" "struct _mystruct_s { int x; };\n" "static mystruct_t result_struct = { 42 };\n" "mystruct_t *ff6(void) { return &result_struct; }\n" "int ff6b(mystruct_t *p) { return p->x; }") p = lib.ff6() assert ffi.cast("int *", p)[0] == 42 assert lib.ff6b(p) == 42 def test_include_7(): ffi1 = FFI() ffi1.cdef("typedef ... mystruct_t;\n" "int ff7b(mystruct_t *);") verify(ffi1, "test_include_7_parent", "typedef struct { int x; } mystruct_t;\n" "int ff7b(mystruct_t *p) { return p->x; }") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_t *ff7(void);") lib = verify(ffi, "test_include_7", "typedef struct { int x; } mystruct_t; //usually from a #include\n" "static mystruct_t result_struct = { 42 };" "mystruct_t *ff7(void) { return &result_struct; }") p = lib.ff7() assert ffi.cast("int *", p)[0] == 42 assert lib.ff7b(p) == 42 def test_include_8(): ffi1 = FFI() ffi1.cdef("struct foo_s;") verify(ffi1, "test_include_8_parent", "struct foo_s;") ffi = FFI() ffi.include(ffi1) ffi.cdef("struct foo_s { int x, y; };") verify(ffi, "test_include_8", "struct foo_s { int x, y; };") e = py.test.raises(NotImplementedError, ffi.new, "struct foo_s *") assert str(e.value) == ( "'struct foo_s' is opaque in the ffi.include(), but no longer in " "the ffi doing the include (workaround: don't use ffi.include() but" " duplicate the declarations of everything using struct foo_s)") def test_unicode_libraries(): try: unicode except NameError: py.test.skip("for python 2.x") # import math 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' ffi = FFI() ffi.cdef(unicode("float sin(double); double cos(double);")) lib = verify(ffi, 'test_math_sin_unicode', unicode('#include '), libraries=[unicode(lib_m)]) assert lib.cos(1.43) == math.cos(1.43) def test_incomplete_struct_as_arg(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; int f(int, struct foo_s);") lib = verify(ffi, "test_incomplete_struct_as_arg", "struct foo_s { int a, x, z; };\n" "int f(int b, struct foo_s s) { return s.x * b; }") s = ffi.new("struct foo_s *", [21]) assert s.x == 21 assert ffi.sizeof(s[0]) == 12 assert ffi.offsetof(ffi.typeof(s), 'x') == 4 assert lib.f(2, s[0]) == 42 assert ffi.typeof(lib.f) == ffi.typeof("int(*)(int, struct foo_s)") def test_incomplete_struct_as_result(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; struct foo_s f(int);") lib = verify(ffi, "test_incomplete_struct_as_result", "struct foo_s { int a, x, z; };\n" "struct foo_s f(int x) { struct foo_s r; r.x = x * 2; return r; }") s = lib.f(21) assert s.x == 42 assert ffi.typeof(lib.f) == ffi.typeof("struct foo_s(*)(int)") def test_incomplete_struct_as_both(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; struct bar_s { int y; ...; };\n" "struct foo_s f(int, struct bar_s);") lib = verify(ffi, "test_incomplete_struct_as_both", "struct foo_s { int a, x, z; };\n" "struct bar_s { int b, c, y, d; };\n" "struct foo_s f(int x, struct bar_s b) {\n" " struct foo_s r; r.x = x * b.y; return r;\n" "}") b = ffi.new("struct bar_s *", [7]) s = lib.f(6, b[0]) assert s.x == 42 assert ffi.typeof(lib.f) == ffi.typeof( "struct foo_s(*)(int, struct bar_s)") s = lib.f(14, {'y': -3}) assert s.x == -42 def test_name_of_unnamed_struct(): ffi = FFI() ffi.cdef("typedef struct { int x; } foo_t;\n" "typedef struct { int y; } *bar_p;\n" "typedef struct { int y; } **baz_pp;\n") verify(ffi, "test_name_of_unnamed_struct", "typedef struct { int x; } foo_t;\n" "typedef struct { int y; } *bar_p;\n" "typedef struct { int y; } **baz_pp;\n") assert repr(ffi.typeof("foo_t")) == "" assert repr(ffi.typeof("bar_p")) == "" assert repr(ffi.typeof("baz_pp")) == "" def test_address_of_global_var(): ffi = FFI() ffi.cdef(""" long bottom, bottoms[2]; long FetchRectBottom(void); long FetchRectBottoms1(void); #define FOOBAR 42 """) lib = verify(ffi, "test_address_of_global_var", """ long bottom, bottoms[2]; long FetchRectBottom(void) { return bottom; } long FetchRectBottoms1(void) { return bottoms[1]; } #define FOOBAR 42 """) lib.bottom = 300 assert lib.FetchRectBottom() == 300 lib.bottom += 1 assert lib.FetchRectBottom() == 301 lib.bottoms[1] = 500 assert lib.FetchRectBottoms1() == 500 lib.bottoms[1] += 2 assert lib.FetchRectBottoms1() == 502 # p = ffi.addressof(lib, 'bottom') assert ffi.typeof(p) == ffi.typeof("long *") assert p[0] == 301 p[0] += 1 assert lib.FetchRectBottom() == 302 p = ffi.addressof(lib, 'bottoms') assert ffi.typeof(p) == ffi.typeof("long(*)[2]") assert p[0] == lib.bottoms # py.test.raises(AttributeError, ffi.addressof, lib, 'unknown_var') py.test.raises(AttributeError, ffi.addressof, lib, "FOOBAR") def test_defines__CFFI_(): # Check that we define the macro _CFFI_ automatically. # It should be done before including Python.h, so that PyPy's Python.h # can check for it. ffi = FFI() ffi.cdef(""" #define CORRECT 1 """) lib = verify(ffi, "test_defines__CFFI_", """ #ifdef _CFFI_ # define CORRECT 1 #endif """) assert lib.CORRECT == 1 def test_unpack_args(): ffi = FFI() ffi.cdef("void foo0(void); void foo1(int); void foo2(int, int);") lib = verify(ffi, "test_unpack_args", """ void foo0(void) { } void foo1(int x) { } void foo2(int x, int y) { } """) assert 'foo0' in repr(lib.foo0) assert 'foo1' in repr(lib.foo1) assert 'foo2' in repr(lib.foo2) lib.foo0() lib.foo1(42) lib.foo2(43, 44) e1 = py.test.raises(TypeError, lib.foo0, 42) e2 = py.test.raises(TypeError, lib.foo0, 43, 44) e3 = py.test.raises(TypeError, lib.foo1) e4 = py.test.raises(TypeError, lib.foo1, 43, 44) e5 = py.test.raises(TypeError, lib.foo2) e6 = py.test.raises(TypeError, lib.foo2, 42) e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47) assert str(e1.value) == "foo0() takes no arguments (1 given)" assert str(e2.value) == "foo0() takes no arguments (2 given)" assert str(e3.value) == "foo1() takes exactly one argument (0 given)" assert str(e4.value) == "foo1() takes exactly one argument (2 given)" assert str(e5.value) == "foo2() takes exactly 2 arguments (0 given)" assert str(e6.value) == "foo2() takes exactly 2 arguments (1 given)" assert str(e7.value) == "foo2() takes exactly 2 arguments (3 given)" def test_address_of_function(): ffi = FFI() ffi.cdef("long myfunc(long x);") lib = verify(ffi, "test_addressof_function", """ char myfunc(char x) { return (char)(x + 42); } """) assert lib.myfunc(5) == 47 assert lib.myfunc(0xABC05) == 47 assert not isinstance(lib.myfunc, ffi.CData) assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)") addr = ffi.addressof(lib, 'myfunc') assert addr(5) == 47 assert addr(0xABC05) == 47 assert isinstance(addr, ffi.CData) assert ffi.typeof(addr) == ffi.typeof("long(*)(long)") def test_address_of_function_with_struct(): ffi = FFI() ffi.cdef("struct foo_s { int x; }; long myfunc(struct foo_s);") lib = verify(ffi, "test_addressof_function_with_struct", """ struct foo_s { int x; }; char myfunc(struct foo_s input) { return (char)(input.x + 42); } """) s = ffi.new("struct foo_s *", [5])[0] assert lib.myfunc(s) == 47 assert not isinstance(lib.myfunc, ffi.CData) assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(struct foo_s)") addr = ffi.addressof(lib, 'myfunc') assert addr(s) == 47 assert isinstance(addr, ffi.CData) assert ffi.typeof(addr) == ffi.typeof("long(*)(struct foo_s)") def test_issue198(): ffi = FFI() ffi.cdef(""" typedef struct{...;} opaque_t; const opaque_t CONSTANT; int toint(opaque_t); """) lib = verify(ffi, 'test_issue198', """ typedef int opaque_t; #define CONSTANT ((opaque_t)42) static int toint(opaque_t o) { return o; } """) def random_stuff(): pass assert lib.toint(lib.CONSTANT) == 42 random_stuff() assert lib.toint(lib.CONSTANT) == 42 def test_constant_is_not_a_compiler_constant(): ffi = FFI() ffi.cdef("static const float almost_forty_two;") lib = verify(ffi, 'test_constant_is_not_a_compiler_constant', """ static float f(void) { return 42.25; } #define almost_forty_two (f()) """) assert lib.almost_forty_two == 42.25 def test_constant_of_unknown_size(): ffi = FFI() ffi.cdef(""" typedef ... opaque_t; const opaque_t CONSTANT; """) lib = verify(ffi, 'test_constant_of_unknown_size', "typedef int opaque_t;" "const int CONSTANT = 42;") e = py.test.raises(ffi.error, getattr, lib, 'CONSTANT') assert str(e.value) == ("constant 'CONSTANT' is of " "type 'opaque_t', whose size is not known") def test_variable_of_unknown_size(): ffi = FFI() ffi.cdef(""" typedef ... opaque_t; opaque_t globvar; """) lib = verify(ffi, 'test_variable_of_unknown_size', """ typedef char opaque_t[6]; opaque_t globvar = "hello"; """) # can't read or write it at all e = py.test.raises(TypeError, getattr, lib, 'globvar') assert str(e.value) in ["cdata 'opaque_t' is opaque", "'opaque_t' is opaque or not completed yet"] #pypy e = py.test.raises(TypeError, setattr, lib, 'globvar', []) assert str(e.value) in ["'opaque_t' is opaque", "'opaque_t' is opaque or not completed yet"] #pypy # but we can get its address p = ffi.addressof(lib, 'globvar') assert ffi.typeof(p) == ffi.typeof('opaque_t *') assert ffi.string(ffi.cast("char *", p), 8) == b"hello" def test_constant_of_value_unknown_to_the_compiler(): extra_c_source = udir.join( 'extra_test_constant_of_value_unknown_to_the_compiler.c') extra_c_source.write('const int external_foo = 42;\n') ffi = FFI() ffi.cdef("const int external_foo;") lib = verify(ffi, 'test_constant_of_value_unknown_to_the_compiler', """ extern const int external_foo; """, sources=[str(extra_c_source)]) assert lib.external_foo == 42 def test_dotdot_in_source_file_names(): extra_c_source = udir.join( 'extra_test_dotdot_in_source_file_names.c') extra_c_source.write('const int external_foo = 42;\n') ffi = FFI() ffi.cdef("const int external_foo;") lib = verify(ffi, 'test_dotdot_in_source_file_names', """ extern const int external_foo; """, sources=[os.path.join(os.path.dirname(str(extra_c_source)), 'foobar', '..', os.path.basename(str(extra_c_source)))]) assert lib.external_foo == 42 def test_call_with_incomplete_structs(): ffi = FFI() ffi.cdef("typedef struct {...;} foo_t; " "foo_t myglob; " "foo_t increment(foo_t s); " "double getx(foo_t s);") lib = verify(ffi, 'test_call_with_incomplete_structs', """ typedef double foo_t; double myglob = 42.5; double getx(double x) { return x; } double increment(double x) { return x + 1; } """) assert lib.getx(lib.myglob) == 42.5 assert lib.getx(lib.increment(lib.myglob)) == 43.5 def test_struct_array_guess_length_2(): ffi = FFI() ffi.cdef("struct foo_s { int a[...][...]; };") lib = verify(ffi, 'test_struct_array_guess_length_2', "struct foo_s { int x; int a[5][8]; int y; };") assert ffi.sizeof('struct foo_s') == 42 * ffi.sizeof('int') s = ffi.new("struct foo_s *") assert ffi.sizeof(s.a) == 40 * ffi.sizeof('int') assert s.a[4][7] == 0 py.test.raises(IndexError, 's.a[4][8]') py.test.raises(IndexError, 's.a[5][0]') assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") assert ffi.typeof(s.a[0]) == ffi.typeof("int[8]") def test_struct_array_guess_length_3(): ffi = FFI() ffi.cdef("struct foo_s { int a[][...]; };") lib = verify(ffi, 'test_struct_array_guess_length_3', "struct foo_s { int x; int a[5][7]; int y; };") assert ffi.sizeof('struct foo_s') == 37 * ffi.sizeof('int') s = ffi.new("struct foo_s *") assert ffi.typeof(s.a) == ffi.typeof("int(*)[7]") assert s.a[4][6] == 0 py.test.raises(IndexError, 's.a[4][7]') assert ffi.typeof(s.a[0]) == ffi.typeof("int[7]") def test_global_var_array_2(): ffi = FFI() ffi.cdef("int a[...][...];") lib = verify(ffi, 'test_global_var_array_2', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 py.test.raises(IndexError, 'lib.a[0][8]') py.test.raises(IndexError, 'lib.a[10][0]') assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") def test_global_var_array_3(): ffi = FFI() ffi.cdef("int a[][...];") lib = verify(ffi, 'test_global_var_array_3', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 py.test.raises(IndexError, 'lib.a[0][8]') assert ffi.typeof(lib.a) == ffi.typeof("int(*)[8]") assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") def test_global_var_array_4(): ffi = FFI() ffi.cdef("int a[10][...];") lib = verify(ffi, 'test_global_var_array_4', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 py.test.raises(IndexError, 'lib.a[0][8]') py.test.raises(IndexError, 'lib.a[10][8]') assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") def test_some_integer_type(): ffi = FFI() ffi.cdef(""" typedef int... foo_t; typedef unsigned long... bar_t; typedef struct { foo_t a, b; } mystruct_t; foo_t foobar(bar_t, mystruct_t); static const bar_t mu = -20; static const foo_t nu = 20; """) lib = verify(ffi, 'test_some_integer_type', """ typedef unsigned long long foo_t; typedef short bar_t; typedef struct { foo_t a, b; } mystruct_t; static foo_t foobar(bar_t x, mystruct_t s) { return (foo_t)x + s.a + s.b; } static const bar_t mu = -20; static const foo_t nu = 20; """) assert ffi.sizeof("foo_t") == ffi.sizeof("unsigned long long") assert ffi.sizeof("bar_t") == ffi.sizeof("short") maxulonglong = 2 ** 64 - 1 assert int(ffi.cast("foo_t", -1)) == maxulonglong assert int(ffi.cast("bar_t", -1)) == -1 assert lib.foobar(-1, [0, 0]) == maxulonglong assert lib.foobar(2 ** 15 - 1, [0, 0]) == 2 ** 15 - 1 assert lib.foobar(10, [20, 31]) == 61 assert lib.foobar(0, [0, maxulonglong]) == maxulonglong py.test.raises(OverflowError, lib.foobar, 2 ** 15, [0, 0]) py.test.raises(OverflowError, lib.foobar, -(2 ** 15) - 1, [0, 0]) py.test.raises(OverflowError, ffi.new, "mystruct_t *", [0, -1]) assert lib.mu == -20 assert lib.nu == 20 def test_some_float_type(): ffi = FFI() ffi.cdef(""" typedef double... foo_t; typedef float... bar_t; foo_t sum(foo_t[]); bar_t neg(bar_t); """) lib = verify(ffi, 'test_some_float_type', """ typedef float foo_t; static foo_t sum(foo_t x[]) { return x[0] + x[1]; } typedef double bar_t; static double neg(double x) { return -x; } """) assert lib.sum([40.0, 2.25]) == 42.25 assert lib.sum([12.3, 45.6]) != 12.3 + 45.6 # precision loss assert lib.neg(12.3) == -12.3 # no precision loss assert ffi.sizeof("foo_t") == ffi.sizeof("float") assert ffi.sizeof("bar_t") == ffi.sizeof("double") def test_some_float_invalid_1(): ffi = FFI() py.test.raises(FFIError, ffi.cdef, "typedef long double... foo_t;") def test_some_float_invalid_2(): ffi = FFI() ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") lib = verify(ffi, 'test_some_float_invalid_2', """ typedef unsigned long foo_t; foo_t neg(foo_t x) { return -x; } """) e = py.test.raises(ffi.error, getattr, lib, 'neg') assert str(e.value) == ("primitive floating-point type with an unexpected " "size (or not a float type at all)") def test_some_float_invalid_3(): ffi = FFI() ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") lib = verify(ffi, 'test_some_float_invalid_3', """ typedef long double foo_t; foo_t neg(foo_t x) { return -x; } """) if ffi.sizeof("long double") == ffi.sizeof("double"): assert lib.neg(12.3) == -12.3 else: e = py.test.raises(ffi.error, getattr, lib, 'neg') assert str(e.value) == ("primitive floating-point type is " "'long double', not supported for now with " "the syntax 'typedef double... xxx;'") def test_issue200(): ffi = FFI() ffi.cdef(""" typedef void (function_t)(void*); void function(void *); """) lib = verify(ffi, 'test_issue200', """ static void function(void *p) { (void)p; } """) ffi.typeof('function_t*') lib.function(ffi.NULL) # assert did not crash def test_alignment_of_longlong(): ffi = FFI() x1 = ffi.alignof('unsigned long long') assert x1 in [4, 8] ffi.cdef("struct foo_s { unsigned long long x; };") lib = verify(ffi, 'test_alignment_of_longlong', "struct foo_s { unsigned long long x; };") assert ffi.alignof('unsigned long long') == x1 assert ffi.alignof('struct foo_s') == x1 def test_import_from_lib(): ffi = FFI() ffi.cdef("int mybar(int); int myvar;\n#define MYFOO ...") lib = verify(ffi, 'test_import_from_lib', "#define MYFOO 42\n" "static int mybar(int x) { return x + 1; }\n" "static int myvar = -5;") assert sys.modules['_CFFI_test_import_from_lib'].lib is lib assert sys.modules['_CFFI_test_import_from_lib.lib'] is lib from _CFFI_test_import_from_lib.lib import MYFOO assert MYFOO == 42 assert hasattr(lib, '__dict__') assert lib.__all__ == ['MYFOO', 'mybar'] # but not 'myvar' assert lib.__name__ == repr(lib) assert lib.__class__ is type(lib) def test_macro_var_callback(): ffi = FFI() ffi.cdef("int my_value; int *(*get_my_value)(void);") lib = verify(ffi, 'test_macro_var_callback', "int *(*get_my_value)(void);\n" "#define my_value (*get_my_value())") # values = ffi.new("int[50]") def it(): for i in range(50): yield i it = it() # @ffi.callback("int *(*)(void)") def get_my_value(): for nextvalue in it: return values + nextvalue lib.get_my_value = get_my_value # values[0] = 41 assert lib.my_value == 41 # [0] p = ffi.addressof(lib, 'my_value') # [1] assert p == values + 1 assert p[-1] == 41 assert p[+1] == 0 lib.my_value = 42 # [2] assert values[2] == 42 assert p[-1] == 41 assert p[+1] == 42 # # if get_my_value raises or returns nonsense, the exception is printed # to stderr like with any callback, but then the C expression 'my_value' # expand to '*NULL'. We assume here that '&my_value' will return NULL # without segfaulting, and check for NULL when accessing the variable. @ffi.callback("int *(*)(void)") def get_my_value(): raise LookupError lib.get_my_value = get_my_value py.test.raises(ffi.error, getattr, lib, 'my_value') py.test.raises(ffi.error, setattr, lib, 'my_value', 50) py.test.raises(ffi.error, ffi.addressof, lib, 'my_value') @ffi.callback("int *(*)(void)") def get_my_value(): return "hello" lib.get_my_value = get_my_value py.test.raises(ffi.error, getattr, lib, 'my_value') e = py.test.raises(ffi.error, setattr, lib, 'my_value', 50) assert str(e.value) == "global variable 'my_value' is at address NULL" def test_const_fields(): ffi = FFI() ffi.cdef("""struct foo_s { const int a; void *const b; };""") lib = verify(ffi, 'test_const_fields', """ struct foo_s { const int a; void *const b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int") assert foo_s.fields[1][0] == 'b' assert foo_s.fields[1][1].type is ffi.typeof("void *") def test_restrict_fields(): ffi = FFI() ffi.cdef("""struct foo_s { void * restrict b; };""") lib = verify(ffi, 'test_restrict_fields', """ struct foo_s { void * __restrict b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'b' assert foo_s.fields[0][1].type is ffi.typeof("void *") def test_volatile_fields(): ffi = FFI() ffi.cdef("""struct foo_s { void * volatile b; };""") lib = verify(ffi, 'test_volatile_fields', """ struct foo_s { void * volatile b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'b' assert foo_s.fields[0][1].type is ffi.typeof("void *") def test_const_array_fields(): ffi = FFI() ffi.cdef("""struct foo_s { const int a[4]; };""") lib = verify(ffi, 'test_const_array_fields', """ struct foo_s { const int a[4]; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int[4]") def test_const_array_fields_varlength(): ffi = FFI() ffi.cdef("""struct foo_s { const int a[]; ...; };""") lib = verify(ffi, 'test_const_array_fields_varlength', """ struct foo_s { const int a[4]; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int[]") def test_const_array_fields_unknownlength(): ffi = FFI() ffi.cdef("""struct foo_s { const int a[...]; ...; };""") lib = verify(ffi, 'test_const_array_fields_unknownlength', """ struct foo_s { const int a[4]; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int[4]") def test_const_function_args(): ffi = FFI() ffi.cdef("""int foobar(const int a, const int *b, const int c[]);""") lib = verify(ffi, 'test_const_function_args', """ int foobar(const int a, const int *b, const int c[]) { return a + *b + *c; } """) assert lib.foobar(100, ffi.new("int *", 40), ffi.new("int *", 2)) == 142 def test_const_function_type_args(): ffi = FFI() ffi.cdef("""int (*foobar)(const int a, const int *b, const int c[]);""") lib = verify(ffi, 'test_const_function_type_args', """ int (*foobar)(const int a, const int *b, const int c[]); """) t = ffi.typeof(lib.foobar) assert t.args[0] is ffi.typeof("int") assert t.args[1] is ffi.typeof("int *") assert t.args[2] is ffi.typeof("int *") def test_const_constant(): ffi = FFI() ffi.cdef("""struct foo_s { int x,y; }; const struct foo_s myfoo;""") lib = verify(ffi, 'test_const_constant', """ struct foo_s { int x,y; }; const struct foo_s myfoo = { 40, 2 }; """) assert lib.myfoo.x == 40 assert lib.myfoo.y == 2 def test_const_via_typedef(): ffi = FFI() ffi.cdef("""typedef const int const_t; const_t aaa;""") lib = verify(ffi, 'test_const_via_typedef', """ typedef const int const_t; #define aaa 42 """) assert lib.aaa == 42 py.test.raises(AttributeError, "lib.aaa = 43") def test_win32_calling_convention_0(): ffi = FFI() ffi.cdef(""" int call1(int(__cdecl *cb)(int)); int (*const call2)(int(__stdcall *cb)(int)); """) lib = verify(ffi, 'test_win32_calling_convention_0', r""" #ifndef _MSC_VER # define __stdcall /* nothing */ #endif int call1(int(*cb)(int)) { int i, result = 0; //printf("call1: cb = %p\n", cb); for (i = 0; i < 1000; i++) result += cb(i); //printf("result = %d\n", result); return result; } int call2(int(__stdcall *cb)(int)) { int i, result = 0; //printf("call2: cb = %p\n", cb); for (i = 0; i < 1000; i++) result += cb(-i); //printf("result = %d\n", result); return result; } """) @ffi.callback("int(int)") def cb1(x): return x * 2 @ffi.callback("int __stdcall(int)") def cb2(x): return x * 3 res = lib.call1(cb1) assert res == 500*999*2 assert res == ffi.addressof(lib, 'call1')(cb1) res = lib.call2(cb2) assert res == -500*999*3 assert res == ffi.addressof(lib, 'call2')(cb2) if sys.platform == 'win32' and not sys.maxsize > 2**32: assert '__stdcall' in str(ffi.typeof(cb2)) assert '__stdcall' not in str(ffi.typeof(cb1)) py.test.raises(TypeError, lib.call1, cb2) py.test.raises(TypeError, lib.call2, cb1) else: assert '__stdcall' not in str(ffi.typeof(cb2)) assert ffi.typeof(cb2) is ffi.typeof(cb1) def test_win32_calling_convention_1(): ffi = FFI() ffi.cdef(""" int __cdecl call1(int(__cdecl *cb)(int)); int __stdcall call2(int(__stdcall *cb)(int)); int (__cdecl *const cb1)(int); int (__stdcall *const cb2)(int); """) lib = verify(ffi, 'test_win32_calling_convention_1', r""" #ifndef _MSC_VER # define __cdecl # define __stdcall #endif int __cdecl cb1(int x) { return x * 2; } int __stdcall cb2(int x) { return x * 3; } int __cdecl call1(int(__cdecl *cb)(int)) { int i, result = 0; //printf("here1\n"); //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); for (i = 0; i < 1000; i++) result += cb(i); //printf("result = %d\n", result); return result; } int __stdcall call2(int(__stdcall *cb)(int)) { int i, result = 0; //printf("here1\n"); //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); for (i = 0; i < 1000; i++) result += cb(-i); //printf("result = %d\n", result); return result; } """) #print '<<< cb1 =', ffi.addressof(lib, 'cb1') ptr_call1 = ffi.addressof(lib, 'call1') assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 #print '<<< cb2 =', ffi.addressof(lib, 'cb2') ptr_call2 = ffi.addressof(lib, 'call2') assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 #print '<<< done' def test_win32_calling_convention_2(): # any mistake in the declaration of plain function (including the # precise argument types and, here, the calling convention) are # automatically corrected. But this does not apply to the 'cb' # function pointer argument. ffi = FFI() ffi.cdef(""" int __stdcall call1(int(__cdecl *cb)(int)); int __cdecl call2(int(__stdcall *cb)(int)); int (__cdecl *const cb1)(int); int (__stdcall *const cb2)(int); """) lib = verify(ffi, 'test_win32_calling_convention_2', """ #ifndef _MSC_VER # define __cdecl # define __stdcall #endif int __cdecl call1(int(__cdecl *cb)(int)) { int i, result = 0; for (i = 0; i < 1000; i++) result += cb(i); return result; } int __stdcall call2(int(__stdcall *cb)(int)) { int i, result = 0; for (i = 0; i < 1000; i++) result += cb(-i); return result; } int __cdecl cb1(int x) { return x * 2; } int __stdcall cb2(int x) { return x * 3; } """) ptr_call1 = ffi.addressof(lib, 'call1') ptr_call2 = ffi.addressof(lib, 'call2') if sys.platform == 'win32' and not sys.maxsize > 2**32: py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 def test_win32_calling_convention_3(): ffi = FFI() ffi.cdef(""" struct point { int x, y; }; int (*const cb1)(struct point); int (__stdcall *const cb2)(struct point); struct point __stdcall call1(int(*cb)(struct point)); struct point call2(int(__stdcall *cb)(struct point)); """) lib = verify(ffi, 'test_win32_calling_convention_3', r""" #ifndef _MSC_VER # define __cdecl # define __stdcall #endif struct point { int x, y; }; int cb1(struct point pt) { return pt.x + 10 * pt.y; } int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } struct point __stdcall call1(int(__cdecl *cb)(struct point)) { int i; struct point result = { 0, 0 }; //printf("here1\n"); //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); for (i = 0; i < 1000; i++) { struct point p = { i, -i }; int r = cb(p); result.x += r; result.y -= r; } return result; } struct point __cdecl call2(int(__stdcall *cb)(struct point)) { int i; struct point result = { 0, 0 }; for (i = 0; i < 1000; i++) { struct point p = { -i, i }; int r = cb(p); result.x += r; result.y -= r; } return result; } """) ptr_call1 = ffi.addressof(lib, 'call1') ptr_call2 = ffi.addressof(lib, 'call2') if sys.platform == 'win32' and not sys.maxsize > 2**32: py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) pt = lib.call1(ffi.addressof(lib, 'cb1')) assert (pt.x, pt.y) == (-9*500*999, 9*500*999) pt = ptr_call1(ffi.addressof(lib, 'cb1')) assert (pt.x, pt.y) == (-9*500*999, 9*500*999) pt = lib.call2(ffi.addressof(lib, 'cb2')) assert (pt.x, pt.y) == (99*500*999, -99*500*999) pt = ptr_call2(ffi.addressof(lib, 'cb2')) assert (pt.x, pt.y) == (99*500*999, -99*500*999) def test_extern_python_1(): ffi = FFI() ffi.cdef(""" extern "Python" { int bar(int, int); void baz(int, int); int bok(void); void boz(void); } """) lib = verify(ffi, 'test_extern_python_1', "") assert ffi.typeof(lib.bar) == ffi.typeof("int(*)(int, int)") with FdWriteCapture() as f: res = lib.bar(4, 5) assert res == 0 assert f.getvalue() == ( b"extern \"Python\": function bar() called, but no code was attached " b"to it yet with @ffi.def_extern(). Returning 0.\n") @ffi.def_extern("bar") def my_bar(x, y): seen.append(("Bar", x, y)) return x * y assert my_bar != lib.bar seen = [] res = lib.bar(6, 7) assert seen == [("Bar", 6, 7)] assert res == 42 def baz(x, y): seen.append(("Baz", x, y)) baz1 = ffi.def_extern()(baz) assert baz1 is baz seen = [] baz(long(40), long(4)) res = lib.baz(long(50), long(8)) assert res is None assert seen == [("Baz", 40, 4), ("Baz", 50, 8)] assert type(seen[0][1]) is type(seen[0][2]) is long assert type(seen[1][1]) is type(seen[1][2]) is int @ffi.def_extern(name="bok") def bokk(): seen.append("Bok") return 42 seen = [] assert lib.bok() == 42 assert seen == ["Bok"] @ffi.def_extern() def boz(): seen.append("Boz") seen = [] assert lib.boz() is None assert seen == ["Boz"] def test_extern_python_bogus_name(): ffi = FFI() ffi.cdef("int abc;") lib = verify(ffi, 'test_extern_python_bogus_name', "int abc;") def fn(): pass py.test.raises(ffi.error, ffi.def_extern("unknown_name"), fn) py.test.raises(ffi.error, ffi.def_extern("abc"), fn) assert lib.abc == 0 e = py.test.raises(ffi.error, ffi.def_extern("abc"), fn) assert str(e.value) == ("ffi.def_extern('abc'): no 'extern \"Python\"' " "function with this name") e = py.test.raises(ffi.error, ffi.def_extern(), fn) assert str(e.value) == ("ffi.def_extern('fn'): no 'extern \"Python\"' " "function with this name") # py.test.raises(TypeError, ffi.def_extern(42), fn) py.test.raises((TypeError, AttributeError), ffi.def_extern(), "foo") class X: pass x = X() x.__name__ = x py.test.raises(TypeError, ffi.def_extern(), x) def test_extern_python_bogus_result_type(): ffi = FFI() ffi.cdef("""extern "Python" void bar(int);""") lib = verify(ffi, 'test_extern_python_bogus_result_type', "") # @ffi.def_extern() def bar(n): return n * 10 with StdErrCapture() as f: res = lib.bar(321) assert res is None assert f.getvalue() == ( "From cffi callback %r:\n" % (bar,) + "Trying to convert the result back to C:\n" "TypeError: callback with the return type 'void' must return None\n") def test_extern_python_redefine(): ffi = FFI() ffi.cdef("""extern "Python" int bar(int);""") lib = verify(ffi, 'test_extern_python_redefine', "") # @ffi.def_extern() def bar(n): return n * 10 assert lib.bar(42) == 420 # @ffi.def_extern() def bar(n): return -n assert lib.bar(42) == -42 def test_extern_python_struct(): ffi = FFI() ffi.cdef(""" struct foo_s { int a, b, c; }; extern "Python" int bar(int, struct foo_s, int); extern "Python" { struct foo_s baz(int, int); struct foo_s bok(void); } """) lib = verify(ffi, 'test_extern_python_struct', "struct foo_s { int a, b, c; };") # @ffi.def_extern() def bar(x, s, z): return x + s.a + s.b + s.c + z res = lib.bar(1000, [1001, 1002, 1004], 1008) assert res == 5015 # @ffi.def_extern() def baz(x, y): return [x + y, x - y, x * y] res = lib.baz(1000, 42) assert res.a == 1042 assert res.b == 958 assert res.c == 42000 # @ffi.def_extern() def bok(): return [10, 20, 30] res = lib.bok() assert [res.a, res.b, res.c] == [10, 20, 30] def test_extern_python_long_double(): ffi = FFI() ffi.cdef(""" extern "Python" int bar(int, long double, int); extern "Python" long double baz(int, int); extern "Python" long double bok(void); """) lib = verify(ffi, 'test_extern_python_long_double', "") # @ffi.def_extern() def bar(x, l, z): seen.append((x, l, z)) return 6 seen = [] lib.bar(10, 3.5, 20) expected = ffi.cast("long double", 3.5) assert repr(seen) == repr([(10, expected, 20)]) # @ffi.def_extern() def baz(x, z): assert x == 10 and z == 20 return expected res = lib.baz(10, 20) assert repr(res) == repr(expected) # @ffi.def_extern() def bok(): return expected res = lib.bok() assert repr(res) == repr(expected) def test_extern_python_signature(): ffi = FFI() lib = verify(ffi, 'test_extern_python_signature', "") py.test.raises(TypeError, ffi.def_extern(425), None) py.test.raises(TypeError, ffi.def_extern, 'a', 'b', 'c', 'd') def test_extern_python_errors(): ffi = FFI() ffi.cdef(""" extern "Python" int bar(int); """) lib = verify(ffi, 'test_extern_python_errors', "") seen = [] def oops(*args): seen.append(args) @ffi.def_extern(onerror=oops) def bar(x): return x + "" assert lib.bar(10) == 0 @ffi.def_extern(name="bar", onerror=oops, error=-66) def bar2(x): return x + "" assert lib.bar(10) == -66 assert len(seen) == 2 exc, val, tb = seen[0] assert exc is TypeError assert isinstance(val, TypeError) assert tb.tb_frame.f_code.co_name == "bar" exc, val, tb = seen[1] assert exc is TypeError assert isinstance(val, TypeError) assert tb.tb_frame.f_code.co_name == "bar2" # # a case where 'onerror' is not callable py.test.raises(TypeError, ffi.def_extern(name='bar', onerror=42), lambda x: x) def test_extern_python_stdcall(): ffi = FFI() ffi.cdef(""" extern "Python" int __stdcall foo(int); extern "Python" int WINAPI bar(int); int (__stdcall * mycb1)(int); int indirect_call(int); """) lib = verify(ffi, 'test_extern_python_stdcall', """ #ifndef _MSC_VER # define __stdcall #endif static int (__stdcall * mycb1)(int); static int indirect_call(int x) { return mycb1(x); } """) # @ffi.def_extern() def foo(x): return x + 42 @ffi.def_extern() def bar(x): return x + 43 assert lib.foo(100) == 142 assert lib.bar(100) == 143 lib.mycb1 = lib.foo assert lib.mycb1(200) == 242 assert lib.indirect_call(300) == 342 cffi-1.5.2/testing/cffi1/test_zdist.py0000664000175000017500000004022012657646311020100 0ustar arigoarigo00000000000000import sys, os, py import subprocess import cffi from testing.udir import udir def chdir_to_tmp(f): f.chdir_to_tmp = True return f def from_outside(f): f.chdir_to_tmp = False return f class TestDist(object): def setup_method(self, meth): self.executable = os.path.abspath(sys.executable) self.rootdir = os.path.abspath(os.path.dirname(os.path.dirname( cffi.__file__))) self.udir = udir.join(meth.__name__) os.mkdir(str(self.udir)) if meth.chdir_to_tmp: self.saved_cwd = os.getcwd() os.chdir(str(self.udir)) def teardown_method(self, meth): if hasattr(self, 'saved_cwd'): os.chdir(self.saved_cwd) def run(self, args, cwd=None): env = os.environ.copy() # a horrible hack to prevent distutils from finding ~/.pydistutils.cfg # (there is the --no-user-cfg option, but not in Python 2.6...) env['HOME'] = '/this/path/does/not/exist' if cwd is None: newpath = self.rootdir if 'PYTHONPATH' in env: newpath += os.pathsep + env['PYTHONPATH'] env['PYTHONPATH'] = newpath subprocess.check_call([self.executable] + args, cwd=cwd, env=env) def _prepare_setuptools(self): if hasattr(TestDist, '_setuptools_ready'): return try: import setuptools except ImportError: py.test.skip("setuptools not found") if os.path.exists(os.path.join(self.rootdir, 'setup.py')): self.run(['setup.py', 'egg_info'], cwd=self.rootdir) TestDist._setuptools_ready = True def check_produced_files(self, content, curdir=None): if curdir is None: curdir = str(self.udir) found_so = None for name in os.listdir(curdir): if (name.endswith('.so') or name.endswith('.pyd') or name.endswith('.dylib') or name.endswith('.dll')): found_so = os.path.join(curdir, name) # foo.so => foo parts = name.split('.') del parts[-1] if len(parts) > 1 and parts[-1] != 'bar': # foo.cpython-34m.so => foo, but foo.bar.so => foo.bar del parts[-1] name = '.'.join(parts) # foo_d => foo (Python 2 debug builds) if name.endswith('_d') and hasattr(sys, 'gettotalrefcount'): name = name[:-2] name += '.SO' if name.startswith('pycparser') and name.endswith('.egg'): continue # no clue why this shows up sometimes and not others if name == '.eggs': continue # seems new in 3.5, ignore it assert name in content, "found unexpected file %r" % ( os.path.join(curdir, name),) value = content.pop(name) if value is None: assert name.endswith('.SO') or ( os.path.isfile(os.path.join(curdir, name))) else: subdir = os.path.join(curdir, name) assert os.path.isdir(subdir) if value == '?': continue found_so = self.check_produced_files(value, subdir) or found_so assert content == {}, "files or dirs not produced in %r: %r" % ( curdir, content.keys()) return found_so @chdir_to_tmp def test_empty(self): self.check_produced_files({}) @chdir_to_tmp def test_abi_emit_python_code_1(self): ffi = cffi.FFI() ffi.set_source("package_name_1.mymod", None) ffi.emit_python_code('xyz.py') self.check_produced_files({'xyz.py': None}) @chdir_to_tmp def test_abi_emit_python_code_2(self): ffi = cffi.FFI() ffi.set_source("package_name_1.mymod", None) py.test.raises(IOError, ffi.emit_python_code, 'unexisting/xyz.py') @from_outside def test_abi_emit_python_code_3(self): ffi = cffi.FFI() ffi.set_source("package_name_1.mymod", None) ffi.emit_python_code(str(self.udir.join('xyt.py'))) self.check_produced_files({'xyt.py': None}) @chdir_to_tmp def test_abi_compile_1(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", None) x = ffi.compile() self.check_produced_files({'mod_name_in_package': {'mymod.py': None}}) assert x == os.path.join('.', 'mod_name_in_package', 'mymod.py') @chdir_to_tmp def test_abi_compile_2(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", None) x = ffi.compile('build2') self.check_produced_files({'build2': { 'mod_name_in_package': {'mymod.py': None}}}) assert x == os.path.join('build2', 'mod_name_in_package', 'mymod.py') @from_outside def test_abi_compile_3(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", None) tmpdir = str(self.udir.join('build3')) x = ffi.compile(tmpdir) self.check_produced_files({'build3': { 'mod_name_in_package': {'mymod.py': None}}}) assert x == os.path.join(tmpdir, 'mod_name_in_package', 'mymod.py') @chdir_to_tmp def test_api_emit_c_code_1(self): ffi = cffi.FFI() ffi.set_source("package_name_1.mymod", "/*code would be here*/") ffi.emit_c_code('xyz.c') self.check_produced_files({'xyz.c': None}) @chdir_to_tmp def test_api_emit_c_code_2(self): ffi = cffi.FFI() ffi.set_source("package_name_1.mymod", "/*code would be here*/") py.test.raises(IOError, ffi.emit_c_code, 'unexisting/xyz.c') @from_outside def test_api_emit_c_code_3(self): ffi = cffi.FFI() ffi.set_source("package_name_1.mymod", "/*code would be here*/") ffi.emit_c_code(str(self.udir.join('xyu.c'))) self.check_produced_files({'xyu.c': None}) @chdir_to_tmp def test_api_compile_1(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") x = ffi.compile() if sys.platform != 'win32': sofile = self.check_produced_files({ 'mod_name_in_package': {'mymod.SO': None, 'mymod.c': None, 'mymod.o': None}}) assert os.path.isabs(x) and os.path.samefile(x, sofile) else: self.check_produced_files({ 'mod_name_in_package': {'mymod.SO': None, 'mymod.c': None}, 'Release': '?'}) @chdir_to_tmp def test_api_compile_2(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") x = ffi.compile('output') if sys.platform != 'win32': sofile = self.check_produced_files({ 'output': {'mod_name_in_package': {'mymod.SO': None, 'mymod.c': None, 'mymod.o': None}}}) assert os.path.isabs(x) and os.path.samefile(x, sofile) else: self.check_produced_files({ 'output': {'mod_name_in_package': {'mymod.SO': None, 'mymod.c': None}, 'Release': '?'}}) @from_outside def test_api_compile_3(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") x = ffi.compile(str(self.udir.join('foo'))) if sys.platform != 'win32': sofile = self.check_produced_files({ 'foo': {'mod_name_in_package': {'mymod.SO': None, 'mymod.c': None, 'mymod.o': None}}}) assert os.path.isabs(x) and os.path.samefile(x, sofile) else: self.check_produced_files({ 'foo': {'mod_name_in_package': {'mymod.SO': None, 'mymod.c': None}, 'Release': '?'}}) @chdir_to_tmp def test_api_compile_explicit_target_1(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") x = ffi.compile(target="foo.bar.*") if sys.platform != 'win32': sofile = self.check_produced_files({ 'mod_name_in_package': {'foo.bar.SO': None, 'mymod.c': None, 'mymod.o': None}}) assert os.path.isabs(x) and os.path.samefile(x, sofile) else: self.check_produced_files({ 'mod_name_in_package': {'foo.bar.SO': None, 'mymod.c': None}, 'Release': '?'}) @chdir_to_tmp def test_api_compile_explicit_target_3(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") x = ffi.compile(target="foo.bar.baz") if sys.platform != 'win32': self.check_produced_files({ 'mod_name_in_package': {'foo.bar.baz': None, 'mymod.c': None, 'mymod.o': None}}) sofile = os.path.join(str(self.udir), 'mod_name_in_package', 'foo.bar.baz') assert os.path.isabs(x) and os.path.samefile(x, sofile) else: self.check_produced_files({ 'mod_name_in_package': {'foo.bar.baz': None, 'mymod.c': None}, 'Release': '?'}) @chdir_to_tmp def test_api_distutils_extension_1(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") ext = ffi.distutils_extension() self.check_produced_files({'build': { 'mod_name_in_package': {'mymod.c': None}}}) if hasattr(os.path, 'samefile'): assert os.path.samefile(ext.sources[0], 'build/mod_name_in_package/mymod.c') @from_outside def test_api_distutils_extension_2(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") ext = ffi.distutils_extension(str(self.udir.join('foo'))) self.check_produced_files({'foo': { 'mod_name_in_package': {'mymod.c': None}}}) if hasattr(os.path, 'samefile'): assert os.path.samefile(ext.sources[0], str(self.udir.join('foo/mod_name_in_package/mymod.c'))) def _make_distutils_api(self): os.mkdir("src") os.mkdir(os.path.join("src", "pack1")) with open(os.path.join("src", "pack1", "__init__.py"), "w") as f: pass with open("setup.py", "w") as f: f.write("""if 1: import cffi ffi = cffi.FFI() ffi.set_source("pack1.mymod", "/*code would be here*/") from distutils.core import setup setup(name='example1', version='0.1', packages=['pack1'], package_dir={'': 'src'}, ext_modules=[ffi.distutils_extension()]) """) @chdir_to_tmp def test_distutils_api_1(self): self._make_distutils_api() self.run(["setup.py", "build"]) self.check_produced_files({'setup.py': None, 'build': '?', 'src': {'pack1': {'__init__.py': None}}}) @chdir_to_tmp def test_distutils_api_2(self): self._make_distutils_api() self.run(["setup.py", "build_ext", "-i"]) self.check_produced_files({'setup.py': None, 'build': '?', 'src': {'pack1': {'__init__.py': None, 'mymod.SO': None}}}) def _make_setuptools_abi(self): self._prepare_setuptools() os.mkdir("src0") os.mkdir(os.path.join("src0", "pack2")) with open(os.path.join("src0", "pack2", "__init__.py"), "w") as f: pass with open(os.path.join("src0", "pack2", "_build.py"), "w") as f: f.write("""if 1: import cffi ffi = cffi.FFI() ffi.set_source("pack2.mymod", None) """) with open("setup.py", "w") as f: f.write("""if 1: from setuptools import setup setup(name='example1', version='0.1', packages=['pack2'], package_dir={'': 'src0'}, cffi_modules=["src0/pack2/_build.py:ffi"]) """) @chdir_to_tmp def test_setuptools_abi_1(self): self._make_setuptools_abi() self.run(["setup.py", "build"]) self.check_produced_files({'setup.py': None, 'build': '?', 'src0': {'pack2': {'__init__.py': None, '_build.py': None}}}) @chdir_to_tmp def test_setuptools_abi_2(self): self._make_setuptools_abi() self.run(["setup.py", "build_ext", "-i"]) self.check_produced_files({'setup.py': None, 'src0': {'pack2': {'__init__.py': None, '_build.py': None, 'mymod.py': None}}}) def _make_setuptools_api(self): self._prepare_setuptools() os.mkdir("src1") os.mkdir(os.path.join("src1", "pack3")) with open(os.path.join("src1", "pack3", "__init__.py"), "w") as f: pass with open(os.path.join("src1", "pack3", "_build.py"), "w") as f: f.write("""if 1: import cffi ffi = cffi.FFI() ffi.set_source("pack3.mymod", "/*code would be here*/") ffi._hi_there = 42 """) with open("setup.py", "w") as f: f.write("from __future__ import print_function\n" """if 1: from setuptools import setup from distutils.command.build_ext import build_ext import os class TestBuildExt(build_ext): def pre_run(self, ext, ffi): print('_make_setuptools_api: in pre_run:', end=" ") assert ffi._hi_there == 42 assert ext.name == "pack3.mymod" fn = os.path.join(os.path.dirname(self.build_lib), '..', 'see_me') print('creating %r' % (fn,)) open(fn, 'w').close() setup(name='example1', version='0.1', packages=['pack3'], package_dir={'': 'src1'}, cffi_modules=["src1/pack3/_build.py:ffi"], cmdclass={'build_ext': TestBuildExt}, ) """) @chdir_to_tmp def test_setuptools_api_1(self): self._make_setuptools_api() self.run(["setup.py", "build"]) self.check_produced_files({'setup.py': None, 'build': '?', 'see_me': None, 'src1': {'pack3': {'__init__.py': None, '_build.py': None}}}) @chdir_to_tmp def test_setuptools_api_2(self): self._make_setuptools_api() self.run(["setup.py", "build_ext", "-i"]) self.check_produced_files({'setup.py': None, 'build': '?', 'see_me': None, 'src1': {'pack3': {'__init__.py': None, '_build.py': None, 'mymod.SO': None}}}) cffi-1.5.2/testing/cffi1/test_dlopen_unicode_literals.py0000664000175000017500000000027212657646311023634 0ustar arigoarigo00000000000000import py, os s = """from __future__ import unicode_literals """ with open(os.path.join(os.path.dirname(__file__), 'test_dlopen.py')) as f: s += f.read() exec(py.code.compile(s)) cffi-1.5.2/testing/__init__.py0000664000175000017500000000000012657646311016443 0ustar arigoarigo00000000000000cffi-1.5.2/testing/udir.py0000664000175000017500000000010312657646311015653 0ustar arigoarigo00000000000000import py udir = py.path.local.make_numbered_dir(prefix = 'ffi-') cffi-1.5.2/setup_base.py0000664000175000017500000000162112657646311015373 0ustar arigoarigo00000000000000import 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.builtin_module_names 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-1.5.2/demo/0000775000175000017500000000000012657646372013622 5ustar arigoarigo00000000000000cffi-1.5.2/demo/py.cleanup0000775000175000017500000000153612657646311015624 0ustar arigoarigo00000000000000#! /usr/bin/env python import sys, os, stat from bsdopendirtype import opendir def clean(path): global count try: content = opendir(path) except OSError: print >> 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-1.5.2/demo/gmp.py0000664000175000017500000000175412657646311014757 0ustar arigoarigo00000000000000import sys # # This is only a demo based on the GMP library. # There is a rather more complete (but perhaps outdated) version available at: # http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files # try: from _gmp_cffi import ffi, lib except ImportError: print 'run gmp_build first, then make sure the shared object is on sys.path' sys.exit(1) # ffi "knows" about the declared variables and functions from the # cdef parts of the module created from gmp_build # lib "knows" how to call the functions from the set_source parts # of the module. # ____________________________________________________________ a = ffi.new("mpz_t") b = ffi.new("mpz_t") if len(sys.argv) < 3: print 'call as %s bigint1, bigint2' % sys.argv[0] sys.exit(2) 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-1.5.2/demo/setup.py0000664000175000017500000000054612657646311015332 0ustar arigoarigo00000000000000# # 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-1.5.2/demo/readdir_ctypes.py0000664000175000017500000000337512657646311017176 0ustar arigoarigo00000000000000# 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-1.5.2/demo/pyobj.py0000664000175000017500000000650712657646311015320 0ustar arigoarigo00000000000000 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=1.0.dev0"], cffi_modules=[ "bsdopendirtype_build.py:ffi", ], install_requires=["cffi>=1.0.dev0"], # should maybe be "cffi-backend" only? zip_safe=False, ) cffi-1.5.2/demo/recopendirtype.py0000664000175000017500000000253212657646311017223 0ustar arigoarigo00000000000000from _recopendirtype import ffi, lib 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 = b'.' dirname = dir if not dirname.endswith(b'/'): dirname += b'/' dirp = lib.opendir(dir) if dirp == ffi.NULL: raise _posix_error() dirent = ffi.new("struct dirent *") result = ffi.new("struct dirent **") try: while True: ffi.errno = 0 err = lib.readdir_r(dirp, dirent, result) if err: # really got an error raise OSError(err, os.strerror(err)) if result[0] == ffi.NULL: return # name = ffi.string(dirent.d_name) if name == b'.' or name == b'..': 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) if __name__ == '__main__': for name, smode in opendir(b'/tmp'): print(hex(smode), name) cffi-1.5.2/demo/pwuid.py0000664000175000017500000000024612657646311015317 0ustar arigoarigo00000000000000import sys, os # run pwuid_build first, then make sure the shared object is on sys.path from _pwuid_cffi import ffi, lib print ffi.string(lib.getpwuid(0).pw_name) cffi-1.5.2/demo/winclipboard.py0000664000175000017500000000227212657646311016645 0ustar arigoarigo00000000000000__author__ = "Israel Fruchter " import sys, os if not sys.platform == 'win32': raise Exception("Windows-only demo") try: from _winclipboard_cffi import ffi, lib except ImportError: print 'run winclipboard_build first, then make sure the shared object is on sys.path' sys.exit(1) # ffi "knows" about the declared variables and functions from the # cdef parts of the module _winclipboard_cffi created, # lib "knows" how to call the functions from the set_source parts # of the module. def CopyToClipboard(string): ''' use win32 api to copy `string` to the clipboard ''' hWnd = lib.GetConsoleWindow() if lib.OpenClipboard(hWnd): cstring = ffi.new("char[]", string) size = ffi.sizeof(cstring) # make it a moveable memory for other processes hGlobal = lib.GlobalAlloc(lib.GMEM_MOVEABLE, size) buffer = lib.GlobalLock(hGlobal) lib.memcpy(buffer, cstring, size) lib.GlobalUnlock(hGlobal) res = lib.EmptyClipboard() res = lib.SetClipboardData(lib.CF_TEXT, buffer) lib.CloseClipboard() CopyToClipboard("hello world from cffi") cffi-1.5.2/demo/recopendirtype_build.py0000664000175000017500000000061112657646311020376 0ustar arigoarigo00000000000000from cffi import FFI import bsdopendirtype_build ffi = FFI() # ========== This is a demo of ffi.include() ========== ffi.include(bsdopendirtype_build.ffi) ffi.cdef(""" int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); """) ffi.set_source("_recopendirtype", """ #include #include """) if __name__ == '__main__': ffi.compile() cffi-1.5.2/demo/_curses_build.py0000664000175000017500000002124412657646311017012 0ustar arigoarigo00000000000000import sys if sys.platform == 'win32': #This module does not exist in windows raise ImportError('No module named _curses') from cffi import FFI ffi = FFI() ffi.cdef(""" typedef ... WINDOW; typedef ... SCREEN; typedef unsigned long... mmask_t; typedef unsigned char bool; typedef unsigned long... chtype; typedef chtype attr_t; typedef struct { short id; /* ID to distinguish multiple devices */ int x, y, z; /* event coordinates (character-cell) */ mmask_t bstate; /* button state bits */ } MEVENT; static const int ERR, OK; static const int TRUE, FALSE; static const int KEY_MIN, KEY_MAX; 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 chtype A_ATTRIBUTES; static const chtype A_NORMAL; static const chtype A_STANDOUT; static const chtype A_UNDERLINE; static const chtype A_REVERSE; static const chtype A_BLINK; static const chtype A_DIM; static const chtype A_BOLD; static const chtype A_ALTCHARSET; static const chtype A_INVIS; static const chtype A_PROTECT; static const chtype A_CHARTEXT; static const chtype A_COLOR; static const int BUTTON1_RELEASED; static const int BUTTON1_PRESSED; static const int BUTTON1_CLICKED; static const int BUTTON1_DOUBLE_CLICKED; static const int BUTTON1_TRIPLE_CLICKED; static const int BUTTON2_RELEASED; static const int BUTTON2_PRESSED; static const int BUTTON2_CLICKED; static const int BUTTON2_DOUBLE_CLICKED; static const int BUTTON2_TRIPLE_CLICKED; static const int BUTTON3_RELEASED; static const int BUTTON3_PRESSED; static const int BUTTON3_CLICKED; static const int BUTTON3_DOUBLE_CLICKED; static const int BUTTON3_TRIPLE_CLICKED; static const int BUTTON4_RELEASED; static const int BUTTON4_PRESSED; static const int BUTTON4_CLICKED; static const int BUTTON4_DOUBLE_CLICKED; static const int BUTTON4_TRIPLE_CLICKED; static const int BUTTON_SHIFT; static const int BUTTON_CTRL; static const int BUTTON_ALT; static const int ALL_MOUSE_EVENTS; static const int REPORT_MOUSE_POSITION; int setupterm(char *, int, int *); WINDOW *stdscr; int COLORS; int COLOR_PAIRS; int COLS; int LINES; int baudrate(void); int beep(void); int box(WINDOW *, chtype, chtype); bool can_change_color(void); int cbreak(void); int clearok(WINDOW *, bool); int color_content(short, short*, short*, short*); int copywin(const WINDOW*, WINDOW*, int, int, int, int, int, int, int); int curs_set(int); int def_prog_mode(void); int def_shell_mode(void); int delay_output(int); int delwin(WINDOW *); WINDOW * derwin(WINDOW *, int, int, int, int); int doupdate(void); int echo(void); int endwin(void); char erasechar(void); void filter(void); int flash(void); int flushinp(void); chtype getbkgd(WINDOW *); WINDOW * getwin(FILE *); int halfdelay(int); bool has_colors(void); bool has_ic(void); bool has_il(void); void idcok(WINDOW *, bool); int idlok(WINDOW *, bool); void immedok(WINDOW *, bool); WINDOW * initscr(void); int init_color(short, short, short, short); int init_pair(short, short, short); int intrflush(WINDOW *, bool); bool isendwin(void); bool is_linetouched(WINDOW *, int); bool is_wintouched(WINDOW *); const char * keyname(int); int keypad(WINDOW *, bool); char killchar(void); int leaveok(WINDOW *, bool); char * longname(void); int meta(WINDOW *, bool); int mvderwin(WINDOW *, int, int); int mvwaddch(WINDOW *, int, int, const chtype); int mvwaddnstr(WINDOW *, int, int, const char *, int); int mvwaddstr(WINDOW *, int, int, const char *); int mvwchgat(WINDOW *, int, int, int, attr_t, short, const void *); int mvwdelch(WINDOW *, int, int); int mvwgetch(WINDOW *, int, int); int mvwgetnstr(WINDOW *, int, int, char *, int); int mvwin(WINDOW *, int, int); chtype mvwinch(WINDOW *, int, int); int mvwinnstr(WINDOW *, int, int, char *, int); int mvwinsch(WINDOW *, int, int, chtype); int mvwinsnstr(WINDOW *, int, int, const char *, int); int mvwinsstr(WINDOW *, int, int, const char *); int napms(int); WINDOW * newpad(int, int); WINDOW * newwin(int, int, int, int); int nl(void); int nocbreak(void); int nodelay(WINDOW *, bool); int noecho(void); int nonl(void); void noqiflush(void); int noraw(void); int notimeout(WINDOW *, bool); int overlay(const WINDOW*, WINDOW *); int overwrite(const WINDOW*, WINDOW *); int pair_content(short, short*, short*); int pechochar(WINDOW *, const chtype); int pnoutrefresh(WINDOW*, int, int, int, int, int, int); int prefresh(WINDOW *, int, int, int, int, int, int); int putwin(WINDOW *, FILE *); void qiflush(void); int raw(void); int redrawwin(WINDOW *); int resetty(void); int reset_prog_mode(void); int reset_shell_mode(void); int savetty(void); int scroll(WINDOW *); int scrollok(WINDOW *, bool); int start_color(void); WINDOW * subpad(WINDOW *, int, int, int, int); WINDOW * subwin(WINDOW *, int, int, int, int); int syncok(WINDOW *, bool); chtype termattrs(void); char * termname(void); int touchline(WINDOW *, int, int); int touchwin(WINDOW *); int typeahead(int); int ungetch(int); int untouchwin(WINDOW *); void use_env(bool); int waddch(WINDOW *, const chtype); int waddnstr(WINDOW *, const char *, int); int waddstr(WINDOW *, const char *); int wattron(WINDOW *, int); int wattroff(WINDOW *, int); int wattrset(WINDOW *, int); int wbkgd(WINDOW *, chtype); void wbkgdset(WINDOW *, chtype); int wborder(WINDOW *, chtype, chtype, chtype, chtype, chtype, chtype, chtype, chtype); int wchgat(WINDOW *, int, attr_t, short, const void *); int wclear(WINDOW *); int wclrtobot(WINDOW *); int wclrtoeol(WINDOW *); void wcursyncup(WINDOW *); int wdelch(WINDOW *); int wdeleteln(WINDOW *); int wechochar(WINDOW *, const chtype); int werase(WINDOW *); int wgetch(WINDOW *); int wgetnstr(WINDOW *, char *, int); int whline(WINDOW *, chtype, int); chtype winch(WINDOW *); int winnstr(WINDOW *, char *, int); int winsch(WINDOW *, chtype); int winsdelln(WINDOW *, int); int winsertln(WINDOW *); int winsnstr(WINDOW *, const char *, int); int winsstr(WINDOW *, const char *); int wmove(WINDOW *, int, int); int wresize(WINDOW *, int, int); int wnoutrefresh(WINDOW *); int wredrawln(WINDOW *, int, int); int wrefresh(WINDOW *); int wscrl(WINDOW *, int); int wsetscrreg(WINDOW *, int, int); int wstandout(WINDOW *); int wstandend(WINDOW *); void wsyncdown(WINDOW *); void wsyncup(WINDOW *); void wtimeout(WINDOW *, int); int wtouchln(WINDOW *, int, int, int); int wvline(WINDOW *, chtype, int); int tigetflag(char *); int tigetnum(char *); char * tigetstr(char *); int putp(const char *); char * tparm(const char *, ...); int getattrs(const WINDOW *); int getcurx(const WINDOW *); int getcury(const WINDOW *); int getbegx(const WINDOW *); int getbegy(const WINDOW *); int getmaxx(const WINDOW *); int getmaxy(const WINDOW *); int getparx(const WINDOW *); int getpary(const WINDOW *); int getmouse(MEVENT *); int ungetmouse(MEVENT *); mmask_t mousemask(mmask_t, mmask_t *); bool wenclose(const WINDOW *, int, int); int mouseinterval(int); void setsyx(int y, int x); const char *unctrl(chtype); int use_default_colors(void); int has_key(int); bool is_term_resized(int, int); #define _m_STRICT_SYSV_CURSES ... #define _m_NCURSES_MOUSE_VERSION ... #define _m_NetBSD ... int _m_ispad(WINDOW *); chtype acs_map[]; // For _curses_panel: typedef ... PANEL; WINDOW *panel_window(const PANEL *); void update_panels(void); int hide_panel(PANEL *); int show_panel(PANEL *); int del_panel(PANEL *); int top_panel(PANEL *); int bottom_panel(PANEL *); PANEL *new_panel(WINDOW *); PANEL *panel_above(const PANEL *); PANEL *panel_below(const PANEL *); int set_panel_userptr(PANEL *, void *); const void *panel_userptr(const PANEL *); int move_panel(PANEL *, int, int); int replace_panel(PANEL *,WINDOW *); int panel_hidden(const PANEL *); void _m_getsyx(int *yx); """) ffi.set_source("_curses_cffi", """ #ifdef __APPLE__ /* the following define is necessary for OS X 10.6+; without it, the Apple-supplied ncurses.h sets NCURSES_OPAQUE to 1, and then Python can't get at the WINDOW flags field. */ #define NCURSES_OPAQUE 0 #endif #include #include #include #if defined STRICT_SYSV_CURSES #define _m_STRICT_SYSV_CURSES TRUE #else #define _m_STRICT_SYSV_CURSES FALSE #endif #if defined NCURSES_MOUSE_VERSION #define _m_NCURSES_MOUSE_VERSION TRUE #else #define _m_NCURSES_MOUSE_VERSION FALSE #endif #if defined __NetBSD__ #define _m_NetBSD TRUE #else #define _m_NetBSD FALSE #endif int _m_ispad(WINDOW *win) { // may not have _flags (and possibly _ISPAD), // but for now let's assume that always has it return (win->_flags & _ISPAD); } void _m_getsyx(int *yx) { getsyx(yx[0], yx[1]); } """, libraries=['ncurses', 'panel']) if __name__ == '__main__': ffi.compile() cffi-1.5.2/demo/fastcsv.py0000664000175000017500000002024012657646311015634 0ustar arigoarigo00000000000000import csv import cffi # IN-PROGRESS. See the demo at the end of the file def _make_ffi_from_dialect(dialect_name): dialect = csv.get_dialect(dialect_name) 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' ffi.set_source('_fastcsv_' + dialect_name, 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) ffi.compile() def fastcsv_reader(f, dialect_name): try: module = __import__('_fastcsv_' + dialect_name) except ImportError: _make_ffi_from_dialect(dialect_name) module = __import__('_fastcsv_' + dialect_name) ffi, lib = module.ffi, module.lib # 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-1.5.2/demo/readdir_setup.py0000664000175000017500000000037112657646311017020 0ustar arigoarigo00000000000000from setuptools import setup setup( name="example", version="0.1", py_modules=["readdir"], setup_requires=["cffi>=1.0.dev0"], cffi_modules=["readdir_build.py:ffi"], install_requires=["cffi>=1.0.dev0"], zip_safe=False, ) cffi-1.5.2/demo/embedding_test.c0000664000175000017500000000052112657646311016732 0ustar arigoarigo00000000000000/* Link this program with libembedding_test.so. E.g. with gcc: gcc -o embedding_test embedding_test.c _embedding_cffi*.so */ #include extern int add(int x, int y); int main(void) { int res = add(40, 2); printf("result: %d\n", res); res = add(100, -5); printf("result: %d\n", res); return 0; } cffi-1.5.2/demo/_curses_setup.py0000664000175000017500000000046112657646311017051 0ustar arigoarigo00000000000000from setuptools import setup setup( name="_curses", version="0.1", py_modules=["_curses"], setup_requires=["cffi>=1.0.dev0"], cffi_modules=[ "_curses_build.py:ffi", ], install_requires=["cffi>=1.0.dev0"], # should maybe be "cffi-backend" only? zip_safe=False, ) cffi-1.5.2/demo/xclient.py0000664000175000017500000000137212657646311015636 0ustar arigoarigo00000000000000import sys, os # run xclient_build first, then make sure the shared object is on sys.path from _xclient_cffi import ffi, lib # ffi "knows" about the declared variables and functions from the # cdef parts of the module xclient_build created, # lib "knows" how to call the functions from the set_source parts # of the module. class XError(Exception): pass def main(): display = lib.XOpenDisplay(ffi.NULL) if display == ffi.NULL: raise XError("cannot open display") w = lib.XCreateSimpleWindow(display, lib.DefaultRootWindow(display), 10, 10, 500, 350, 0, 0, 0) lib.XMapRaised(display, w) event = ffi.new("XEvent *") lib.XNextEvent(display, event) if __name__ == '__main__': main() cffi-1.5.2/demo/xclient_build.py0000664000175000017500000000135512657646311017016 0ustar arigoarigo00000000000000from 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); """) ffi.set_source('_xclient_cffi', """ #include """, libraries=['X11']) if __name__ == '__main__': ffi.compile() cffi-1.5.2/demo/bsdopendirtype_build.py0000664000175000017500000000102712657646311020377 0ustar arigoarigo00000000000000from 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; """) ffi.set_source("_bsdopendirtype", """ #include #include """) if __name__ == '__main__': ffi.compile() cffi-1.5.2/demo/gmp_build.py0000664000175000017500000000123412657646311016127 0ustar arigoarigo00000000000000import cffi # # This is only a demo based on the GMP library. # There is a rather more complete (but perhaps outdated) 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); """) ffi.set_source('_gmp_cffi', "#include ", libraries=['gmp', 'm']) if __name__ == '__main__': ffi.compile() cffi-1.5.2/demo/winclipboard_build.py0000664000175000017500000000151412657646311020022 0ustar arigoarigo00000000000000from 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); ''') ffi.set_source('_winclipboard_cffi', ''' #include ''', libraries=["user32"]) if __name__ == '__main__': ffi.compile() cffi-1.5.2/demo/btrfs-snap.py0000664000175000017500000000213712657646311016247 0ustar arigoarigo00000000000000""" 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[]; ...; }; """) ffi.set_source("_btrfs_cffi", "#include ") ffi.compile() # ____________________________________________________________ from _btrfs_cffi import ffi, lib 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, lib.BTRFS_IOC_SNAP_CREATE_V2, args_buffer) except IOError as e: print e sys.exit(1) cffi-1.5.2/demo/readdir2_build.py0000664000175000017500000000141312657646311017037 0ustar arigoarigo00000000000000from cffi import FFI 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.set_source("_readdir2_cffi", """ #ifndef _ATFILE_SOURCE # define _ATFILE_SOURCE #endif #ifndef _BSD_SOURCE # define _BSD_SOURCE #endif #include #include #include """) if __name__ == '__main__': ffi.compile() cffi-1.5.2/demo/readdir2.py0000664000175000017500000000170112657646311015660 0ustar arigoarigo00000000000000# A Linux-only demo, using set_source() instead of hard-coding the exact layouts # import sys if not sys.platform.startswith('linux'): raise Exception("Linux-only demo") # run readdir2_build first, then make sure the shared object is on sys.path from _readdir2_cffi import ffi, lib def walk(basefd, path): print '{', path dirfd = lib.openat(basefd, path, 0) if dirfd < 0: # error in openat() return dir = lib.fdopendir(dirfd) dirent = ffi.new("struct dirent *") result = ffi.new("struct dirent **") while True: if lib.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 == lib.DT_DIR and name != '.' and name != '..': walk(dirfd, name) lib.closedir(dir) print '}' walk(-1, "/tmp") cffi-1.5.2/demo/extern_python_varargs.py0000664000175000017500000000247212657646311020625 0ustar arigoarigo00000000000000import cffi ffi = cffi.FFI() ffi.cdef(""" int my_algo(int); typedef ... va_list; extern "Python" int f(int, va_list *); int fetch_int(va_list *); double fetch_double(va_list *); void *fetch_ptr(va_list *); """) ffi.set_source("_extern_python_cffi", """ #include static int f(int, va_list *); static int f1(int n, ...) { va_list ap; va_start(ap, n); int res = f(n, &ap); va_end(ap); return res; } static int fetch_int(va_list *va) { return va_arg((*va), int); } static double fetch_double(va_list *va) { return va_arg((*va), double); } static void * fetch_ptr(va_list *va) { return va_arg((*va), void *); } static int my_algo(int n) { return f1(3, n, n+1, n+2) + f1(1, &n) + f1(2, 12.3, 45.6); } """) ffi.compile() from _extern_python_cffi import ffi, lib @ffi.def_extern() def f(n, va): if n == 3: x = lib.fetch_int(va) y = lib.fetch_int(va) z = lib.fetch_int(va) print (x, y, z) elif n == 1: ptr = lib.fetch_ptr(va) print 'ptr to:', ffi.cast("int *", ptr)[0] elif n == 2: x = lib.fetch_double(va) y = lib.fetch_double(va) print (x, y) else: raise AssertionError(n) return 14 print lib.my_algo(10) cffi-1.5.2/demo/readdir_build.py0000664000175000017500000000157212657646311016763 0ustar arigoarigo00000000000000import 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.set_source("_readdir", None) if __name__ == '__main__': ffi.compile() cffi-1.5.2/demo/api.py0000664000175000017500000000315312657646311014740 0ustar arigoarigo00000000000000import 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-1.5.2/demo/pwuid_build.py0000664000175000017500000000054612657646311016501 0ustar arigoarigo00000000000000from cffi import FFI ffi = FFI() ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; }; struct passwd *getpwuid(int uid); """) ffi.set_source('_pwuid_cffi', """ // passed to the real C compiler #include #include """) if __name__ == '__main__': ffi.compile() cffi-1.5.2/demo/readdir2_setup.py0000664000175000017500000000031112657646311017074 0ustar arigoarigo00000000000000from distutils.core import setup import readdir2_build setup( name="readdir2", version="0.1", py_modules=["readdir2"], ext_modules=[readdir2_build.ffi.distutils_extension('build')], ) cffi-1.5.2/demo/extern_python.py0000664000175000017500000000071412657646311017075 0ustar arigoarigo00000000000000import cffi ffi = cffi.FFI() ffi.cdef("""int my_algo(int); extern "Python" int f(int);""") ffi.set_source("_extern_python_cffi", """ static int f(int); static int my_algo(int n) { int i, sum = 0; for (i = 0; i < n; i++) sum += f(i); return sum; } """) ffi.compile() from _extern_python_cffi import ffi, lib @ffi.def_extern() def f(n): return n * n assert lib.my_algo(10) == 0+1+4+9+16+25+36+49+64+81 cffi-1.5.2/demo/embedding.py0000664000175000017500000000055312657646311016106 0ustar arigoarigo00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api(""" int add(int, int); """) ffi.embedding_init_code(""" from _embedding_cffi import ffi print("preparing") # printed once @ffi.def_extern() def add(x, y): print("adding %d and %d" % (x, y)) return x + y """) ffi.set_source("_embedding_cffi", "") ffi.compile(verbose=True) cffi-1.5.2/demo/manual2.py0000664000175000017500000000222712657646311015527 0ustar arigoarigo00000000000000import _cffi_backend ffi = _cffi_backend.FFI(b"manual2", _version = 0x2601, _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x00\x09\x00\x00\x00\x0B\x00\x00\x01\x03', _globals = (b'\xff\xff\xff\x0bAA',0,b'\xff\xff\xff\x0bBB',-1,b'\xff\xff\xff\x0bCC',2,b'\xff\xff\xff\x1fFOO',0x9999999999999999,b'\x00\x00\x00#close',0,b'\x00\x00\x05#stdout',0), _struct_unions = ((b'\x00\x00\x00\x03\x00\x00\x00\x00point_s',b'\x00\x00\x01\x11\xff\xff\xff\xffx',b'\x00\x00\x01\x11\xff\xff\xff\xffy'),), _enums = (b'\x00\x00\x00\x04\x00\x00\x00\x07myenum_e\x00AA,BB,CC',), _typenames = (b'\x00\x00\x00\x01myint_t',), ) # trying it out lib = ffi.dlopen(None) assert lib.AA == 0 assert lib.BB == -1 assert lib.FOO == 0x9999999999999999 x = lib.close(-42) assert x == -1 print lib.stdout print ffi.new("struct point_s *") print ffi.offsetof("struct point_s", "x") print ffi.offsetof("struct point_s", "y") print ffi.new("struct point_s[CC]") assert ffi.sizeof("struct point_s[CC]") == 2 * ffi.sizeof("struct point_s") print ffi.cast("enum myenum_e", 2) print ffi.cast("myint_t", -2) assert ffi.typeof("myint_t") == ffi.typeof("int") del ffi, lib cffi-1.5.2/demo/_curses.py0000664000175000017500000007366412657646311015650 0ustar arigoarigo00000000000000"""Reimplementation of the standard extension module '_curses' using cffi.""" import sys from functools import wraps from _curses_cffi import ffi, lib def _copy_to_globals(name): globals()[name] = getattr(lib, name) def _setup(): for name in ['ERR', 'OK', 'KEY_MIN', 'KEY_MAX', 'A_ATTRIBUTES', 'A_NORMAL', 'A_STANDOUT', 'A_UNDERLINE', 'A_REVERSE', 'A_BLINK', 'A_DIM', 'A_BOLD', 'A_ALTCHARSET', 'A_PROTECT', 'A_CHARTEXT', 'A_COLOR', 'COLOR_BLACK', 'COLOR_RED', 'COLOR_GREEN', 'COLOR_YELLOW', 'COLOR_BLUE', 'COLOR_MAGENTA', 'COLOR_CYAN', 'COLOR_WHITE', ]: _copy_to_globals(name) if not lib._m_NetBSD: _copy_to_globals('A_INVIS') for name in ['A_HORIZONTAL', 'A_LEFT', 'A_LOW', 'A_RIGHT', 'A_TOP', 'A_VERTICAL', ]: if hasattr(lib, name): _copy_to_globals(name) if lib._m_NCURSES_MOUSE_VERSION: for name in ["BUTTON1_PRESSED", "BUTTON1_RELEASED", "BUTTON1_CLICKED", "BUTTON1_DOUBLE_CLICKED", "BUTTON1_TRIPLE_CLICKED", "BUTTON2_PRESSED", "BUTTON2_RELEASED", "BUTTON2_CLICKED", "BUTTON2_DOUBLE_CLICKED", "BUTTON2_TRIPLE_CLICKED", "BUTTON3_PRESSED", "BUTTON3_RELEASED", "BUTTON3_CLICKED", "BUTTON3_DOUBLE_CLICKED", "BUTTON3_TRIPLE_CLICKED", "BUTTON4_PRESSED", "BUTTON4_RELEASED", "BUTTON4_CLICKED", "BUTTON4_DOUBLE_CLICKED", "BUTTON4_TRIPLE_CLICKED", "BUTTON_SHIFT", "BUTTON_CTRL", "BUTTON_ALT", "ALL_MOUSE_EVENTS", "REPORT_MOUSE_POSITION", ]: _copy_to_globals(name) if not lib._m_NetBSD: for key in range(lib.KEY_MIN, lib.KEY_MAX): key_n = lib.keyname(key) if key_n == ffi.NULL: continue key_n = ffi.string(key_n) if key_n == b"UNKNOWN KEY": continue if not isinstance(key_n, str): # python 3 key_n = key_n.decode() key_n = key_n.replace('(', '').replace(')', '') globals()[key_n] = key _setup() # Do we want this? # version = "2.2" # __version__ = "2.2" # ____________________________________________________________ _initialised_setupterm = False _initialised = False _initialised_color = False def _ensure_initialised_setupterm(): if not _initialised_setupterm: raise error("must call (at least) setupterm() first") def _ensure_initialised(): if not _initialised: raise error("must call initscr() first") def _ensure_initialised_color(): if not _initialised and _initialised_color: raise error("must call start_color() first") def _check_ERR(code, fname): if code != lib.ERR: return None elif fname is None: raise error("curses function returned ERR") else: raise error("%s() returned ERR" % (fname,)) def _check_NULL(rval): if rval == ffi.NULL: raise error("curses function returned NULL") return rval def _call_lib(method_name, *args): return getattr(lib, method_name)(*args) def _call_lib_check_ERR(method_name, *args): return _check_ERR(_call_lib(method_name, *args), method_name) def _mk_no_return(method_name): def _execute(): _ensure_initialised() return _call_lib_check_ERR(method_name) _execute.__name__ = method_name return _execute def _mk_flag_func(method_name): # This is in the CPython implementation, but not documented anywhere. # We have to support it, though, even if it make me sad. def _execute(flag=True): _ensure_initialised() if flag: return _call_lib_check_ERR(method_name) else: return _call_lib_check_ERR('no' + method_name) _execute.__name__ = method_name return _execute def _mk_return_val(method_name): def _execute(): return _call_lib(method_name) _execute.__name__ = method_name return _execute def _mk_w_getyx(method_name): def _execute(self): y = _call_lib(method_name + 'y', self._win) x = _call_lib(method_name + 'x', self._win) return (y, x) _execute.__name__ = method_name return _execute def _mk_w_no_return(method_name): def _execute(self, *args): return _call_lib_check_ERR(method_name, self._win, *args) _execute.__name__ = method_name return _execute def _mk_w_return_val(method_name): def _execute(self, *args): return _call_lib(method_name, self._win, *args) _execute.__name__ = method_name return _execute def _chtype(ch): return int(ffi.cast("chtype", ch)) def _texttype(text): if isinstance(text, str): return text elif isinstance(text, unicode): return str(text) # default encoding else: raise TypeError("str or unicode expected, got a '%s' object" % (type(text).__name__,)) def _extract_yx(args): if len(args) >= 2: return (args[0], args[1], args[2:]) return (None, None, args) def _process_args(funcname, args, count, optcount, frontopt=0): outargs = [] if frontopt: if len(args) > count + optcount: # We have the front optional args here. outargs.extend(args[:frontopt]) args = args[frontopt:] else: # No front optional args, so make them None. outargs.extend([None] * frontopt) if (len(args) < count) or (len(args) > count + optcount): raise error("%s requires %s to %s arguments" % ( funcname, count, count + optcount + frontopt)) outargs.extend(args) return outargs def _argspec(count, optcount=0, frontopt=0): def _argspec_deco(func): @wraps(func) def _wrapped(self, *args): outargs = _process_args( func.__name__, args, count, optcount, frontopt) return func(self, *outargs) return _wrapped return _argspec_deco # ____________________________________________________________ class error(Exception): pass class Window(object): def __init__(self, window): self._win = window def __del__(self): if self._win != lib.stdscr: lib.delwin(self._win) untouchwin = _mk_w_no_return("untouchwin") touchwin = _mk_w_no_return("touchwin") redrawwin = _mk_w_no_return("redrawwin") insertln = _mk_w_no_return("winsertln") erase = _mk_w_no_return("werase") deleteln = _mk_w_no_return("wdeleteln") is_wintouched = _mk_w_return_val("is_wintouched") syncdown = _mk_w_return_val("wsyncdown") syncup = _mk_w_return_val("wsyncup") standend = _mk_w_return_val("wstandend") standout = _mk_w_return_val("wstandout") cursyncup = _mk_w_return_val("wcursyncup") clrtoeol = _mk_w_return_val("wclrtoeol") clrtobot = _mk_w_return_val("wclrtobot") clear = _mk_w_return_val("wclear") idcok = _mk_w_no_return("idcok") immedok = _mk_w_no_return("immedok") timeout = _mk_w_no_return("wtimeout") getyx = _mk_w_getyx("getcur") getbegyx = _mk_w_getyx("getbeg") getmaxyx = _mk_w_getyx("getmax") getparyx = _mk_w_getyx("getpar") clearok = _mk_w_no_return("clearok") idlok = _mk_w_no_return("idlok") leaveok = _mk_w_no_return("leaveok") notimeout = _mk_w_no_return("notimeout") scrollok = _mk_w_no_return("scrollok") insdelln = _mk_w_no_return("winsdelln") syncok = _mk_w_no_return("syncok") mvwin = _mk_w_no_return("mvwin") mvderwin = _mk_w_no_return("mvderwin") move = _mk_w_no_return("wmove") if not lib._m_STRICT_SYSV_CURSES: resize = _mk_w_no_return("wresize") if lib._m_NetBSD: keypad = _mk_w_return_val("keypad") nodelay = _mk_w_return_val("nodelay") else: keypad = _mk_w_no_return("keypad") nodelay = _mk_w_no_return("nodelay") @_argspec(1, 1, 2) def addch(self, y, x, ch, attr=None): if attr is None: attr = lib.A_NORMAL ch = _chtype(ch) if y is not None: code = lib.mvwaddch(self._win, y, x, ch | attr) else: code = lib.waddch(self._win, ch | attr) return _check_ERR(code, "addch") @_argspec(1, 1, 2) def addstr(self, y, x, text, attr=None): text = _texttype(text) if attr is not None: attr_old = lib.getattrs(self._win) lib.wattrset(self._win, attr) if y is not None: code = lib.mvwaddstr(self._win, y, x, text) else: code = lib.waddstr(self._win, text) if attr is not None: lib.wattrset(self._win, attr_old) return _check_ERR(code, "addstr") @_argspec(2, 1, 2) def addnstr(self, y, x, text, n, attr=None): text = _texttype(text) if attr is not None: attr_old = lib.getattrs(self._win) lib.wattrset(self._win, attr) if y is not None: code = lib.mvwaddnstr(self._win, y, x, text, n) else: code = lib.waddnstr(self._win, text, n) if attr is not None: lib.wattrset(self._win, attr_old) return _check_ERR(code, "addnstr") def bkgd(self, ch, attr=None): if attr is None: attr = lib.A_NORMAL return _check_ERR(lib.wbkgd(self._win, _chtype(ch) | attr), "bkgd") attroff = _mk_w_no_return("wattroff") attron = _mk_w_no_return("wattron") attrset = _mk_w_no_return("wattrset") def bkgdset(self, ch, attr=None): if attr is None: attr = lib.A_NORMAL lib.wbkgdset(self._win, _chtype(ch) | attr) return None def border(self, ls=0, rs=0, ts=0, bs=0, tl=0, tr=0, bl=0, br=0): lib.wborder(self._win, _chtype(ls), _chtype(rs), _chtype(ts), _chtype(bs), _chtype(tl), _chtype(tr), _chtype(bl), _chtype(br)) return None def box(self, vertint=0, horint=0): lib.box(self._win, vertint, horint) return None @_argspec(1, 1, 2) def chgat(self, y, x, num, attr=None): # These optional args are in a weird order. if attr is None: attr = num num = -1 color = ((attr >> 8) & 0xff) attr = attr - (color << 8) if y is not None: code = lib.mvwchgat(self._win, y, x, num, attr, color, ffi.NULL) lib.touchline(self._win, y, 1) else: yy, _ = self.getyx() code = lib.wchgat(self._win, num, attr, color, ffi.NULL) lib.touchline(self._win, yy, 1) return _check_ERR(code, "chgat") def delch(self, *args): if len(args) == 0: code = lib.wdelch(self._win) elif len(args) == 2: code = lib.mvwdelch(self._win, *args) else: raise error("delch requires 0 or 2 arguments") return _check_ERR(code, "[mv]wdelch") def derwin(self, *args): nlines = 0 ncols = 0 if len(args) == 2: begin_y, begin_x = args elif len(args) == 4: nlines, ncols, begin_y, begin_x = args else: raise error("derwin requires 2 or 4 arguments") win = lib.derwin(self._win, nlines, ncols, begin_y, begin_x) return Window(_check_NULL(win)) def echochar(self, ch, attr=None): if attr is None: attr = lib.A_NORMAL ch = _chtype(ch) if lib._m_ispad(self._win): code = lib.pechochar(self._win, ch | attr) else: code = lib.wechochar(self._win, ch | attr) return _check_ERR(code, "echochar") if lib._m_NCURSES_MOUSE_VERSION: enclose = _mk_w_return_val("wenclose") getbkgd = _mk_w_return_val("getbkgd") def getch(self, *args): if len(args) == 0: val = lib.wgetch(self._win) elif len(args) == 2: val = lib.mvwgetch(self._win, *args) else: raise error("getch requires 0 or 2 arguments") return val def getkey(self, *args): if len(args) == 0: val = lib.wgetch(self._win) elif len(args) == 2: val = lib.mvwgetch(self._win, *args) else: raise error("getkey requires 0 or 2 arguments") if val == lib.ERR: raise error("no input") elif val <= 255: return chr(val) else: # XXX: The following line is different if `__NetBSD__` is defined. val = lib.keyname(val) if val == ffi.NULL: return "" return ffi.string(val) @_argspec(0, 1, 2) def getstr(self, y, x, n=1023): n = min(n, 1023) buf = ffi.new("char[1024]") # /* This should be big enough.. I hope */ if y is None: val = lib.wgetnstr(self._win, buf, n) else: val = lib.mvwgetnstr(self._win, y, x, buf, n) if val == lib.ERR: return "" return ffi.string(buf) @_argspec(2, 1, 2) def hline(self, y, x, ch, n, attr=None): ch = _chtype(ch) if attr is None: attr = lib.A_NORMAL if y is not None: _check_ERR(lib.wmove(self._win, y, x), "wmove") return _check_ERR(lib.whline(self._win, ch | attr, n), "hline") @_argspec(1, 1, 2) def insch(self, y, x, ch, attr=None): ch = _chtype(ch) if attr is None: attr = lib.A_NORMAL if y is not None: code = lib.mvwinsch(self._win, y, x, ch | attr) else: code = lib.winsch(self._win, ch | attr) return _check_ERR(code, "insch") def inch(self, *args): if len(args) == 0: return lib.winch(self._win) elif len(args) == 2: return lib.mvwinch(self._win, *args) else: raise error("inch requires 0 or 2 arguments") @_argspec(0, 1, 2) def instr(self, y, x, n=1023): n = min(n, 1023) buf = ffi.new("char[1024]") # /* This should be big enough.. I hope */ if y is None: code = lib.winnstr(self._win, buf, n) else: code = lib.mvwinnstr(self._win, y, x, buf, n) if code == lib.ERR: return "" return ffi.string(buf) @_argspec(1, 1, 2) def insstr(self, y, x, text, attr=None): text = _texttype(text) if attr is not None: attr_old = lib.getattrs(self._win) lib.wattrset(self._win, attr) if y is not None: code = lib.mvwinsstr(self._win, y, x, text) else: code = lib.winsstr(self._win, text) if attr is not None: lib.wattrset(self._win, attr_old) return _check_ERR(code, "insstr") @_argspec(2, 1, 2) def insnstr(self, y, x, text, n, attr=None): text = _texttype(text) if attr is not None: attr_old = lib.getattrs(self._win) lib.wattrset(self._win, attr) if y is not None: code = lib.mvwinsnstr(self._win, y, x, text, n) else: code = lib.winsnstr(self._win, text, n) if attr is not None: lib.wattrset(self._win, attr_old) return _check_ERR(code, "insnstr") def is_linetouched(self, line): code = lib.is_linetouched(self._win, line) if code == lib.ERR: raise error("is_linetouched: line number outside of boundaries") if code == lib.FALSE: return False return True def noutrefresh(self, *args): if lib._m_ispad(self._win): if len(args) != 6: raise error( "noutrefresh() called for a pad requires 6 arguments") return _check_ERR(lib.pnoutrefresh(self._win, *args), "pnoutrefresh") else: # XXX: Better args check here? We need zero args. return _check_ERR(lib.wnoutrefresh(self._win, *args), "wnoutrefresh") nooutrefresh = noutrefresh # "to be removed in 2.3", but in 2.7, 3.x. def _copywin(self, dstwin, overlay, sminr, sminc, dminr, dminc, dmaxr, dmaxc): return _check_ERR(lib.copywin(self._win, dstwin._win, sminr, sminc, dminr, dminc, dmaxr, dmaxc, overlay), "copywin") def overlay(self, dstwin, *args): if len(args) == 6: return self._copywin(dstwin, True, *args) elif len(args) == 0: return _check_ERR(lib.overlay(self._win, dstwin._win), "overlay") else: raise error("overlay requires one or seven arguments") def overwrite(self, dstwin, *args): if len(args) == 6: return self._copywin(dstwin, False, *args) elif len(args) == 0: return _check_ERR(lib.overwrite(self._win, dstwin._win), "overwrite") else: raise error("overwrite requires one or seven arguments") def putwin(self, filep): # filestar = ffi.new("FILE *", filep) return _check_ERR(lib.putwin(self._win, filep), "putwin") def redrawln(self, beg, num): return _check_ERR(lib.wredrawln(self._win, beg, num), "redrawln") def refresh(self, *args): if lib._m_ispad(self._win): if len(args) != 6: raise error( "noutrefresh() called for a pad requires 6 arguments") return _check_ERR(lib.prefresh(self._win, *args), "prefresh") else: # XXX: Better args check here? We need zero args. return _check_ERR(lib.wrefresh(self._win, *args), "wrefresh") def setscrreg(self, y, x): return _check_ERR(lib.wsetscrreg(self._win, y, x), "wsetscrreg") def subwin(self, *args): nlines = 0 ncols = 0 if len(args) == 2: begin_y, begin_x = args elif len(args) == 4: nlines, ncols, begin_y, begin_x = args else: raise error("subwin requires 2 or 4 arguments") if lib._m_ispad(self._win): win = lib.subpad(self._win, nlines, ncols, begin_y, begin_x) else: win = lib.subwin(self._win, nlines, ncols, begin_y, begin_x) return Window(_check_NULL(win)) def scroll(self, nlines=None): if nlines is None: return _check_ERR(lib.scroll(self._win), "scroll") else: return _check_ERR(lib.wscrl(self._win, nlines), "scroll") def touchline(self, st, cnt, val=None): if val is None: return _check_ERR(lib.touchline(self._win, st, cnt), "touchline") else: return _check_ERR(lib.wtouchln(self._win, st, cnt, val), "touchline") @_argspec(2, 1, 2) def vline(self, y, x, ch, n, attr=None): ch = _chtype(ch) if attr is None: attr = lib.A_NORMAL if y is not None: _check_ERR(lib.wmove(self._win, y, x), "wmove") return _check_ERR(lib.wvline(self._win, ch | attr, n), "vline") beep = _mk_no_return("beep") def_prog_mode = _mk_no_return("def_prog_mode") def_shell_mode = _mk_no_return("def_shell_mode") doupdate = _mk_no_return("doupdate") endwin = _mk_no_return("endwin") flash = _mk_no_return("flash") nocbreak = _mk_no_return("nocbreak") noecho = _mk_no_return("noecho") nonl = _mk_no_return("nonl") noraw = _mk_no_return("noraw") reset_prog_mode = _mk_no_return("reset_prog_mode") reset_shell_mode = _mk_no_return("reset_shell_mode") resetty = _mk_no_return("resetty") savetty = _mk_no_return("savetty") cbreak = _mk_flag_func("cbreak") echo = _mk_flag_func("echo") nl = _mk_flag_func("nl") raw = _mk_flag_func("raw") baudrate = _mk_return_val("baudrate") termattrs = _mk_return_val("termattrs") termname = _mk_return_val("termname") longname = _mk_return_val("longname") can_change_color = _mk_return_val("can_change_color") has_colors = _mk_return_val("has_colors") has_ic = _mk_return_val("has_ic") has_il = _mk_return_val("has_il") isendwin = _mk_return_val("isendwin") flushinp = _mk_return_val("flushinp") noqiflush = _mk_return_val("noqiflush") def filter(): lib.filter() return None def color_content(color): _ensure_initialised_color() r, g, b = ffi.new("short *"), ffi.new("short *"), ffi.new("short *") if lib.color_content(color, r, g, b) == lib.ERR: raise error("Argument 1 was out of range. Check value of COLORS.") return (r[0], g[0], b[0]) def color_pair(n): _ensure_initialised_color() return (n << 8) def curs_set(vis): _ensure_initialised() val = lib.curs_set(vis) _check_ERR(val, "curs_set") return val def delay_output(ms): _ensure_initialised() return _check_ERR(lib.delay_output(ms), "delay_output") def erasechar(): _ensure_initialised() return lib.erasechar() def getsyx(): _ensure_initialised() yx = ffi.new("int[2]") lib._m_getsyx(yx) return (yx[0], yx[1]) if lib._m_NCURSES_MOUSE_VERSION: def getmouse(): _ensure_initialised() mevent = ffi.new("MEVENT *") _check_ERR(lib.getmouse(mevent), "getmouse") return (mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate) def ungetmouse(id, x, y, z, bstate): _ensure_initialised() mevent = ffi.new("MEVENT *") mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate = ( id, x, y, z, bstate) return _check_ERR(lib.ungetmouse(mevent), "ungetmouse") def getwin(filep): return Window(_check_NULL(lib.getwin(filep))) def halfdelay(tenths): _ensure_initialised() return _check_ERR(lib.halfdelay(tenths), "halfdelay") if not lib._m_STRICT_SYSV_CURSES: def has_key(ch): _ensure_initialised() return lib.has_key(ch) def init_color(color, r, g, b): _ensure_initialised_color() return _check_ERR(lib.init_color(color, r, g, b), "init_color") def init_pair(pair, f, b): _ensure_initialised_color() return _check_ERR(lib.init_pair(pair, f, b), "init_pair") def _mk_acs(name, ichar): if len(ichar) == 1: globals()[name] = lib.acs_map[ord(ichar)] else: globals()[name] = globals()[ichar] def _map_acs(): _mk_acs("ACS_ULCORNER", 'l') _mk_acs("ACS_LLCORNER", 'm') _mk_acs("ACS_URCORNER", 'k') _mk_acs("ACS_LRCORNER", 'j') _mk_acs("ACS_LTEE", 't') _mk_acs("ACS_RTEE", 'u') _mk_acs("ACS_BTEE", 'v') _mk_acs("ACS_TTEE", 'w') _mk_acs("ACS_HLINE", 'q') _mk_acs("ACS_VLINE", 'x') _mk_acs("ACS_PLUS", 'n') _mk_acs("ACS_S1", 'o') _mk_acs("ACS_S9", 's') _mk_acs("ACS_DIAMOND", '`') _mk_acs("ACS_CKBOARD", 'a') _mk_acs("ACS_DEGREE", 'f') _mk_acs("ACS_PLMINUS", 'g') _mk_acs("ACS_BULLET", '~') _mk_acs("ACS_LARROW", ',') _mk_acs("ACS_RARROW", '+') _mk_acs("ACS_DARROW", '.') _mk_acs("ACS_UARROW", '-') _mk_acs("ACS_BOARD", 'h') _mk_acs("ACS_LANTERN", 'i') _mk_acs("ACS_BLOCK", '0') _mk_acs("ACS_S3", 'p') _mk_acs("ACS_S7", 'r') _mk_acs("ACS_LEQUAL", 'y') _mk_acs("ACS_GEQUAL", 'z') _mk_acs("ACS_PI", '{') _mk_acs("ACS_NEQUAL", '|') _mk_acs("ACS_STERLING", '}') _mk_acs("ACS_BSSB", "ACS_ULCORNER") _mk_acs("ACS_SSBB", "ACS_LLCORNER") _mk_acs("ACS_BBSS", "ACS_URCORNER") _mk_acs("ACS_SBBS", "ACS_LRCORNER") _mk_acs("ACS_SBSS", "ACS_RTEE") _mk_acs("ACS_SSSB", "ACS_LTEE") _mk_acs("ACS_SSBS", "ACS_BTEE") _mk_acs("ACS_BSSS", "ACS_TTEE") _mk_acs("ACS_BSBS", "ACS_HLINE") _mk_acs("ACS_SBSB", "ACS_VLINE") _mk_acs("ACS_SSSS", "ACS_PLUS") def initscr(): if _initialised: lib.wrefresh(lib.stdscr) return Window(lib.stdscr) win = _check_NULL(lib.initscr()) globals()['_initialised_setupterm'] = True globals()['_initialised'] = True _map_acs() globals()["LINES"] = lib.LINES globals()["COLS"] = lib.COLS return Window(win) def setupterm(term=None, fd=-1): if fd == -1: # XXX: Check for missing stdout here? fd = sys.stdout.fileno() if _initialised_setupterm: return None if term is None: term = ffi.NULL err = ffi.new("int *") if lib.setupterm(term, fd, err) == lib.ERR: err = err[0] if err == 0: raise error("setupterm: could not find terminal") elif err == -1: raise error("setupterm: could not find terminfo database") else: raise error("setupterm: unknown error") globals()["_initialised_setupterm"] = True return None def intrflush(ch): _ensure_initialised() return _check_ERR(lib.intrflush(ffi.NULL, ch), "intrflush") # XXX: #ifdef HAVE_CURSES_IS_TERM_RESIZED def is_term_resized(lines, columns): _ensure_initialised() return lib.is_term_resized(lines, columns) if not lib._m_NetBSD: def keyname(ch): _ensure_initialised() if ch < 0: raise error("invalid key number") knp = lib.keyname(ch) if knp == ffi.NULL: return "" return ffi.string(knp) def killchar(): return lib.killchar() def meta(ch): return _check_ERR(lib.meta(lib.stdscr, ch), "meta") if lib._m_NCURSES_MOUSE_VERSION: def mouseinterval(interval): _ensure_initialised() return _check_ERR(lib.mouseinterval(interval), "mouseinterval") def mousemask(newmask): _ensure_initialised() oldmask = ffi.new("mmask_t *") availmask = lib.mousemask(newmask, oldmask) return (availmask, oldmask) def napms(ms): _ensure_initialised() return lib.napms(ms) def newpad(nlines, ncols): _ensure_initialised() return Window(_check_NULL(lib.newpad(nlines, ncols))) def newwin(nlines, ncols, begin_y=None, begin_x=None): _ensure_initialised() if begin_x is None: if begin_y is not None: raise error("newwin requires 2 or 4 arguments") begin_y = begin_x = 0 return Window(_check_NULL(lib.newwin(nlines, ncols, begin_y, begin_x))) def pair_content(pair): _ensure_initialised_color() f = ffi.new("short *") b = ffi.new("short *") if lib.pair_content(pair, f, b) == lib.ERR: raise error("Argument 1 was out of range. (1..COLOR_PAIRS-1)") return (f, b) def pair_number(pairvalue): _ensure_initialised_color() return (pairvalue & lib.A_COLOR) >> 8 def putp(text): text = _texttype(text) return _check_ERR(lib.putp(text), "putp") def qiflush(flag=True): _ensure_initialised() if flag: lib.qiflush() else: lib.noqiflush() return None # XXX: Do something about the following? # /* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES # * and _curses.COLS */ # #if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) # static int # update_lines_cols(void) # { # PyObject *o; # PyObject *m = PyImport_ImportModuleNoBlock("curses"); # if (!m) # return 0; # o = PyInt_FromLong(LINES); # if (!o) { # Py_DECREF(m); # return 0; # } # if (PyObject_SetAttrString(m, "LINES", o)) { # Py_DECREF(m); # Py_DECREF(o); # return 0; # } # if (PyDict_SetItemString(ModDict, "LINES", o)) { # Py_DECREF(m); # Py_DECREF(o); # return 0; # } # Py_DECREF(o); # o = PyInt_FromLong(COLS); # if (!o) { # Py_DECREF(m); # return 0; # } # if (PyObject_SetAttrString(m, "COLS", o)) { # Py_DECREF(m); # Py_DECREF(o); # return 0; # } # if (PyDict_SetItemString(ModDict, "COLS", o)) { # Py_DECREF(m); # Py_DECREF(o); # return 0; # } # Py_DECREF(o); # Py_DECREF(m); # return 1; # } # #endif # #ifdef HAVE_CURSES_RESIZETERM # static PyObject * # PyCurses_ResizeTerm(PyObject *self, PyObject *args) # { # int lines; # int columns; # PyObject *result; # PyCursesInitialised; # if (!PyArg_ParseTuple(args,"ii:resizeterm", &lines, &columns)) # return NULL; # result = PyCursesCheckERR(resizeterm(lines, columns), "resizeterm"); # if (!result) # return NULL; # if (!update_lines_cols()) # return NULL; # return result; # } # #endif # #ifdef HAVE_CURSES_RESIZE_TERM # static PyObject * # PyCurses_Resize_Term(PyObject *self, PyObject *args) # { # int lines; # int columns; # PyObject *result; # PyCursesInitialised; # if (!PyArg_ParseTuple(args,"ii:resize_term", &lines, &columns)) # return NULL; # result = PyCursesCheckERR(resize_term(lines, columns), "resize_term"); # if (!result) # return NULL; # if (!update_lines_cols()) # return NULL; # return result; # } # #endif /* HAVE_CURSES_RESIZE_TERM */ def setsyx(y, x): _ensure_initialised() lib.setsyx(y, x) return None def start_color(): _check_ERR(lib.start_color(), "start_color") globals()["COLORS"] = lib.COLORS globals()["COLOR_PAIRS"] = lib.COLOR_PAIRS globals()["_initialised_color"] = True return None def tigetflag(capname): _ensure_initialised_setupterm() return lib.tigetflag(capname) def tigetnum(capname): _ensure_initialised_setupterm() return lib.tigetnum(capname) def tigetstr(capname): _ensure_initialised_setupterm() val = lib.tigetstr(capname) if int(ffi.cast("intptr_t", val)) in (0, -1): return None return ffi.string(val) def tparm(fmt, i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0): args = [ffi.cast("int", i) for i in (i1, i2, i3, i4, i5, i6, i7, i8, i9)] result = lib.tparm(fmt, *args) if result == ffi.NULL: raise error("tparm() returned NULL") return ffi.string(result) def typeahead(fd): _ensure_initialised() return _check_ERR(lib.typeahead(fd), "typeahead") def unctrl(ch): _ensure_initialised() return lib.unctrl(_chtype(ch)) def ungetch(ch): _ensure_initialised() return _check_ERR(lib.ungetch(_chtype(ch)), "ungetch") def use_env(flag): lib.use_env(flag) return None if not lib._m_STRICT_SYSV_CURSES: def use_default_colors(): _ensure_initialised_color() return _check_ERR(lib.use_default_colors(), "use_default_colors") cffi-1.5.2/demo/bsdopendirtype.py0000664000175000017500000000233012657646311017216 0ustar arigoarigo00000000000000from _bsdopendirtype import ffi, lib 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 = b'.' dirname = dir if not dirname.endswith(b'/'): dirname += b'/' 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 == b'.' or name == b'..': 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) if __name__ == '__main__': for name, smode in opendir(b'/tmp'): print(hex(smode), name) cffi-1.5.2/demo/cffi-cocoa.py0000664000175000017500000000643212657646311016163 0ustar arigoarigo00000000000000# 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-1.5.2/demo/manual.c0000664000175000017500000000670112657646311015240 0ustar arigoarigo00000000000000#include "_cffi_include.h" #define AA (42) #define BB (&bb) static int bb = 16261; int foo42(int a, int *b) { return a - *b; } int foo64(int a) { return ~a; } struct foo_s { int a; }; /************************************************************/ static void *_cffi_types[] = { _CFFI_OP(_CFFI_OP_FUNCTION, 1), _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT), _CFFI_OP(_CFFI_OP_POINTER, 1), _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), _CFFI_OP(_CFFI_OP_FUNCTION, 1), _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT), _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), _CFFI_OP(_CFFI_OP_STRUCT_UNION, 0), }; #ifndef PYPY_VERSION static PyObject * _cffi_f_foo42(PyObject *self, PyObject *args) { int x0; int * x1; Py_ssize_t datasize; int result; PyObject *arg0; PyObject *arg1; if (!PyArg_ParseTuple(args, "OO:foo42", &arg0, &arg1)) return NULL; x0 = _cffi_to_c_int(arg0, int); if (x0 == (int)-1 && PyErr_Occurred()) return NULL; datasize = _cffi_prepare_pointer_call_argument( _cffi_types[1], arg1, (char **)&x1); if (datasize != 0) { if (datasize < 0) return NULL; x1 = alloca(datasize); memset((void *)x1, 0, datasize); if (_cffi_convert_array_from_object((char *)x1, _cffi_types[1], arg1) < 0) return NULL; } Py_BEGIN_ALLOW_THREADS _cffi_restore_errno(); { result = foo42(x0, x1); } _cffi_save_errno(); Py_END_ALLOW_THREADS return _cffi_from_c_int(result, int); } #else static int _cffi_f_foo42(int x0, int *x1) { return foo42(x0, x1); } #endif #ifndef PYPY_VERSION static PyObject * _cffi_f_foo64(PyObject *self, PyObject *arg0) { int x0; int result; x0 = _cffi_to_c_int(arg0, int); if (x0 == (int)-1 && PyErr_Occurred()) return NULL; Py_BEGIN_ALLOW_THREADS _cffi_restore_errno(); { result = foo64(x0); } _cffi_save_errno(); Py_END_ALLOW_THREADS return _cffi_from_c_int(result, int); } #else static int _cffi_f_foo64(int x0) { return foo64(x0); } #endif static int _cffi_const_AA(unsigned long long *output) { *output = (unsigned long long)((AA) << 0); // integer return (AA) <= 0; } static void _cffi_const_BB(char *output) { *(int **)output = BB; } static const struct _cffi_global_s _cffi_globals[] = { { "AA", &_cffi_const_AA, _CFFI_OP(_CFFI_OP_CONSTANT_INT, 0) }, { "BB", &_cffi_const_BB, _CFFI_OP(_CFFI_OP_CONSTANT, 2) }, { "bb", &bb, _CFFI_OP(_CFFI_OP_GLOBAL_VAR, 1) }, { "foo42", &_cffi_f_foo42, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 0) }, { "foo64", &_cffi_f_foo64, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 4) }, }; struct _cffi_align_foo_s { char x; struct foo_s y; }; static const struct _cffi_struct_union_s _cffi_struct_unions[] = { { "foo_s", 7, 0, sizeof(struct foo_s), offsetof(struct _cffi_align_foo_s, y), 1, 0 }, }; static const struct _cffi_field_s _cffi_fields[] = { { "a", offsetof(struct foo_s, a), sizeof(((struct foo_s *)0)->a), _CFFI_OP(_CFFI_OP_NOOP, 1) }, }; static const struct _cffi_type_context_s _cffi_type_context = { _cffi_types, _cffi_globals, _cffi_fields, _cffi_struct_unions, NULL, NULL, 5, /* num_globals */ 1, /* num_struct_unions */ 0, 0, NULL, 8, /* num_types */ }; #ifndef PYPY_VERSION PyMODINIT_FUNC initmanual(void) { _cffi_init("manual", 0x2601, &_cffi_type_context); } #else PyMODINIT_FUNC _cffi_pypyinit_manual(const void *p[]) { p[0] = (const void *)0x2601; p[1] = &_cffi_type_context; } #endif cffi-1.5.2/cffi/0000775000175000017500000000000012657646372013605 5ustar arigoarigo00000000000000cffi-1.5.2/cffi/vengine_cpy.py0000664000175000017500000012063212657646311016462 0ustar arigoarigo00000000000000# # DEPRECATED: implementation for ffi.verify() # import 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 = ['((void)lib,0)', '((void)lib,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, NULL},') prnt(' {NULL, NULL, 0, NULL} /* Sentinel */') prnt('};') prnt() # # standard init. modname = self.verifier.get_module_name() constants = self._chained_list_constants[False] prnt('#if PY_MAJOR_VERSION >= 3') prnt() 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() prnt('PyMODINIT_FUNC') prnt('PyInit_%s(void)' % modname) prnt('{') prnt(' PyObject *lib;') prnt(' lib = PyModule_Create(&_cffi_module_def);') prnt(' if (lib == NULL)') prnt(' return NULL;') prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,)) prnt(' Py_DECREF(lib);') prnt(' return NULL;') prnt(' }') prnt(' return lib;') prnt('}') prnt() prnt('#else') prnt() prnt('PyMODINIT_FUNC') prnt('init%s(void)' % modname) prnt('{') prnt(' PyObject *lib;') prnt(' lib = Py_InitModule("%s", _cffi_methods);' % modname) prnt(' if (lib == NULL)') prnt(' return;') prnt(' if (%s < 0 || _cffi_init() < 0)' % (constants,)) prnt(' return;') prnt(' return;') prnt('}') prnt() prnt('#endif') def load_library(self, flags=None): # XXX review all usages of 'self' here! # import it as a new extension module imp.acquire_lock() try: if hasattr(sys, "getdlopenflags"): previous_flags = sys.getdlopenflags() try: if hasattr(sys, "setdlopenflags") and flags is not None: sys.setdlopenflags(flags) 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) finally: if hasattr(sys, "setdlopenflags"): sys.setdlopenflags(previous_flags) finally: imp.release_lock() # # 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): lst = [(key, tp) for (key, (tp, qual)) in self.ffi._parser._declarations.items()] lst.sort() return lst 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 = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), 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((size_t)datasize);' % (tovar,)) self._prnt(' memset((void *)%s, 0, (size_t)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 = 'noarg' 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() # prnt(' (void)self; /* unused */') if numargs == 0: prnt(' (void)noarg; /* unused */') 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, NULL},' % (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 */') prnt(' (void)p;') for fname, ftype, fbitsize, fqual 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, quals=fqual), 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, fqual 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(' (void)self; /* unused */') prnt(' (void)noarg; /* unused */') 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, NULL},' % (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, fqual 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, check_value=None): 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 check_value is not None: self._check_int_constant_value(name, check_value) # 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(' o = _cffi_from_c_int_const(%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 _check_int_constant_value(self, name, value, err_prefix=''): prnt = self._prnt if value <= 0: prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( name, name, value)) else: prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( name, name, value)) prnt(' char buf[64];') prnt(' if ((%s) <= 0)' % name) prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name) prnt(' else') prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % name) prnt(' PyErr_Format(_cffi_VerificationError,') prnt(' "%s%s has the real value %s, not %s",') prnt(' "%s", "%s", buf, "%d");' % ( err_prefix, name, value)) prnt(' return -1;') prnt(' }') def _enum_funcname(self, prefix, name): # "$enum_$1" => "___D_enum____D_1" name = name.replace('$', '___D_') return '_cffi_e_%s_%s' % (prefix, name) def _generate_cpy_enum_decl(self, tp, name, prefix='enum'): if tp.partial: for enumerator in tp.enumerators: self._generate_cpy_const(True, enumerator, delayed=False) return # funcname = self._enum_funcname(prefix, name) prnt = self._prnt prnt('static int %s(PyObject *lib)' % funcname) prnt('{') for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): self._check_int_constant_value(enumerator, enumvalue, "enum %s: " % name) 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): if tp == '...': check_value = None else: check_value = tp # an integer self._generate_cpy_const(True, name, check_value=check_value) _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 /* this block of #ifs should be kept exactly identical between c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py */ #if defined(_MSC_VER) # include /* for alloca() */ # if _MSC_VER < 1600 /* MSVC < 2010 */ 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 __int8 int_least8_t; typedef __int16 int_least16_t; typedef __int32 int_least32_t; typedef __int64 int_least64_t; typedef unsigned __int8 uint_least8_t; typedef unsigned __int16 uint_least16_t; typedef unsigned __int32 uint_least32_t; typedef unsigned __int64 uint_least64_t; typedef __int8 int_fast8_t; typedef __int16 int_fast16_t; typedef __int32 int_fast32_t; typedef __int64 int_fast64_t; typedef unsigned __int8 uint_fast8_t; typedef unsigned __int16 uint_fast16_t; typedef unsigned __int32 uint_fast32_t; typedef unsigned __int64 uint_fast64_t; typedef __int64 intmax_t; typedef unsigned __int64 uintmax_t; # else # include # endif # if _MSC_VER < 1800 /* MSVC < 2013 */ typedef unsigned char _Bool; # endif #else # include # if (defined (__SVR4) && defined (__sun)) || defined(_AIX) # include # endif #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_const(x) \ (((x) > 0) ? \ ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \ PyInt_FromLong((long)(x)) : \ PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \ ((long long)(x) >= (long long)LONG_MIN) ? \ PyInt_FromLong((long)(x)) : \ PyLong_FromLongLong((long long)(x))) #define _cffi_from_c_int(x, type) \ (((type)-1) > 0 ? /* unsigned */ \ (sizeof(type) < sizeof(long) ? \ PyInt_FromLong((long)x) : \ sizeof(type) == sizeof(long) ? \ PyLong_FromUnsignedLong((unsigned long)x) : \ PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ (sizeof(type) <= sizeof(long) ? \ PyInt_FromLong((long)x) : \ PyLong_FromLongLong((long long)x))) #define _cffi_to_c_int(o, type) \ ((type)( \ sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ : (type)_cffi_to_c_i8(o)) : \ sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ : (type)_cffi_to_c_i16(o)) : \ sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ : (type)_cffi_to_c_i32(o)) : \ sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ : (type)_cffi_to_c_i64(o)) : \ (Py_FatalError("unsupported size for type " #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); (void)self; /* unused */ 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 int _cffi_init(void) { PyObject *module, *c_api_object = NULL; module = PyImport_ImportModule("_cffi_backend"); if (module == NULL) goto failure; c_api_object = PyObject_GetAttrString(module, "_C_API"); if (c_api_object == NULL) goto failure; if (!PyCapsule_CheckExact(c_api_object)) { PyErr_SetNone(PyExc_ImportError); goto failure; } memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), _CFFI_NUM_EXPORTS * sizeof(void *)); Py_DECREF(module); Py_DECREF(c_api_object); return 0; failure: Py_XDECREF(module); Py_XDECREF(c_api_object); return -1; } #define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) /**********/ ''' cffi-1.5.2/cffi/model.py0000664000175000017500000005116612657646311015261 0ustar arigoarigo00000000000000import types, sys import weakref from .lock import allocate_lock # type qualifiers Q_CONST = 0x01 Q_RESTRICT = 0x02 Q_VOLATILE = 0x04 def qualify(quals, replace_with): if quals & Q_CONST: replace_with = ' const ' + replace_with.lstrip() if quals & Q_VOLATILE: replace_with = ' volatile ' + replace_with.lstrip() if quals & Q_RESTRICT: # It seems that __restrict is supported by gcc and msvc. # If you hit some different compiler, add a #define in # _cffi_include.h for it (and in its copies, documented there) replace_with = ' __restrict ' + replace_with.lstrip() return replace_with class BaseTypeByIdentity(object): is_array_type = False is_raw_function = False def get_c_name(self, replace_with='', context='a C file', quals=0): 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 replace_with = qualify(quals, 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 is_integer_type(self): return False 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 BasePrimitiveType(BaseType): pass class PrimitiveType(BasePrimitiveType): _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', 'int_least8_t': 'i', 'uint_least8_t': 'i', 'int_least16_t': 'i', 'uint_least16_t': 'i', 'int_least32_t': 'i', 'uint_least32_t': 'i', 'int_least64_t': 'i', 'uint_least64_t': 'i', 'int_fast8_t': 'i', 'uint_fast8_t': 'i', 'int_fast16_t': 'i', 'uint_fast16_t': 'i', 'int_fast32_t': 'i', 'uint_fast32_t': 'i', 'int_fast64_t': 'i', 'uint_fast64_t': 'i', 'intptr_t': 'i', 'uintptr_t': 'i', 'intmax_t': 'i', 'uintmax_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 UnknownIntegerType(BasePrimitiveType): _attrs_ = ('name',) def __init__(self, name): self.name = name self.c_name_with_marker = name + '&' def is_integer_type(self): return True def build_backend_type(self, ffi, finishlist): raise NotImplementedError("integer type '%s' can only be used after " "compilation" % self.name) class UnknownFloatType(BasePrimitiveType): _attrs_ = ('name', ) def __init__(self, name): self.name = name self.c_name_with_marker = name + '&' def build_backend_type(self, ffi, finishlist): raise NotImplementedError("float type '%s' can only be used after " "compilation" % self.name) class BaseFunctionType(BaseType): _attrs_ = ('args', 'result', 'ellipsis', 'abi') def __init__(self, args, result, ellipsis, abi=None): self.args = args self.result = result self.ellipsis = ellipsis self.abi = abi # 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),) if abi is not None: replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] 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, self.abi) 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)) abi_args = () if self.abi == "__stdcall": if not self.ellipsis: # __stdcall ignored for variadic funcs try: abi_args = (ffi._backend.FFI_STDCALL,) except AttributeError: pass return global_cache(self, ffi, 'new_function_type', tuple(args), result, self.ellipsis, *abi_args) def as_raw_function(self): return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) class PointerType(BaseType): _attrs_ = ('totype', 'quals') def __init__(self, totype, quals=0): self.totype = totype self.quals = quals extra = qualify(quals, " *&") if totype.is_array_type: extra = "(%s)" % (extra.lstrip(),) 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) def ConstPointerType(totype): return PointerType(totype, Q_CONST) const_voidp_type = ConstPointerType(void_type) class NamedPointerType(PointerType): _attrs_ = ('totype', 'name') def __init__(self, totype, name, quals=0): PointerType.__init__(self, totype, quals) 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 = '&[%s]' % 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) char_array_type = ArrayType(PrimitiveType('char'), None) 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 = 0 partial = False packed = False def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None): self.name = name self.fldnames = fldnames self.fldtypes = fldtypes self.fldbitsize = fldbitsize self.fldquals = fldquals self.build_c_name_with_marker() def has_anonymous_struct_fields(self): if self.fldtypes is None: return False for name, type in zip(self.fldnames, self.fldtypes): if name == '' and isinstance(type, StructOrUnion): return True return False def enumfields(self): fldquals = self.fldquals if fldquals is None: fldquals = (0,) * len(self.fldnames) for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes, self.fldbitsize, fldquals): if name == '' and isinstance(type, StructOrUnion): # nested anonymous struct/union for result in type.enumfields(): yield result else: yield (name, type, bitsize, quals) 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 = [] fldquals = [] for name, type, bitsize, quals in self.enumfields(): names.append(name) types.append(type) bitsizes.append(bitsize) fldquals.append(quals) self.fldnames = tuple(names) self.fldtypes = tuple(types) self.fldbitsize = tuple(bitsizes) self.fldquals = tuple(fldquals) 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] # self.completed = 1 # if self.fldtypes is None: pass # not completing it: it's an opaque struct # elif 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) # from . import api if self.enumvalues: smallest_value = min(self.enumvalues) largest_value = max(self.enumvalues) else: import warnings warnings.warn("%r has no values explicitly defined; next version " "will refuse to guess which integer type it is " "meant to be (unsigned/signed, int/long)" % self._get_c_name()) smallest_value = 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) tp.origin = "unknown_type" 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("%s: %r: %s" % (funcname, 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-1.5.2/cffi/commontypes.py0000664000175000017500000000474312657646311016535 0ustar arigoarigo00000000000000import sys from . import api, model COMMON_TYPES = {} try: # fetch "bool" and all simple Windows types from _cffi_backend import _get_common_types _get_common_types(COMMON_TYPES) except ImportError: pass COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE') COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES: if _type.endswith('_t'): COMMON_TYPES[_type] = _type del _type _CACHE = {} def resolve_common_type(parser, commontype): try: return _CACHE[commontype] except KeyError: cdecl = COMMON_TYPES.get(commontype, commontype) if not isinstance(cdecl, str): result, quals = cdecl, 0 # cdecl is already a BaseType elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES: result, quals = model.PrimitiveType(cdecl), 0 elif cdecl == 'set-unicode-needed': raise api.FFIError("The Windows type %r is only available after " "you call ffi.set_unicode()" % (commontype,)) else: if commontype == cdecl: raise api.FFIError("Unsupported type: %r. Please file a bug " "if you think it should be." % (commontype,)) result, quals = parser.parse_type_and_quals(cdecl) # recursive assert isinstance(result, model.BaseTypeByIdentity) _CACHE[commontype] = result, quals return result, quals # ____________________________________________________________ # extra types for Windows (most of them are in commontypes.c) def win_common_types(): return { "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 *", "TBYTE": "set-unicode-needed", "TCHAR": "set-unicode-needed", "LPCTSTR": "set-unicode-needed", "PCTSTR": "set-unicode-needed", "LPTSTR": "set-unicode-needed", "PTSTR": "set-unicode-needed", "PTBYTE": "set-unicode-needed", "PTCHAR": "set-unicode-needed", } if sys.platform == 'win32': COMMON_TYPES.update(win_common_types()) cffi-1.5.2/cffi/backend_ctypes.py0000664000175000017500000011630212657646311017131 0ustar arigoarigo00000000000000import ctypes, ctypes.util, operator, sys from . import model if sys.version_info < (3,): bytechr = chr else: unicode = str long = 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 kind = "pointer" @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, (int, long)): 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, (int, long, 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 # kind1 = kind class CTypesPrimitive(CTypesGenericPrimitive): __slots__ = ['_value'] _ctype = ctype _reftypename = '%s &' % name kind = kind1 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, (int, long, 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, (int, long)): 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, (int, long, 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, (int, long)): return self._new_pointer_at(self._address + other * self._bitem_size) else: return NotImplemented def __sub__(self, other): if isinstance(other, (int, long)): 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, (int, long)): 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, (int, long)): 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) kind1 = kind # class CTypesStructOrUnion(CTypesBaseStructOrUnion): __slots__ = ['_blob'] _ctype = struct_or_union _reftypename = '%s &' % (name,) _kind = kind = kind1 # 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, onerror): assert onerror is None # XXX not implemented return BType(source, error) typeof = type def getcname(self, BType, replace_with): return BType._get_c_name(replace_with) def typeoffsetof(self, BType, fieldname, num=0): if isinstance(fieldname, str): if num == 0 and issubclass(BType, CTypesGenericPtr): BType = BType._BItem if not issubclass(BType, CTypesBaseStructOrUnion): raise TypeError("expected a struct or union ctype") BField = BType._bfield_types[fieldname] if BField is Ellipsis: raise TypeError("not supported for bitfields") return (BField, BType._offsetof(fieldname)) elif isinstance(fieldname, (int, long)): if issubclass(BType, CTypesGenericArray): BType = BType._CTPtr if not issubclass(BType, CTypesGenericPtr): raise TypeError("expected an array or ptr ctype") BItem = BType._BItem offset = BItem._get_size() * fieldname if offset > sys.maxsize: raise OverflowError return (BItem, offset) else: raise TypeError(type(fieldname)) def rawaddressof(self, BTypePtr, cdata, offset=None): if isinstance(cdata, CTypesBaseStructOrUnion): ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) elif isinstance(cdata, CTypesGenericPtr): if offset is None or not issubclass(type(cdata)._BItem, CTypesBaseStructOrUnion): raise TypeError("unexpected cdata type") ptr = type(cdata)._to_ctypes(cdata) elif isinstance(cdata, CTypesGenericArray): ptr = type(cdata)._to_ctypes(cdata) else: raise TypeError("expected a ") if offset: 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-1.5.2/cffi/cparser.py0000664000175000017500000010743212657646311015616 0ustar arigoarigo00000000000000from . 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"/\*.*?\*/|//([^\n\\]|\\.)*?$", re.DOTALL | re.MULTILINE) _r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" r"\b((?:[^\n\\]|\\.)*?)$", re.DOTALL | 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 _r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) _r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") _r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") _r_cdecl = re.compile(r"\b__cdecl\b") _r_extern_python = re.compile(r'\bextern\s*"Python"\s*.') _r_star_const_space = re.compile( # matches "* const " r"[*]\s*((const|volatile|restrict)\b\s*)+") def _get_parser(): global _parser_cache if _parser_cache is None: _parser_cache = pycparser.CParser() return _parser_cache def _workaround_for_old_pycparser(csource): # Workaround for a pycparser issue (fixed between pycparser 2.10 and # 2.14): "char*const***" gives us a wrong syntax tree, the same as # for "char***(*const)". This means we can't tell the difference # afterwards. But "char(*const(***))" gives us the right syntax # tree. The issue only occurs if there are several stars in # sequence with no parenthesis inbetween, just possibly qualifiers. # Attempt to fix it by adding some parentheses in the source: each # time we see "* const" or "* const *", we add an opening # parenthesis before each star---the hard part is figuring out where # to close them. parts = [] while True: match = _r_star_const_space.search(csource) if not match: break #print repr(''.join(parts)+csource), '=>', parts.append(csource[:match.start()]) parts.append('('); closing = ')' parts.append(match.group()) # e.g. "* const " endpos = match.end() if csource.startswith('*', endpos): parts.append('('); closing += ')' level = 0 i = endpos while i < len(csource): c = csource[i] if c == '(': level += 1 elif c == ')': if level == 0: break level -= 1 elif c in ',;=': if level == 0: break i += 1 csource = csource[endpos:i] + closing + csource[i:] #print repr(''.join(parts)+csource) parts.append(csource) return ''.join(parts) def _preprocess_extern_python(csource): # input: `extern "Python" int foo(int);` or # `extern "Python" { int foo(int); }` # output: # void __cffi_extern_python_start; # int foo(int); # void __cffi_extern_python_stop; parts = [] while True: match = _r_extern_python.search(csource) if not match: break endpos = match.end() - 1 #print #print ''.join(parts)+csource #print '=>' parts.append(csource[:match.start()]) parts.append('void __cffi_extern_python_start; ') if csource[endpos] == '{': # grouping variant closing = csource.find('}', endpos) if closing < 0: raise api.CDefError("'extern \"Python\" {': no '}' found") if csource.find('{', endpos + 1, closing) >= 0: raise NotImplementedError("cannot use { } inside a block " "'extern \"Python\" { ... }'") parts.append(csource[endpos+1:closing]) csource = csource[closing+1:] else: # non-grouping variant semicolon = csource.find(';', endpos) if semicolon < 0: raise api.CDefError("'extern \"Python\": no ';' found") parts.append(csource[endpos:semicolon+1]) csource = csource[semicolon+1:] parts.append(' void __cffi_extern_python_stop;') #print ''.join(parts)+csource #print parts.append(csource) return ''.join(parts) 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() macrovalue = macrovalue.replace('\\\n', '').strip() macros[macroname] = macrovalue csource = _r_define.sub('', csource) # if pycparser.__version__ < '2.14': csource = _workaround_for_old_pycparser(csource) # # BIG HACK: replace WINAPI or __stdcall with "volatile const". # It doesn't make sense for the return type of a function to be # "volatile volatile const", so we abuse it to detect __stdcall... # Hack number 2 is that "int(volatile *fptr)();" is not valid C # syntax, so we place the "volatile" before the opening parenthesis. csource = _r_stdcall2.sub(' volatile volatile const(', csource) csource = _r_stdcall1.sub(' volatile volatile const ', csource) csource = _r_cdecl.sub(' ', csource) # # Replace `extern "Python"` with start/end markers csource = _preprocess_extern_python(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(',') look_for_words.add('(') look_for_words.add(')') look_for_words.add('typedef') words_used = set() is_typedef = False paren = 0 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 paren = 0 elif word == '(': paren += 1 elif word == ')': paren -= 1 elif word == ',': if is_typedef and paren == 0: words_used.discard(previous_word) look_for_words.discard(previous_word) else: # word in COMMON_TYPES words_used.add(word) previous_word = word return words_used class Parser(object): def __init__(self): self._declarations = {} self._included_declarations = set() self._anonymous_counter = 0 self._structnode2type = weakref.WeakKeyDictionary() self._options = {} self._int_constants = {} self._recomplete = [] self._uses_new_feature = None 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() # csource will be used to find buggy source text return ast, macros, csource 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] return line def convert_pycparser_error(self, e, csource): line = self._convert_pycparser_error(e, csource) msg = str(e) 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, dllexport=False): prev_options = self._options try: self._options = {'override': override, 'packed': packed, 'dllexport': dllexport} self._internal_parse(csource) finally: self._options = prev_options def _internal_parse(self, csource): ast, macros, csource = self._parse(csource) # add the macros self._process_macros(macros) # 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 # try: self._inside_extern_python = False 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) quals = 0 if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and decl.type.type.names[-1] == '__dotdotdot__'): realtype = self._get_unknown_type(decl) 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, quals = self._get_type_and_quals( decl.type, name=decl.name) self._declare('typedef ' + decl.name, realtype, quals=quals) else: raise api.CDefError("unrecognized construct", decl) except api.FFIError as e: msg = self._convert_pycparser_error(e, csource) if msg: e.args = (e.args[0] + "\n *** Err: %s" % msg,) raise def _add_constants(self, key, val): if key in self._int_constants: if self._int_constants[key] == val: return # ignore identical double declarations raise api.FFIError( "multiple declarations of constant: %s" % (key,)) self._int_constants[key] = val def _add_integer_constant(self, name, int_str): int_str = int_str.lower().rstrip("ul") neg = int_str.startswith('-') if neg: int_str = int_str[1:] # "010" is not valid oct in py3 if (int_str.startswith("0") and int_str != '0' and not int_str.startswith("0x")): int_str = "0o" + int_str[1:] pyvalue = int(int_str, 0) if neg: pyvalue = -pyvalue self._add_constants(name, pyvalue) self._declare('macro ' + name, pyvalue) def _process_macros(self, macros): for key, value in macros.items(): value = value.strip() if _r_int_literal.match(value): self._add_integer_constant(key, value) elif value == '...': self._declare('macro ' + key, value) else: raise api.CDefError( 'only supports one of the following syntax:\n' ' #define %s ... (literally dot-dot-dot)\n' ' #define %s NUMBER (with NUMBER an integer' ' constant, decimal/hex/octal)\n' 'got:\n' ' #define %s %s' % (key, key, key, value)) def _declare_function(self, tp, quals, decl): tp = self._get_type_pointer(tp, quals) if self._options.get('dllexport'): tag = 'dllexport_python ' elif self._inside_extern_python: tag = 'extern_python ' else: tag = 'function ' self._declare(tag + decl.name, tp) def _parse_decl(self, decl): node = decl.type if isinstance(node, pycparser.c_ast.FuncDecl): tp, quals = self._get_type_and_quals(node, name=decl.name) assert isinstance(tp, model.RawFunctionType) self._declare_function(tp, quals, decl) else: if isinstance(node, pycparser.c_ast.Struct): self._get_struct_union_enum_type('struct', node) elif isinstance(node, pycparser.c_ast.Union): self._get_struct_union_enum_type('union', node) elif isinstance(node, pycparser.c_ast.Enum): 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, quals = self._get_type_and_quals(node, partial_length_ok=True) if tp.is_raw_function: self._declare_function(tp, quals, decl) elif (tp.is_integer_type() and hasattr(decl, 'init') and hasattr(decl.init, 'value') and _r_int_literal.match(decl.init.value)): self._add_integer_constant(decl.name, decl.init.value) elif (tp.is_integer_type() and isinstance(decl.init, pycparser.c_ast.UnaryOp) and decl.init.op == '-' and hasattr(decl.init.expr, 'value') and _r_int_literal.match(decl.init.expr.value)): self._add_integer_constant(decl.name, '-' + decl.init.expr.value) elif (tp is model.void_type and decl.name.startswith('__cffi_extern_python_')): # hack: `extern "Python"` in the C source is replaced # with "void __cffi_extern_python_start;" and # "void __cffi_extern_python_stop;" self._inside_extern_python = not self._inside_extern_python assert self._inside_extern_python == ( decl.name == '__cffi_extern_python_start') else: if self._inside_extern_python: raise api.CDefError( "cannot declare constants or " "variables with 'extern \"Python\"'") if (quals & model.Q_CONST) and not tp.is_array_type: self._declare('constant ' + decl.name, tp, quals=quals) else: self._declare('variable ' + decl.name, tp, quals=quals) def parse_type(self, cdecl): return self.parse_type_and_quals(cdecl)[0] def parse_type_and_quals(self, cdecl): ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] 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_and_quals(exprnode.type) def _declare(self, name, obj, included=False, quals=0): if name in self._declarations: prevobj, prevquals = self._declarations[name] if prevobj is obj and prevquals == quals: return if not self._options.get('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, quals) if included: self._included_declarations.add(obj) def _extract_quals(self, type): quals = 0 if isinstance(type, (pycparser.c_ast.TypeDecl, pycparser.c_ast.PtrDecl)): if 'const' in type.quals: quals |= model.Q_CONST if 'volatile' in type.quals: quals |= model.Q_VOLATILE if 'restrict' in type.quals: quals |= model.Q_RESTRICT return quals def _get_type_pointer(self, type, quals, declname=None): if isinstance(type, model.RawFunctionType): return type.as_function_pointer() if (isinstance(type, model.StructOrUnionOrEnum) and type.name.startswith('$') and type.name[1:].isdigit() and type.forcename is None and declname is not None): return model.NamedPointerType(type, declname, quals) return model.PointerType(type, quals) def _get_type_and_quals(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): tp, quals = self._declarations['typedef ' + typenode.type.names[0]] quals |= self._extract_quals(typenode) return tp, quals # 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) tp, quals = self._get_type_and_quals(typenode.type, partial_length_ok=partial_length_ok) return model.ArrayType(tp, length), quals # if isinstance(typenode, pycparser.c_ast.PtrDecl): # pointer type itemtype, itemquals = self._get_type_and_quals(typenode.type) tp = self._get_type_pointer(itemtype, itemquals, declname=name) quals = self._extract_quals(typenode) return tp, quals # if isinstance(typenode, pycparser.c_ast.TypeDecl): quals = self._extract_quals(typenode) 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, quals if ident == '__dotdotdot__': raise api.FFIError(':%d: bad usage of "..."' % typenode.coord.line) tp0, quals0 = resolve_common_type(self, ident) return tp0, (quals | quals0) # if isinstance(type, pycparser.c_ast.Struct): # 'struct foobar' tp = self._get_struct_union_enum_type('struct', type, name) return tp, quals # if isinstance(type, pycparser.c_ast.Union): # 'union foobar' tp = self._get_struct_union_enum_type('union', type, name) return tp, quals # if isinstance(type, pycparser.c_ast.Enum): # 'enum foobar' tp = self._get_struct_union_enum_type('enum', type, name) return tp, quals # if isinstance(typenode, pycparser.c_ast.FuncDecl): # a function type return self._parse_function_type(typenode, name), 0 # # 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), 0 if isinstance(typenode, pycparser.c_ast.Union): return self._get_struct_union_enum_type('union', typenode, name, nested=True), 0 # raise api.FFIError(":%d: bad or unsupported type declaration" % typenode.coord.line) def _parse_function_type(self, typenode, funcname=None): params = list(getattr(typenode.args, 'params', [])) for i, arg in enumerate(params): if not hasattr(arg, 'type'): raise api.CDefError("%s arg %d: unknown type '%s'" " (if you meant to use the old C syntax of giving" " untyped arguments, it is not supported)" % (funcname or 'in expression', i + 1, getattr(arg, 'name', '?'))) 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')) args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type)) for argdeclnode in params] if not ellipsis and args == [model.void_type]: args = [] result, quals = self._get_type_and_quals(typenode.type) # the 'quals' on the result type are ignored. HACK: we absure them # to detect __stdcall functions: we textually replace "__stdcall" # with "volatile volatile const" above. abi = None if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']: abi = '__stdcall' return model.RawFunctionType(tuple(args), result, ellipsis, abi) def _as_func_arg(self, type, quals): if isinstance(type, model.ArrayType): return model.PointerType(type.item, quals) elif isinstance(type, model.RawFunctionType): return type.as_function_pointer() else: return type 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, 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': if explicit_name == '__dotdotdot__': raise CDefError("Enums cannot be declared with ...") 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 = [] fldquals = [] 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, fqual = self._get_type_and_quals(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) fldquals.append(fqual) tp.fldnames = tuple(fldnames) tp.fldtypes = tuple(fldtypes) tp.fldbitsize = tuple(fldbitsize) tp.fldquals = tuple(fldquals) if fldbitsize != [-1] * len(fldbitsize): if isinstance(tp, model.StructType) and tp.partial: raise NotImplementedError("%s: using both bitfields and '...;'" % (tp,)) tp.packed = self._options.get('packed') if tp.completed: # must be re-completed: it is not opaque any more tp.completed = 0 self._recomplete.append(tp) 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 positive/negative number if isinstance(exprnode, pycparser.c_ast.Constant): s = exprnode.value if s.startswith('0'): if s.startswith('0x') or s.startswith('0X'): return int(s, 16) return int(s, 8) elif '1' <= s[0] <= '9': return int(s, 10) elif s[0] == "'" and s[-1] == "'" and ( len(s) == 3 or (len(s) == 4 and s[1] == "\\")): return ord(s[-2]) else: raise api.CDefError("invalid constant %r" % (s,)) # if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and exprnode.op == '+'): return self._parse_constant(exprnode.expr) # if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and exprnode.op == '-'): return -self._parse_constant(exprnode.expr) # load previously defined int constant if (isinstance(exprnode, pycparser.c_ast.ID) and exprnode.name in self._int_constants): return self._int_constants[exprnode.name] # if partial_length_ok: if (isinstance(exprnode, pycparser.c_ast.ID) and exprnode.name == '__dotdotdotarray__'): self._partial_length = True return '...' # raise api.FFIError(":%d: unsupported expression: expected a " "simple numeric constant" % exprnode.coord.line) def _build_enum_type(self, explicit_name, decls): if decls is not None: partial = False enumerators = [] enumvalues = [] nextenumvalue = 0 for enum in decls.enumerators: if _r_enum_dotdotdot.match(enum.name): partial = True continue if enum.value is not None: nextenumvalue = self._parse_constant(enum.value) enumerators.append(enum.name) enumvalues.append(nextenumvalue) self._add_constants(enum.name, nextenumvalue) nextenumvalue += 1 enumerators = tuple(enumerators) 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, quals) in other._declarations.items(): if name.startswith('anonymous $enum_$'): continue # fix for test_anonymous_enum_include kind = name.split(' ', 1)[0] if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'): self._declare(name, tp, included=True, quals=quals) for k, v in other._int_constants.items(): self._add_constants(k, v) def _get_unknown_type(self, decl): typenames = decl.type.type.names assert typenames[-1] == '__dotdotdot__' if len(typenames) == 1: return model.unknown_type(decl.name) if (typenames[:-1] == ['float'] or typenames[:-1] == ['double']): # not for 'long double' so far result = model.UnknownFloatType(decl.name) else: for t in typenames[:-1]: if t not in ['int', 'short', 'long', 'signed', 'unsigned', 'char']: raise api.FFIError(':%d: bad usage of "..."' % decl.coord.line) result = model.UnknownIntegerType(decl.name) if self._uses_new_feature is None: self._uses_new_feature = "'typedef %s... %s'" % ( ' '.join(typenames[:-1]), decl.name) return result cffi-1.5.2/cffi/parse_c_type.h0000664000175000017500000001331312657646311016425 0ustar arigoarigo00000000000000 /* This part is from file 'cffi/parse_c_type.h'. It is copied at the beginning of C sources generated by CFFI's ffi.set_source(). */ typedef void *_cffi_opcode_t; #define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) #define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) #define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) #define _CFFI_OP_PRIMITIVE 1 #define _CFFI_OP_POINTER 3 #define _CFFI_OP_ARRAY 5 #define _CFFI_OP_OPEN_ARRAY 7 #define _CFFI_OP_STRUCT_UNION 9 #define _CFFI_OP_ENUM 11 #define _CFFI_OP_FUNCTION 13 #define _CFFI_OP_FUNCTION_END 15 #define _CFFI_OP_NOOP 17 #define _CFFI_OP_BITFIELD 19 #define _CFFI_OP_TYPENAME 21 #define _CFFI_OP_CPYTHON_BLTN_V 23 // varargs #define _CFFI_OP_CPYTHON_BLTN_N 25 // noargs #define _CFFI_OP_CPYTHON_BLTN_O 27 // O (i.e. a single arg) #define _CFFI_OP_CONSTANT 29 #define _CFFI_OP_CONSTANT_INT 31 #define _CFFI_OP_GLOBAL_VAR 33 #define _CFFI_OP_DLOPEN_FUNC 35 #define _CFFI_OP_DLOPEN_CONST 37 #define _CFFI_OP_GLOBAL_VAR_F 39 #define _CFFI_OP_EXTERN_PYTHON 41 #define _CFFI_PRIM_VOID 0 #define _CFFI_PRIM_BOOL 1 #define _CFFI_PRIM_CHAR 2 #define _CFFI_PRIM_SCHAR 3 #define _CFFI_PRIM_UCHAR 4 #define _CFFI_PRIM_SHORT 5 #define _CFFI_PRIM_USHORT 6 #define _CFFI_PRIM_INT 7 #define _CFFI_PRIM_UINT 8 #define _CFFI_PRIM_LONG 9 #define _CFFI_PRIM_ULONG 10 #define _CFFI_PRIM_LONGLONG 11 #define _CFFI_PRIM_ULONGLONG 12 #define _CFFI_PRIM_FLOAT 13 #define _CFFI_PRIM_DOUBLE 14 #define _CFFI_PRIM_LONGDOUBLE 15 #define _CFFI_PRIM_WCHAR 16 #define _CFFI_PRIM_INT8 17 #define _CFFI_PRIM_UINT8 18 #define _CFFI_PRIM_INT16 19 #define _CFFI_PRIM_UINT16 20 #define _CFFI_PRIM_INT32 21 #define _CFFI_PRIM_UINT32 22 #define _CFFI_PRIM_INT64 23 #define _CFFI_PRIM_UINT64 24 #define _CFFI_PRIM_INTPTR 25 #define _CFFI_PRIM_UINTPTR 26 #define _CFFI_PRIM_PTRDIFF 27 #define _CFFI_PRIM_SIZE 28 #define _CFFI_PRIM_SSIZE 29 #define _CFFI_PRIM_INT_LEAST8 30 #define _CFFI_PRIM_UINT_LEAST8 31 #define _CFFI_PRIM_INT_LEAST16 32 #define _CFFI_PRIM_UINT_LEAST16 33 #define _CFFI_PRIM_INT_LEAST32 34 #define _CFFI_PRIM_UINT_LEAST32 35 #define _CFFI_PRIM_INT_LEAST64 36 #define _CFFI_PRIM_UINT_LEAST64 37 #define _CFFI_PRIM_INT_FAST8 38 #define _CFFI_PRIM_UINT_FAST8 39 #define _CFFI_PRIM_INT_FAST16 40 #define _CFFI_PRIM_UINT_FAST16 41 #define _CFFI_PRIM_INT_FAST32 42 #define _CFFI_PRIM_UINT_FAST32 43 #define _CFFI_PRIM_INT_FAST64 44 #define _CFFI_PRIM_UINT_FAST64 45 #define _CFFI_PRIM_INTMAX 46 #define _CFFI_PRIM_UINTMAX 47 #define _CFFI__NUM_PRIM 48 #define _CFFI__UNKNOWN_PRIM (-1) #define _CFFI__UNKNOWN_FLOAT_PRIM (-2) #define _CFFI__UNKNOWN_LONG_DOUBLE (-3) #define _CFFI__IO_FILE_STRUCT (-1) struct _cffi_global_s { const char *name; void *address; _cffi_opcode_t type_op; void *size_or_direct_fn; // OP_GLOBAL_VAR: size, or 0 if unknown // OP_CPYTHON_BLTN_*: addr of direct function }; struct _cffi_getconst_s { unsigned long long value; const struct _cffi_type_context_s *ctx; int gindex; }; struct _cffi_struct_union_s { const char *name; int type_index; // -> _cffi_types, on a OP_STRUCT_UNION int flags; // _CFFI_F_* flags below size_t size; int alignment; int first_field_index; // -> _cffi_fields array int num_fields; }; #define _CFFI_F_UNION 0x01 // is a union, not a struct #define _CFFI_F_CHECK_FIELDS 0x02 // complain if fields are not in the // "standard layout" or if some are missing #define _CFFI_F_PACKED 0x04 // for CHECK_FIELDS, assume a packed struct #define _CFFI_F_EXTERNAL 0x08 // in some other ffi.include() #define _CFFI_F_OPAQUE 0x10 // opaque struct _cffi_field_s { const char *name; size_t field_offset; size_t field_size; _cffi_opcode_t field_type_op; }; struct _cffi_enum_s { const char *name; int type_index; // -> _cffi_types, on a OP_ENUM int type_prim; // _CFFI_PRIM_xxx const char *enumerators; // comma-delimited string }; struct _cffi_typename_s { const char *name; int type_index; /* if opaque, points to a possibly artificial OP_STRUCT which is itself opaque */ }; struct _cffi_type_context_s { _cffi_opcode_t *types; const struct _cffi_global_s *globals; const struct _cffi_field_s *fields; const struct _cffi_struct_union_s *struct_unions; const struct _cffi_enum_s *enums; const struct _cffi_typename_s *typenames; int num_globals; int num_struct_unions; int num_enums; int num_typenames; const char *const *includes; int num_types; int flags; /* future extension */ }; struct _cffi_parse_info_s { const struct _cffi_type_context_s *ctx; _cffi_opcode_t *output; unsigned int output_size; size_t error_location; const char *error_message; }; struct _cffi_externpy_s { const char *name; size_t size_of_result; void *reserved1, *reserved2; }; #ifdef _CFFI_INTERNAL static int parse_c_type(struct _cffi_parse_info_s *info, const char *input); static int search_in_globals(const struct _cffi_type_context_s *ctx, const char *search, size_t search_len); static int search_in_struct_unions(const struct _cffi_type_context_s *ctx, const char *search, size_t search_len); #endif cffi-1.5.2/cffi/ffiplatform.py0000664000175000017500000000722112657646311016463 0ustar arigoarigo00000000000000import sys, 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 """ LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', 'extra_objects', 'depends'] def get_extension(srcfilename, modname, sources=(), **kwds): from distutils.core import Extension allsources = [srcfilename] for src in sources: allsources.append(os.path.normpath(src)) return Extension(name=modname, sources=allsources, **kwds) def compile(tmpdir, ext, compiler_verbose=0): """Compile a C extension module using distutils.""" saved_environ = os.environ.copy() try: outputfilename = _build(tmpdir, ext, compiler_verbose) 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, compiler_verbose=0): # XXX compact but horrible :-( from distutils.core import Distribution import distutils.errors, distutils.log # dist = Distribution({'ext_modules': [ext]}) dist.parse_config_files() options = dist.get_option_dict('build_ext') options['force'] = ('ffiplatform', True) options['build_lib'] = ('ffiplatform', tmpdir) options['build_temp'] = ('ffiplatform', tmpdir) # try: old_level = distutils.log.set_threshold(0) or 0 try: distutils.log.set_verbosity(compiler_verbose) dist.run_command('build_ext') cmd_obj = dist.get_command_obj('build_ext') [soname] = cmd_obj.get_outputs() finally: distutils.log.set_threshold(old_level) except (distutils.errors.CompileError, distutils.errors.LinkError) as e: raise VerificationError('%s: %s' % (e.__class__.__name__, e)) # 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-1.5.2/cffi/vengine_gen.py0000664000175000017500000006401112657646311016436 0ustar arigoarigo00000000000000# # DEPRECATED: implementation for ffi.verify() # import 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, flags=0): # 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, flags) # # 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): lst = [(key, tp) for (key, (tp, qual)) in self.ffi._parser._declarations.items()] lst.sort() return lst 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)] tpresult = tp.result if isinstance(tpresult, model.StructOrUnion): arglist.insert(0, tpresult.get_c_name(' *r', context)) tpresult = model.void_type arglist = ', '.join(arglist) or 'void' wrappername = '_cffi_f_%s' % name self.export_symbols.append(wrappername) if tp.abi: abi = tp.abi + ' ' else: abi = '' funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) context = 'result of %s' % name prnt(tpresult.get_c_name(funcdecl, context)) prnt('{') # if isinstance(tp.result, model.StructOrUnion): result_code = '*r = ' elif 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) or isinstance(tp.result, model.StructOrUnion)): 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) indirect_result = tp.result if isinstance(indirect_result, model.StructOrUnion): if indirect_result.fldtypes is None: raise TypeError("'%s' is used as result type, " "but is opaque" % ( indirect_result._get_c_name(),)) indirect_result = model.PointerType(indirect_result) indirect_args.insert(0, indirect_result) indirections.insert(0, ("result", indirect_result)) indirect_result = model.void_type tp = model.FunctionPtrType(tuple(indirect_args), indirect_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) if i == "result": ffi = self.ffi def newfunc(*args): res = ffi.new(BType) oldfunc(res, *args) return res[0] else: 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 */') prnt(' (void)p;') for fname, ftype, fbitsize, fqual 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, quals=fqual), fname)) except ffiplatform.VerificationError as e: prnt(' /* %s */' % str(e)) # cannot verify it, ignore prnt('}') self.export_symbols.append(layoutfuncname) prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,)) prnt('{') prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) prnt(' static intptr_t nums[] = {') prnt(' sizeof(%s),' % cname) prnt(' offsetof(struct _cffi_aligncheck, y),') for fname, ftype, fbitsize, fqual 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("intptr_t(*)(intptr_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, fqual 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', check_value=None): prnt = self._prnt funcname = '_cffi_%s_%s' % (category, name) self.export_symbols.append(funcname) if check_value is not None: assert is_int assert category == 'const' prnt('int %s(char *out_error)' % funcname) prnt('{') self._check_int_constant_value(name, check_value) prnt(' return 0;') prnt('}') elif 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 assert check_value is None if category == 'var': ampersand = '&' else: ampersand = '' extra = '' if category == 'const' and isinstance(tp, model.StructOrUnion): extra = 'const *' ampersand = '&' prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name)) prnt('{') 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, check_value=None): funcname = '_cffi_const_%s' % name if check_value is not None: assert is_int self._load_known_int_constant(module, funcname) value = check_value elif 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: assert check_value is None fntypeextra = '(*)(void)' if isinstance(tp, model.StructOrUnion): fntypeextra = '*' + fntypeextra BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0] function = module.load_function(BFunc, funcname) value = function() if isinstance(tp, model.StructOrUnion): value = value[0] 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 _check_int_constant_value(self, name, value): prnt = self._prnt if value <= 0: prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( name, name, value)) else: prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( name, name, value)) prnt(' char buf[64];') prnt(' if ((%s) <= 0)' % name) prnt(' sprintf(buf, "%%ld", (long)(%s));' % name) prnt(' else') prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % name) prnt(' sprintf(out_error, "%s has the real value %s, not %s",') prnt(' "%s", buf, "%d");' % (name[:100], value)) prnt(' return -1;') prnt(' }') def _load_known_int_constant(self, module, funcname): BType = self.ffi._typeof_locked("char[]")[0] BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] 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 _enum_funcname(self, prefix, name): # "$enum_$1" => "___D_enum____D_1" name = name.replace('$', '___D_') return '_cffi_e_%s_%s' % (prefix, name) def _generate_gen_enum_decl(self, tp, name, prefix='enum'): if tp.partial: for enumerator in tp.enumerators: self._generate_gen_const(True, enumerator) return # funcname = self._enum_funcname(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): self._check_int_constant_value(enumerator, enumvalue) 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: funcname = self._enum_funcname(prefix, name) self._load_known_int_constant(module, funcname) 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): if tp == '...': check_value = None else: check_value = tp # an integer self._generate_gen_const(True, name, check_value=check_value) _loading_gen_macro = _loaded_noop def _loaded_gen_macro(self, tp, name, module, library): if tp == '...': check_value = None else: check_value = tp # an integer value = self._load_constant(True, tp, name, module, check_value=check_value) 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 */ /* this block of #ifs should be kept exactly identical between c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py */ #if defined(_MSC_VER) # include /* for alloca() */ # if _MSC_VER < 1600 /* MSVC < 2010 */ 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 __int8 int_least8_t; typedef __int16 int_least16_t; typedef __int32 int_least32_t; typedef __int64 int_least64_t; typedef unsigned __int8 uint_least8_t; typedef unsigned __int16 uint_least16_t; typedef unsigned __int32 uint_least32_t; typedef unsigned __int64 uint_least64_t; typedef __int8 int_fast8_t; typedef __int16 int_fast16_t; typedef __int32 int_fast32_t; typedef __int64 int_fast64_t; typedef unsigned __int8 uint_fast8_t; typedef unsigned __int16 uint_fast16_t; typedef unsigned __int32 uint_fast32_t; typedef unsigned __int64 uint_fast64_t; typedef __int64 intmax_t; typedef unsigned __int64 uintmax_t; # else # include # endif # if _MSC_VER < 1800 /* MSVC < 2013 */ typedef unsigned char _Bool; # endif #else # include # if (defined (__SVR4) && defined (__sun)) || defined(_AIX) # include # endif #endif ''' cffi-1.5.2/cffi/gc_weakref.py0000664000175000017500000000120212657646311016240 0ustar arigoarigo00000000000000from weakref import ref class GcWeakrefs(object): def __init__(self, ffi): self.ffi = ffi self.data = {} 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) # def remove(key): # careful, this function is not protected by any lock old_key = self.data.pop(index) assert old_key is key destructor(cdata) # key = ref(new_cdata, remove) index = object() self.data[index] = key return new_cdata cffi-1.5.2/cffi/api.py0000664000175000017500000010640512657646311014727 0ustar arigoarigo00000000000000import 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__, \ "version mismatch, %s != %s" % (backend.__version__, __version__) # (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 = [] self._included_ffis = [] self._windows_unicode = None self._init_once_cache = {} self._cdef_version = None self._embedding = None 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) self.BCharA = self._get_cached_btype(model.char_array_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. """ self._cdef(csource, override=override, packed=packed) def embedding_api(self, csource, packed=False): self._cdef(csource, packed=packed, dllexport=True) if self._embedding is None: self._embedding = '' def _cdef(self, csource, override=False, **options): 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._cdef_version = object() self._parser.parse(csource, override=override, **options) self._cdefsources.append(csource) if override: for cache in self._function_caches: cache.clear() finishlist = self._parser._recomplete if finishlist: self._parser._recomplete = [] for tp in finishlist: tp.finish_backend_type(self, finishlist) 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, *fields_or_indexes): """Return the offset of the named field inside the given structure or array, which must be given as a C type name. You can give several field names in case of nested structures. You can also give numeric values which correspond to array items, in case of an array type. """ if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl) return self._typeoffsetof(cdecl, *fields_or_indexes)[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 new_allocator(self, alloc=None, free=None, should_clear_after_alloc=True): """Return a new allocator, i.e. a function that behaves like ffi.new() but uses the provided low-level 'alloc' and 'free' functions. 'alloc' is called with the size as argument. If it returns NULL, a MemoryError is raised. 'free' is called with the result of 'alloc' as argument. Both can be either Python function or directly C functions. If 'free' is None, then no free function is called. If both 'alloc' and 'free' are None, the default is used. If 'should_clear_after_alloc' is set to False, then the memory returned by 'alloc' is assumed to be already cleared (or you are fine with garbage); otherwise CFFI will clear it. """ compiled_ffi = self._backend.FFI() allocator = compiled_ffi.new_allocator(alloc, free, should_clear_after_alloc) def allocate(cdecl, init=None): if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl) return allocator(cdecl, init) return allocate 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 from_buffer(self, python_buffer): """Return a that points to the data of the given Python object, which must support the buffer interface. Note that this is not meant to be used on the built-in types str, unicode, or bytearray (you can build 'char[]' arrays explicitly) but only on objects containing large quantities of raw data in some other format, like 'array.array' or numpy arrays. """ return self._backend.from_buffer(self.BCharA, python_buffer) def memmove(self, dest, src, n): """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest. Like the C function memmove(), the memory areas may overlap; apart from that it behaves like the C function memcpy(). 'src' can be any cdata ptr or array, or any Python buffer object. 'dest' can be any cdata ptr or array, or a writable Python buffer object. The size to copy, 'n', is always measured in bytes. Unlike other methods, this one supports all Python buffer including byte strings and bytearrays---but it still does not support non-contiguous buffers. """ return self._backend.memmove(dest, src, n) def callback(self, cdecl, python_callable=None, error=None, onerror=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, onerror) 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. """ try: gcp = self._backend.gcp except AttributeError: pass else: return gcp(cdata, destructor) # 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 # # If set_unicode(True) was called, insert the UNICODE and # _UNICODE macro declarations if self._windows_unicode: self._apply_windows_unicode(kwargs) # # Set the tmpdir here, and not in Verifier.__init__: it picks # up the caller's directory, which we want to be the caller of # ffi.verify(), as opposed to the caller of Veritier(). tmpdir = tmpdir or _caller_dir_pycache() # # Make a Verifier() and use it to load the library. self.verifier = Verifier(self, source, tmpdir, **kwargs) lib = self.verifier.load_library() # # Save the loaded library for keep-alive purposes, even # if the caller doesn't keep it alive itself (it should). 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, *fields_or_indexes): """Return the address of a . If 'fields_or_indexes' are given, returns the address of that field or array item in the structure or array, recursively in case of nested structures. """ ctype = self._backend.typeof(cdata) if fields_or_indexes: ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) else: if ctype.kind == "pointer": raise TypeError("addressof(pointer)") offset = 0 ctypeptr = self._pointer_to(ctype) return self._backend.rawaddressof(ctypeptr, cdata, offset) def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes): ctype, offset = self._backend.typeoffsetof(ctype, field_or_index) for field1 in fields_or_indexes: ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1) offset += offset1 return ctype, 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. """ if not isinstance(ffi_to_include, FFI): raise TypeError("ffi.include() expects an argument that is also of" " type cffi.FFI, not %r" % ( type(ffi_to_include).__name__,)) if ffi_to_include is self: raise ValueError("self.include(self)") 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(']') self._included_ffis.append(ffi_to_include) 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 set_unicode(self, enabled_flag): """Windows: if 'enabled_flag' is True, enable the UNICODE and _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR to be (pointers to) wchar_t. If 'enabled_flag' is False, declare these types to be (pointers to) plain 8-bit characters. This is mostly for backward compatibility; you usually want True. """ if self._windows_unicode is not None: raise ValueError("set_unicode() can only be called once") enabled_flag = bool(enabled_flag) if enabled_flag: self.cdef("typedef wchar_t TBYTE;" "typedef wchar_t TCHAR;" "typedef const wchar_t *LPCTSTR;" "typedef const wchar_t *PCTSTR;" "typedef wchar_t *LPTSTR;" "typedef wchar_t *PTSTR;" "typedef TBYTE *PTBYTE;" "typedef TCHAR *PTCHAR;") else: self.cdef("typedef char TBYTE;" "typedef char TCHAR;" "typedef const char *LPCTSTR;" "typedef const char *PCTSTR;" "typedef char *LPTSTR;" "typedef char *PTSTR;" "typedef TBYTE *PTBYTE;" "typedef TCHAR *PTCHAR;") self._windows_unicode = enabled_flag def _apply_windows_unicode(self, kwds): defmacros = kwds.get('define_macros', ()) if not isinstance(defmacros, (list, tuple)): raise TypeError("'define_macros' must be a list or tuple") defmacros = list(defmacros) + [('UNICODE', '1'), ('_UNICODE', '1')] kwds['define_macros'] = defmacros def _apply_embedding_fix(self, kwds): # must include an argument like "-lpython2.7" for the compiler def ensure(key, value): lst = kwds.setdefault(key, []) if value not in lst: lst.append(value) # if '__pypy__' in sys.builtin_module_names: if sys.platform == "win32": # we need 'libpypy-c.lib'. Right now, distributions of # pypy contain it as 'include/python27.lib'. You need # to manually copy it back to 'libpypy-c.lib'. XXX Will # be fixed in the next pypy release. pythonlib = "libpypy-c" if hasattr(sys, 'prefix'): ensure('library_dirs', sys.prefix) else: # we need 'libpypy-c.{so,dylib}', which should be by # default located in 'sys.prefix/bin' pythonlib = "pypy-c" if hasattr(sys, 'prefix'): import os ensure('library_dirs', os.path.join(sys.prefix, 'bin')) else: if sys.platform == "win32": template = "python%d%d" if hasattr(sys, 'gettotalrefcount'): template += '_d' else: try: import sysconfig except ImportError: # 2.6 from distutils import sysconfig template = "python%d.%d" if sysconfig.get_config_var('DEBUG_EXT'): template += sysconfig.get_config_var('DEBUG_EXT') pythonlib = (template % (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) if hasattr(sys, 'abiflags'): pythonlib += sys.abiflags ensure('libraries', pythonlib) if sys.platform == "win32": ensure('extra_link_args', '/MANIFEST') def set_source(self, module_name, source, source_extension='.c', **kwds): if hasattr(self, '_assigned_source'): raise ValueError("set_source() cannot be called several times " "per ffi object") if not isinstance(module_name, basestring): raise TypeError("'module_name' must be a string") self._assigned_source = (str(module_name), source, source_extension, kwds) def distutils_extension(self, tmpdir='build', verbose=True): from distutils.dir_util import mkpath from .recompiler import recompile # if not hasattr(self, '_assigned_source'): if hasattr(self, 'verifier'): # fallback, 'tmpdir' ignored return self.verifier.get_extension() raise ValueError("set_source() must be called before" " distutils_extension()") module_name, source, source_extension, kwds = self._assigned_source if source is None: raise TypeError("distutils_extension() is only for C extension " "modules, not for dlopen()-style pure Python " "modules") mkpath(tmpdir) ext, updated = recompile(self, module_name, source, tmpdir=tmpdir, extradir=tmpdir, source_extension=source_extension, call_c_compiler=False, **kwds) if verbose: if updated: sys.stderr.write("regenerated: %r\n" % (ext.sources[0],)) else: sys.stderr.write("not modified: %r\n" % (ext.sources[0],)) return ext def emit_c_code(self, filename): from .recompiler import recompile # if not hasattr(self, '_assigned_source'): raise ValueError("set_source() must be called before emit_c_code()") module_name, source, source_extension, kwds = self._assigned_source if source is None: raise TypeError("emit_c_code() is only for C extension modules, " "not for dlopen()-style pure Python modules") recompile(self, module_name, source, c_file=filename, call_c_compiler=False, **kwds) def emit_python_code(self, filename): from .recompiler import recompile # if not hasattr(self, '_assigned_source'): raise ValueError("set_source() must be called before emit_c_code()") module_name, source, source_extension, kwds = self._assigned_source if source is not None: raise TypeError("emit_python_code() is only for dlopen()-style " "pure Python modules, not for C extension modules") recompile(self, module_name, source, c_file=filename, call_c_compiler=False, **kwds) def compile(self, tmpdir='.', verbose=0, target=None): """The 'target' argument gives the final file name of the compiled DLL. Use '*' to force distutils' choice, suitable for regular CPython C API modules. Use a file name ending in '.*' to ask for the system's default extension for dynamic libraries (.so/.dll/.dylib). The default is '*' when building a non-embedded C API extension, and (module_name + '.*') when building an embedded library. """ from .recompiler import recompile # if not hasattr(self, '_assigned_source'): raise ValueError("set_source() must be called before compile()") module_name, source, source_extension, kwds = self._assigned_source return recompile(self, module_name, source, tmpdir=tmpdir, target=target, source_extension=source_extension, compiler_verbose=verbose, **kwds) def init_once(self, func, tag): # Read _init_once_cache[tag], which is either (False, lock) if # we're calling the function now in some thread, or (True, result). # Don't call setdefault() in most cases, to avoid allocating and # immediately freeing a lock; but still use setdefaut() to avoid # races. try: x = self._init_once_cache[tag] except KeyError: x = self._init_once_cache.setdefault(tag, (False, allocate_lock())) # Common case: we got (True, result), so we return the result. if x[0]: return x[1] # Else, it's a lock. Acquire it to serialize the following tests. with x[1]: # Read again from _init_once_cache the current status. x = self._init_once_cache[tag] if x[0]: return x[1] # Call the function and store the result back. result = func() self._init_once_cache[tag] = (True, result) return result def embedding_init_code(self, pysource): if self._embedding: raise ValueError("embedding_init_code() can only be called once") # fix 'pysource' before it gets dumped into the C file: # - remove empty lines at the beginning, so it starts at "line 1" # - dedent, if all non-empty lines are indented # - check for SyntaxErrors import re match = re.match(r'\s*\n', pysource) if match: pysource = pysource[match.end():] lines = pysource.splitlines() or [''] prefix = re.match(r'\s*', lines[0]).group() for i in range(1, len(lines)): line = lines[i] if line.rstrip(): while not line.startswith(prefix): prefix = prefix[:-1] i = len(prefix) lines = [line[i:]+'\n' for line in lines] pysource = ''.join(lines) # compile(pysource, "cffi_init", "exec") # self._embedding = pysource def def_extern(self, *args, **kwds): raise ValueError("ffi.def_extern() is only available on API-mode FFI " "objects") 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) # def accessor_function(name): key = 'function ' + name 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 # def accessor_variable(name): key = 'variable ' + name 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))) # def accessor_constant(name): raise NotImplementedError("non-integer constant '%s' cannot be " "accessed from a dlopen() library" % (name,)) # def accessor_int_constant(name): library.__dict__[name] = ffi._parser._int_constants[name] # accessors = {} accessors_version = [False] # def update_accessors(): if accessors_version[0] is ffi._cdef_version: return # from . import model for key, (tp, _) in ffi._parser._declarations.items(): if not isinstance(tp, model.EnumType): tag, name = key.split(' ', 1) if tag == 'function': accessors[name] = accessor_function elif tag == 'variable': accessors[name] = accessor_variable elif tag == 'constant': accessors[name] = accessor_constant else: for i, enumname in enumerate(tp.enumerators): def accessor_enum(name, tp=tp, i=i): tp.check_not_partial() library.__dict__[name] = tp.enumvalues[i] accessors[enumname] = accessor_enum for name in ffi._parser._int_constants: accessors.setdefault(name, accessor_int_constant) accessors_version[0] = ffi._cdef_version # 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 if name not in accessors: update_accessors() if name not in accessors: raise AttributeError(name) accessors[name](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) def __dir__(self): with ffi._lock: update_accessors() return accessors.keys() # 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-1.5.2/cffi/lock.py0000664000175000017500000000135312657646311015102 0ustar arigoarigo00000000000000import 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-1.5.2/cffi/_embedding.h0000664000175000017500000004154112657646311016031 0ustar arigoarigo00000000000000 /***** Support code for embedding *****/ #if defined(_MSC_VER) # define CFFI_DLLEXPORT __declspec(dllexport) #elif defined(__GNUC__) # define CFFI_DLLEXPORT __attribute__((visibility("default"))) #else # define CFFI_DLLEXPORT /* nothing */ #endif /* There are two global variables of type _cffi_call_python_fnptr: * _cffi_call_python, which we declare just below, is the one called by ``extern "Python"`` implementations. * _cffi_call_python_org, which on CPython is actually part of the _cffi_exports[] array, is the function pointer copied from _cffi_backend. After initialization is complete, both are equal. However, the first one remains equal to &_cffi_start_and_call_python until the very end of initialization, when we are (or should be) sure that concurrent threads also see a completely initialized world, and only then is it changed. */ #undef _cffi_call_python typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; #ifndef _MSC_VER /* --- Assuming a GCC not infinitely old --- */ # define cffi_compare_and_swap(l,o,n) __sync_bool_compare_and_swap(l,o,n) # define cffi_write_barrier() __sync_synchronize() # if !defined(__amd64__) && !defined(__x86_64__) && \ !defined(__i386__) && !defined(__i386) # define cffi_read_barrier() __sync_synchronize() # else # define cffi_read_barrier() (void)0 # endif #else /* --- Windows threads version --- */ # include # define cffi_compare_and_swap(l,o,n) \ (InterlockedCompareExchangePointer(l,n,o) == (o)) # define cffi_write_barrier() InterlockedCompareExchange(&_cffi_dummy,0,0) # define cffi_read_barrier() (void)0 static volatile LONG _cffi_dummy; #endif #ifdef WITH_THREAD # ifndef _MSC_VER # include static pthread_mutex_t _cffi_embed_startup_lock; # else static CRITICAL_SECTION _cffi_embed_startup_lock; # endif static char _cffi_embed_startup_lock_ready = 0; #endif static void _cffi_acquire_reentrant_mutex(void) { static void *volatile lock = NULL; while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { /* should ideally do a spin loop instruction here, but hard to do it portably and doesn't really matter I think: pthread_mutex_init() should be very fast, and this is only run at start-up anyway. */ } #ifdef WITH_THREAD if (!_cffi_embed_startup_lock_ready) { # ifndef _MSC_VER pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&_cffi_embed_startup_lock, &attr); # else InitializeCriticalSection(&_cffi_embed_startup_lock); # endif _cffi_embed_startup_lock_ready = 1; } #endif while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) ; #ifndef _MSC_VER pthread_mutex_lock(&_cffi_embed_startup_lock); #else EnterCriticalSection(&_cffi_embed_startup_lock); #endif } static void _cffi_release_reentrant_mutex(void) { #ifndef _MSC_VER pthread_mutex_unlock(&_cffi_embed_startup_lock); #else LeaveCriticalSection(&_cffi_embed_startup_lock); #endif } /********** CPython-specific section **********/ #ifndef PYPY_VERSION #define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX] PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ static void _cffi_py_initialize(void) { /* XXX use initsigs=0, which "skips initialization registration of signal handlers, which might be useful when Python is embedded" according to the Python docs. But review and think if it should be a user-controllable setting. XXX we should also give a way to write errors to a buffer instead of to stderr. XXX if importing 'site' fails, CPython (any version) calls exit(). Should we try to work around this behavior here? */ Py_InitializeEx(0); } static int _cffi_initialize_python(void) { /* This initializes Python, imports _cffi_backend, and then the present .dll/.so is set up as a CPython C extension module. */ int result; PyGILState_STATE state; PyObject *pycode=NULL, *global_dict=NULL, *x; #if PY_MAJOR_VERSION >= 3 /* see comments in _cffi_carefully_make_gil() about the Python2/Python3 difference */ #else /* Acquire the GIL. We have no threadstate here. If Python is already initialized, it is possible that there is already one existing for this thread, but it is not made current now. */ PyEval_AcquireLock(); _cffi_py_initialize(); /* The Py_InitializeEx() sometimes made a threadstate for us, but not always. Indeed Py_InitializeEx() could be called and do nothing. So do we have a threadstate, or not? We don't know, but we can replace it with NULL in all cases. */ (void)PyThreadState_Swap(NULL); /* Now we can release the GIL and re-acquire immediately using the logic of PyGILState(), which handles making or installing the correct threadstate. */ PyEval_ReleaseLock(); #endif state = PyGILState_Ensure(); /* Call the initxxx() function from the present module. It will create and initialize us as a CPython extension module, instead of letting the startup Python code do it---it might reimport the same .dll/.so and get maybe confused on some platforms. It might also have troubles locating the .dll/.so again for all I know. */ (void)_CFFI_PYTHON_STARTUP_FUNC(); if (PyErr_Occurred()) goto error; /* Now run the Python code provided to ffi.embedding_init_code(). */ pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, "", Py_file_input); if (pycode == NULL) goto error; global_dict = PyDict_New(); if (global_dict == NULL) goto error; if (PyDict_SetItemString(global_dict, "__builtins__", PyThreadState_GET()->interp->builtins) < 0) goto error; x = PyEval_EvalCode( #if PY_MAJOR_VERSION < 3 (PyCodeObject *) #endif pycode, global_dict, global_dict); if (x == NULL) goto error; Py_DECREF(x); /* Done! Now if we've been called from _cffi_start_and_call_python() in an ``extern "Python"``, we can only hope that the Python code did correctly set up the corresponding @ffi.def_extern() function. Otherwise, the general logic of ``extern "Python"`` functions (inside the _cffi_backend module) will find that the reference is still missing and print an error. */ result = 0; done: Py_XDECREF(pycode); Py_XDECREF(global_dict); PyGILState_Release(state); return result; error:; { /* Print as much information as potentially useful. Debugging load-time failures with embedding is not fun */ PyObject *exception, *v, *tb, *f, *modules, *mod; PyErr_Fetch(&exception, &v, &tb); if (exception != NULL) { PyErr_NormalizeException(&exception, &v, &tb); PyErr_Display(exception, v, tb); } Py_XDECREF(exception); Py_XDECREF(v); Py_XDECREF(tb); f = PySys_GetObject((char *)"stderr"); if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME "\ncompiled with cffi version: 1.5.2" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); if (mod == NULL) { PyFile_WriteString("not loaded", f); } else { v = PyObject_GetAttrString(mod, "__file__"); PyFile_WriteObject(v, f, 0); Py_XDECREF(v); } PyFile_WriteString("\nsys.path: ", f); PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); PyFile_WriteString("\n\n", f); } } result = -1; goto done; } PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ static int _cffi_carefully_make_gil(void) { /* This does the basic initialization of Python. It can be called completely concurrently from unrelated threads. It assumes that we don't hold the GIL before (if it exists), and we don't hold it afterwards. What it really does is completely different in Python 2 and Python 3. Python 2 ======== Initialize the GIL, without initializing the rest of Python, by calling PyEval_InitThreads(). PyEval_InitThreads() must not be called concurrently at all. So we use a global variable as a simple spin lock. This global variable must be from 'libpythonX.Y.so', not from this cffi-based extension module, because it must be shared from different cffi-based extension modules. We choose _PyParser_TokenNames[0] as a completely arbitrary pointer value that is never written to. The default is to point to the string "ENDMARKER". We change it temporarily to point to the next character in that string. (Yes, I know it's REALLY obscure.) Python 3 ======== In Python 3, PyEval_InitThreads() cannot be called before Py_InitializeEx() any more. So this function calls Py_InitializeEx() first. It uses the same obscure logic to make sure we never call it concurrently. Arguably, this is less good on the spinlock, because Py_InitializeEx() takes much longer to run than PyEval_InitThreads(). But I didn't find a way around it. */ #ifdef WITH_THREAD char *volatile *lock = (char *volatile *)_PyParser_TokenNames; char *old_value; while (1) { /* spin loop */ old_value = *lock; if (old_value[0] == 'E') { assert(old_value[1] == 'N'); if (cffi_compare_and_swap(lock, old_value, old_value + 1)) break; } else { assert(old_value[0] == 'N'); /* should ideally do a spin loop instruction here, but hard to do it portably and doesn't really matter I think: PyEval_InitThreads() should be very fast, and this is only run at start-up anyway. */ } } #endif #if PY_MAJOR_VERSION >= 3 /* Python 3: call Py_InitializeEx() */ { PyGILState_STATE state = PyGILState_UNLOCKED; if (!Py_IsInitialized()) _cffi_py_initialize(); else state = PyGILState_Ensure(); PyEval_InitThreads(); PyGILState_Release(state); } #else /* Python 2: call PyEval_InitThreads() */ # ifdef WITH_THREAD if (!PyEval_ThreadsInitialized()) { PyEval_InitThreads(); /* makes the GIL */ PyEval_ReleaseLock(); /* then release it */ } /* else: there is already a GIL, but we still needed to do the spinlock dance to make sure that we see it as fully ready */ # endif #endif #ifdef WITH_THREAD /* release the lock */ while (!cffi_compare_and_swap(lock, old_value + 1, old_value)) ; #endif return 0; } /********** end CPython-specific section **********/ #else /********** PyPy-specific section **********/ PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ static struct _cffi_pypy_init_s { const char *name; void (*func)(const void *[]); const char *code; } _cffi_pypy_init = { _CFFI_MODULE_NAME, _CFFI_PYTHON_STARTUP_FUNC, _CFFI_PYTHON_STARTUP_CODE, }; extern int pypy_carefully_make_gil(const char *); extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); static int _cffi_carefully_make_gil(void) { return pypy_carefully_make_gil(_CFFI_MODULE_NAME); } static int _cffi_initialize_python(void) { return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); } /********** end PyPy-specific section **********/ #endif #ifdef __GNUC__ __attribute__((noinline)) #endif static _cffi_call_python_fnptr _cffi_start_python(void) { /* Delicate logic to initialize Python. This function can be called multiple times concurrently, e.g. when the process calls its first ``extern "Python"`` functions in multiple threads at once. It can also be called recursively, in which case we must ignore it. We also have to consider what occurs if several different cffi-based extensions reach this code in parallel threads---it is a different copy of the code, then, and we can't have any shared global variable unless it comes from 'libpythonX.Y.so'. Idea: * _cffi_carefully_make_gil(): "carefully" call PyEval_InitThreads() (possibly with Py_InitializeEx() first). * then we use a (local) custom lock to make sure that a call to this cffi-based extension will wait if another call to the *same* extension is running the initialization in another thread. It is reentrant, so that a recursive call will not block, but only one from a different thread. * then we grab the GIL and (Python 2) we call Py_InitializeEx(). At this point, concurrent calls to Py_InitializeEx() are not possible: we have the GIL. * do the rest of the specific initialization, which may temporarily release the GIL but not the custom lock. Only release the custom lock when we are done. */ static char called = 0; if (_cffi_carefully_make_gil() != 0) return NULL; _cffi_acquire_reentrant_mutex(); /* Here the GIL exists, but we don't have it. We're only protected from concurrency by the reentrant mutex. */ /* This file only initializes the embedded module once, the first time this is called, even if there are subinterpreters. */ if (!called) { called = 1; /* invoke _cffi_initialize_python() only once, but don't set '_cffi_call_python' right now, otherwise concurrent threads won't call this function at all (we need them to wait) */ if (_cffi_initialize_python() == 0) { /* now initialization is finished. Switch to the fast-path. */ /* We would like nobody to see the new value of '_cffi_call_python' without also seeing the rest of the data initialized. However, this is not possible. But the new value of '_cffi_call_python' is the function 'cffi_call_python()' from _cffi_backend. So: */ cffi_write_barrier(); /* ^^^ we put a write barrier here, and a corresponding read barrier at the start of cffi_call_python(). This ensures that after that read barrier, we see everything done here before the write barrier. */ assert(_cffi_call_python_org != NULL); _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; } else { /* initialization failed. Reset this to NULL, even if it was already set to some other value. Future calls to _cffi_start_python() are still forced to occur, and will always return NULL from now on. */ _cffi_call_python_org = NULL; } } _cffi_release_reentrant_mutex(); return (_cffi_call_python_fnptr)_cffi_call_python_org; } static void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) { _cffi_call_python_fnptr fnptr; int current_err = errno; #ifdef _MSC_VER int current_lasterr = GetLastError(); #endif fnptr = _cffi_start_python(); if (fnptr == NULL) { fprintf(stderr, "function %s() called, but initialization code " "failed. Returning 0.\n", externpy->name); memset(args, 0, externpy->size_of_result); } #ifdef _MSC_VER SetLastError(current_lasterr); #endif errno = current_err; if (fnptr != NULL) fnptr(externpy, args); } /* The cffi_start_python() function makes sure Python is initialized and our cffi module is set up. It can be called manually from the user C code. The same effect is obtained automatically from any dll-exported ``extern "Python"`` function. This function returns -1 if initialization failed, 0 if all is OK. */ _CFFI_UNUSED_FN static int cffi_start_python(void) { if (_cffi_call_python == &_cffi_start_and_call_python) { if (_cffi_start_python() == NULL) return -1; } cffi_read_barrier(); return 0; } #undef cffi_compare_and_swap #undef cffi_write_barrier #undef cffi_read_barrier cffi-1.5.2/cffi/__init__.py0000664000175000017500000000074312657646311015713 0ustar arigoarigo00000000000000__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', 'FFIError'] from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing __version__ = "1.5.2" __version_info__ = (1, 5, 2) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ # if nothing is clearly incompatible. __version_verifier_modules__ = "0.8.6" cffi-1.5.2/cffi/setuptools_ext.py0000664000175000017500000001401612657646311017253 0ustar arigoarigo00000000000000import os try: basestring except NameError: # Python 3.x basestring = str def error(msg): from distutils.errors import DistutilsSetupError raise DistutilsSetupError(msg) def execfile(filename, glob): # We use execfile() (here rewritten for Python 3) instead of # __import__() to load the build script. The problem with # a normal import is that in some packages, the intermediate # __init__.py files may already try to import the file that # we are generating. with open(filename) as f: src = f.read() src += '\n' # Python 2.6 compatibility code = compile(src, filename, 'exec') exec(code, glob, glob) def add_cffi_module(dist, mod_spec): from cffi.api import FFI if not isinstance(mod_spec, basestring): error("argument to 'cffi_modules=...' must be a str or a list of str," " not %r" % (type(mod_spec).__name__,)) mod_spec = str(mod_spec) try: build_file_name, ffi_var_name = mod_spec.split(':') except ValueError: error("%r must be of the form 'path/build.py:ffi_variable'" % (mod_spec,)) if not os.path.exists(build_file_name): ext = '' rewritten = build_file_name.replace('.', '/') + '.py' if os.path.exists(rewritten): ext = ' (rewrite cffi_modules to [%r])' % ( rewritten + ':' + ffi_var_name,) error("%r does not name an existing file%s" % (build_file_name, ext)) mod_vars = {'__name__': '__cffi__', '__file__': build_file_name} execfile(build_file_name, mod_vars) try: ffi = mod_vars[ffi_var_name] except KeyError: error("%r: object %r not found in module" % (mod_spec, ffi_var_name)) if not isinstance(ffi, FFI): ffi = ffi() # maybe it's a function instead of directly an ffi if not isinstance(ffi, FFI): error("%r is not an FFI instance (got %r)" % (mod_spec, type(ffi).__name__)) if not hasattr(ffi, '_assigned_source'): error("%r: the set_source() method was not called" % (mod_spec,)) module_name, source, source_extension, kwds = ffi._assigned_source if ffi._windows_unicode: kwds = kwds.copy() ffi._apply_windows_unicode(kwds) if source is None: _add_py_module(dist, ffi, module_name) else: _add_c_module(dist, ffi, module_name, source, source_extension, kwds) def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): from distutils.core import Extension from distutils.command.build_ext import build_ext from distutils.dir_util import mkpath from distutils import log from cffi import recompiler allsources = ['$PLACEHOLDER'] allsources.extend(kwds.pop('sources', [])) ext = Extension(name=module_name, sources=allsources, **kwds) def make_mod(tmpdir, pre_run=None): c_file = os.path.join(tmpdir, module_name + source_extension) log.info("generating cffi module %r" % c_file) mkpath(tmpdir) # a setuptools-only, API-only hook: called with the "ext" and "ffi" # arguments just before we turn the ffi into C code. To use it, # subclass the 'distutils.command.build_ext.build_ext' class and # add a method 'def pre_run(self, ext, ffi)'. if pre_run is not None: pre_run(ext, ffi) updated = recompiler.make_c_source(ffi, module_name, source, c_file) if not updated: log.info("already up-to-date") return c_file if dist.ext_modules is None: dist.ext_modules = [] dist.ext_modules.append(ext) base_class = dist.cmdclass.get('build_ext', build_ext) class build_ext_make_mod(base_class): def run(self): if ext.sources[0] == '$PLACEHOLDER': pre_run = getattr(self, 'pre_run', None) ext.sources[0] = make_mod(self.build_temp, pre_run) base_class.run(self) dist.cmdclass['build_ext'] = build_ext_make_mod # NB. multiple runs here will create multiple 'build_ext_make_mod' # classes. Even in this case the 'build_ext' command should be # run once; but just in case, the logic above does nothing if # called again. def _add_py_module(dist, ffi, module_name): from distutils.dir_util import mkpath from distutils.command.build_py import build_py from distutils.command.build_ext import build_ext from distutils import log from cffi import recompiler def generate_mod(py_file): log.info("generating cffi module %r" % py_file) mkpath(os.path.dirname(py_file)) updated = recompiler.make_py_source(ffi, module_name, py_file) if not updated: log.info("already up-to-date") base_class = dist.cmdclass.get('build_py', build_py) class build_py_make_mod(base_class): def run(self): base_class.run(self) module_path = module_name.split('.') module_path[-1] += '.py' generate_mod(os.path.join(self.build_lib, *module_path)) dist.cmdclass['build_py'] = build_py_make_mod # the following is only for "build_ext -i" base_class_2 = dist.cmdclass.get('build_ext', build_ext) class build_ext_make_mod(base_class_2): def run(self): base_class_2.run(self) if self.inplace: # from get_ext_fullpath() in distutils/command/build_ext.py module_path = module_name.split('.') package = '.'.join(module_path[:-1]) build_py = self.get_finalized_command('build_py') package_dir = build_py.get_package_dir(package) file_name = module_path[-1] + '.py' generate_mod(os.path.join(package_dir, file_name)) dist.cmdclass['build_ext'] = build_ext_make_mod def cffi_modules(dist, attr, value): assert attr == 'cffi_modules' if isinstance(value, basestring): value = [value] for cffi_module in value: add_cffi_module(dist, cffi_module) cffi-1.5.2/cffi/cffi_opcode.py0000664000175000017500000001254512657646311016417 0ustar arigoarigo00000000000000 class CffiOp(object): def __init__(self, op, arg): self.op = op self.arg = arg def as_c_expr(self): if self.op is None: assert isinstance(self.arg, str) return '(_cffi_opcode_t)(%s)' % (self.arg,) classname = CLASS_NAME[self.op] return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg) def as_python_bytes(self): if self.op is None and self.arg.isdigit(): value = int(self.arg) # non-negative: '-' not in self.arg if value >= 2**31: raise OverflowError("cannot emit %r: limited to 2**31-1" % (self.arg,)) return format_four_bytes(value) if isinstance(self.arg, str): from .ffiplatform import VerificationError raise VerificationError("cannot emit to Python: %r" % (self.arg,)) return format_four_bytes((self.arg << 8) | self.op) def __str__(self): classname = CLASS_NAME.get(self.op, self.op) return '(%s %s)' % (classname, self.arg) def format_four_bytes(num): return '\\x%02X\\x%02X\\x%02X\\x%02X' % ( (num >> 24) & 0xFF, (num >> 16) & 0xFF, (num >> 8) & 0xFF, (num ) & 0xFF) OP_PRIMITIVE = 1 OP_POINTER = 3 OP_ARRAY = 5 OP_OPEN_ARRAY = 7 OP_STRUCT_UNION = 9 OP_ENUM = 11 OP_FUNCTION = 13 OP_FUNCTION_END = 15 OP_NOOP = 17 OP_BITFIELD = 19 OP_TYPENAME = 21 OP_CPYTHON_BLTN_V = 23 # varargs OP_CPYTHON_BLTN_N = 25 # noargs OP_CPYTHON_BLTN_O = 27 # O (i.e. a single arg) OP_CONSTANT = 29 OP_CONSTANT_INT = 31 OP_GLOBAL_VAR = 33 OP_DLOPEN_FUNC = 35 OP_DLOPEN_CONST = 37 OP_GLOBAL_VAR_F = 39 OP_EXTERN_PYTHON = 41 PRIM_VOID = 0 PRIM_BOOL = 1 PRIM_CHAR = 2 PRIM_SCHAR = 3 PRIM_UCHAR = 4 PRIM_SHORT = 5 PRIM_USHORT = 6 PRIM_INT = 7 PRIM_UINT = 8 PRIM_LONG = 9 PRIM_ULONG = 10 PRIM_LONGLONG = 11 PRIM_ULONGLONG = 12 PRIM_FLOAT = 13 PRIM_DOUBLE = 14 PRIM_LONGDOUBLE = 15 PRIM_WCHAR = 16 PRIM_INT8 = 17 PRIM_UINT8 = 18 PRIM_INT16 = 19 PRIM_UINT16 = 20 PRIM_INT32 = 21 PRIM_UINT32 = 22 PRIM_INT64 = 23 PRIM_UINT64 = 24 PRIM_INTPTR = 25 PRIM_UINTPTR = 26 PRIM_PTRDIFF = 27 PRIM_SIZE = 28 PRIM_SSIZE = 29 PRIM_INT_LEAST8 = 30 PRIM_UINT_LEAST8 = 31 PRIM_INT_LEAST16 = 32 PRIM_UINT_LEAST16 = 33 PRIM_INT_LEAST32 = 34 PRIM_UINT_LEAST32 = 35 PRIM_INT_LEAST64 = 36 PRIM_UINT_LEAST64 = 37 PRIM_INT_FAST8 = 38 PRIM_UINT_FAST8 = 39 PRIM_INT_FAST16 = 40 PRIM_UINT_FAST16 = 41 PRIM_INT_FAST32 = 42 PRIM_UINT_FAST32 = 43 PRIM_INT_FAST64 = 44 PRIM_UINT_FAST64 = 45 PRIM_INTMAX = 46 PRIM_UINTMAX = 47 _NUM_PRIM = 48 _UNKNOWN_PRIM = -1 _UNKNOWN_FLOAT_PRIM = -2 _UNKNOWN_LONG_DOUBLE = -3 _IO_FILE_STRUCT = -1 PRIMITIVE_TO_INDEX = { 'char': PRIM_CHAR, 'short': PRIM_SHORT, 'int': PRIM_INT, 'long': PRIM_LONG, 'long long': PRIM_LONGLONG, 'signed char': PRIM_SCHAR, 'unsigned char': PRIM_UCHAR, 'unsigned short': PRIM_USHORT, 'unsigned int': PRIM_UINT, 'unsigned long': PRIM_ULONG, 'unsigned long long': PRIM_ULONGLONG, 'float': PRIM_FLOAT, 'double': PRIM_DOUBLE, 'long double': PRIM_LONGDOUBLE, '_Bool': PRIM_BOOL, 'wchar_t': PRIM_WCHAR, 'int8_t': PRIM_INT8, 'uint8_t': PRIM_UINT8, 'int16_t': PRIM_INT16, 'uint16_t': PRIM_UINT16, 'int32_t': PRIM_INT32, 'uint32_t': PRIM_UINT32, 'int64_t': PRIM_INT64, 'uint64_t': PRIM_UINT64, 'intptr_t': PRIM_INTPTR, 'uintptr_t': PRIM_UINTPTR, 'ptrdiff_t': PRIM_PTRDIFF, 'size_t': PRIM_SIZE, 'ssize_t': PRIM_SSIZE, 'int_least8_t': PRIM_INT_LEAST8, 'uint_least8_t': PRIM_UINT_LEAST8, 'int_least16_t': PRIM_INT_LEAST16, 'uint_least16_t': PRIM_UINT_LEAST16, 'int_least32_t': PRIM_INT_LEAST32, 'uint_least32_t': PRIM_UINT_LEAST32, 'int_least64_t': PRIM_INT_LEAST64, 'uint_least64_t': PRIM_UINT_LEAST64, 'int_fast8_t': PRIM_INT_FAST8, 'uint_fast8_t': PRIM_UINT_FAST8, 'int_fast16_t': PRIM_INT_FAST16, 'uint_fast16_t': PRIM_UINT_FAST16, 'int_fast32_t': PRIM_INT_FAST32, 'uint_fast32_t': PRIM_UINT_FAST32, 'int_fast64_t': PRIM_INT_FAST64, 'uint_fast64_t': PRIM_UINT_FAST64, 'intmax_t': PRIM_INTMAX, 'uintmax_t': PRIM_UINTMAX, } F_UNION = 0x01 F_CHECK_FIELDS = 0x02 F_PACKED = 0x04 F_EXTERNAL = 0x08 F_OPAQUE = 0x10 G_FLAGS = dict([('_CFFI_' + _key, globals()[_key]) for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED', 'F_EXTERNAL', 'F_OPAQUE']]) CLASS_NAME = {} for _name, _value in list(globals().items()): if _name.startswith('OP_') and isinstance(_value, int): CLASS_NAME[_value] = _name[3:] cffi-1.5.2/cffi/_cffi_include.h0000664000175000017500000002343512657646311016527 0ustar arigoarigo00000000000000#define _CFFI_ #include #ifdef __cplusplus extern "C" { #endif #include #include "parse_c_type.h" /* this block of #ifs should be kept exactly identical between c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py and cffi/_cffi_include.h */ #if defined(_MSC_VER) # include /* for alloca() */ # if _MSC_VER < 1600 /* MSVC < 2010 */ 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 __int8 int_least8_t; typedef __int16 int_least16_t; typedef __int32 int_least32_t; typedef __int64 int_least64_t; typedef unsigned __int8 uint_least8_t; typedef unsigned __int16 uint_least16_t; typedef unsigned __int32 uint_least32_t; typedef unsigned __int64 uint_least64_t; typedef __int8 int_fast8_t; typedef __int16 int_fast16_t; typedef __int32 int_fast32_t; typedef __int64 int_fast64_t; typedef unsigned __int8 uint_fast8_t; typedef unsigned __int16 uint_fast16_t; typedef unsigned __int32 uint_fast32_t; typedef unsigned __int64 uint_fast64_t; typedef __int64 intmax_t; typedef unsigned __int64 uintmax_t; # else # include # endif # if _MSC_VER < 1800 /* MSVC < 2013 */ typedef unsigned char _Bool; # endif #else # include # if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) # include # endif #endif #ifdef __GNUC__ # define _CFFI_UNUSED_FN __attribute__((unused)) #else # define _CFFI_UNUSED_FN /* nothing */ #endif /********** CPython-specific section **********/ #ifndef PYPY_VERSION #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((long)x) : \ sizeof(type) == sizeof(long) ? \ PyLong_FromUnsignedLong((unsigned long)x) : \ PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ (sizeof(type) <= sizeof(long) ? \ PyInt_FromLong((long)x) : \ PyLong_FromLongLong((long long)x))) #define _cffi_to_c_int(o, type) \ ((type)( \ sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ : (type)_cffi_to_c_i8(o)) : \ sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ : (type)_cffi_to_c_i16(o)) : \ sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ : (type)_cffi_to_c_i32(o)) : \ sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ : (type)_cffi_to_c_i64(o)) : \ (Py_FatalError("unsupported size for type " #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 \ not used any more #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_CPIDX 25 #define _cffi_call_python \ ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) #define _CFFI_NUM_EXPORTS 26 typedef struct _ctypedescr CTypeDescrObject; static void *_cffi_exports[_CFFI_NUM_EXPORTS]; #define _cffi_type(index) ( \ assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ (CTypeDescrObject *)_cffi_types[index]) static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, const struct _cffi_type_context_s *ctx) { PyObject *module, *o_arg, *new_module; void *raw[] = { (void *)module_name, (void *)version, (void *)_cffi_exports, (void *)ctx, }; module = PyImport_ImportModule("_cffi_backend"); if (module == NULL) goto failure; o_arg = PyLong_FromVoidPtr((void *)raw); if (o_arg == NULL) goto failure; new_module = PyObject_CallMethod( module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg); Py_DECREF(o_arg); Py_DECREF(module); return new_module; failure: Py_XDECREF(module); return NULL; } _CFFI_UNUSED_FN static PyObject **_cffi_unpack_args(PyObject *args_tuple, Py_ssize_t expected, const char *fnname) { if (PyTuple_GET_SIZE(args_tuple) != expected) { PyErr_Format(PyExc_TypeError, "%.150s() takes exactly %zd arguments (%zd given)", fnname, expected, PyTuple_GET_SIZE(args_tuple)); return NULL; } return &PyTuple_GET_ITEM(args_tuple, 0); /* pointer to the first item, the others follow */ } /********** end CPython-specific section **********/ #else _CFFI_UNUSED_FN static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *); # define _cffi_call_python _cffi_call_python_org #endif #define _cffi_array_len(array) (sizeof(array) / sizeof((array)[0])) #define _cffi_prim_int(size, sign) \ ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8 : _CFFI_PRIM_UINT8) : \ (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \ (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \ (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \ _CFFI__UNKNOWN_PRIM) #define _cffi_prim_float(size) \ ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \ (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \ (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \ _CFFI__UNKNOWN_FLOAT_PRIM) #define _cffi_check_int(got, got_nonpos, expected) \ ((got_nonpos) == (expected <= 0) && \ (got) == (unsigned long long)expected) #ifdef MS_WIN32 # define _cffi_stdcall __stdcall #else # define _cffi_stdcall /* nothing */ #endif #ifdef __cplusplus } #endif cffi-1.5.2/cffi/verifier.py0000664000175000017500000002637612657646311016001 0ustar arigoarigo00000000000000# # DEPRECATED: implementation for ffi.verify() # import sys, os, binascii, shutil, io from . import __version_verifier_modules__ from . import ffiplatform if sys.version_info >= (3, 3): import importlib.machinery def _extension_suffixes(): return importlib.machinery.EXTENSION_SUFFIXES[:] else: import imp def _extension_suffixes(): return [suffix for suffix, _, type in imp.get_suffixes() if type == imp.C_EXTENSION] if sys.version_info >= (3,): NativeIO = io.StringIO else: class NativeIO(io.BytesIO): def write(self, s): if isinstance(s, unicode): s = s.encode('ascii') super(NativeIO, self).write(s) def _hack_at_distutils(): # Windows-only workaround for some configurations: see # https://bugs.python.org/issue23246 (Python 2.7 with # a specific MS compiler suite download) if sys.platform == "win32": try: import setuptools # for side-effects, patches distutils except ImportError: pass class Verifier(object): def __init__(self, ffi, preamble, tmpdir=None, modulename=None, ext_package=None, tag='', force_generic_engine=False, source_extension='.c', flags=None, relative_to=None, **kwds): if ffi._parser._uses_new_feature: raise ffiplatform.VerificationError( "feature not supported with ffi.verify(), but only " "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,)) 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.flags = flags self.kwds = self.make_relative_to(kwds, relative_to) # if modulename: if tag: raise TypeError("can't specify both 'modulename' and 'tag'") else: key = '\x00'.join([sys.version[:3], __version_verifier_modules__, 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 + source_extension) 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): _hack_at_distutils() # backward compatibility hack 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 make_relative_to(self, kwds, relative_to): if relative_to and os.path.dirname(relative_to): dirname = os.path.dirname(relative_to) kwds = kwds.copy() for key in ffiplatform.LIST_OF_FILE_NAMES: if key in kwds: lst = kwds[key] if not isinstance(lst, (list, tuple)): raise TypeError("keyword '%s' should be a list or tuple" % (key,)) lst = [os.path.join(dirname, fn) for fn in lst] kwds[key] = lst return kwds # ---------- 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_to(self, file): self._vengine._f = file try: self._vengine.write_source_to_f() finally: del self._vengine._f def _write_source(self, file=None): if file is not None: self._write_source_to(file) else: # Write our source file to an in memory file. f = NativeIO() self._write_source_to(f) source_data = f.getvalue() # Determine if this matches the current file if os.path.exists(self.sourcefilename): with open(self.sourcefilename, "r") as fp: needs_written = not (fp.read() == source_data) else: needs_written = True # Actually write the file out if it doesn't match if needs_written: _ensure_dir(self.sourcefilename) with open(self.sourcefilename, "w") as fp: fp.write(source_data) # Set this flag 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 if self.flags is not None: return self._vengine.load_library(self.flags) else: 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 result = os.environ.get('CFFI_TMPDIR') if result: return result 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 = _extension_suffixes() 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-1.5.2/cffi/recompiler.py0000664000175000017500000016521412657646311016322 0ustar arigoarigo00000000000000import os, sys, io from . import ffiplatform, model from .cffi_opcode import * VERSION = "0x2601" VERSION_EMBEDDED = "0x2701" class GlobalExpr: def __init__(self, name, address, type_op, size=0, check_value=0): self.name = name self.address = address self.type_op = type_op self.size = size self.check_value = check_value def as_c_expr(self): return ' { "%s", (void *)%s, %s, (void *)%s },' % ( self.name, self.address, self.type_op.as_c_expr(), self.size) def as_python_expr(self): return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, self.check_value) class FieldExpr: def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): self.name = name self.field_offset = field_offset self.field_size = field_size self.fbitsize = fbitsize self.field_type_op = field_type_op def as_c_expr(self): spaces = " " * len(self.name) return (' { "%s", %s,\n' % (self.name, self.field_offset) + ' %s %s,\n' % (spaces, self.field_size) + ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) def as_python_expr(self): raise NotImplementedError def as_field_python_expr(self): if self.field_type_op.op == OP_NOOP: size_expr = '' elif self.field_type_op.op == OP_BITFIELD: size_expr = format_four_bytes(self.fbitsize) else: raise NotImplementedError return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), size_expr, self.name) class StructUnionExpr: def __init__(self, name, type_index, flags, size, alignment, comment, first_field_index, c_fields): self.name = name self.type_index = type_index self.flags = flags self.size = size self.alignment = alignment self.comment = comment self.first_field_index = first_field_index self.c_fields = c_fields def as_c_expr(self): return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) + '\n %s, %s, ' % (self.size, self.alignment) + '%d, %d ' % (self.first_field_index, len(self.c_fields)) + ('/* %s */ ' % self.comment if self.comment else '') + '},') def as_python_expr(self): flags = eval(self.flags, G_FLAGS) fields_expr = [c_field.as_field_python_expr() for c_field in self.c_fields] return "(b'%s%s%s',%s)" % ( format_four_bytes(self.type_index), format_four_bytes(flags), self.name, ','.join(fields_expr)) class EnumExpr: def __init__(self, name, type_index, size, signed, allenums): self.name = name self.type_index = type_index self.size = size self.signed = signed self.allenums = allenums def as_c_expr(self): return (' { "%s", %d, _cffi_prim_int(%s, %s),\n' ' "%s" },' % (self.name, self.type_index, self.size, self.signed, self.allenums)) def as_python_expr(self): prim_index = { (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, }[self.size, self.signed] return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), format_four_bytes(prim_index), self.name, self.allenums) class TypenameExpr: def __init__(self, name, type_index): self.name = name self.type_index = type_index def as_c_expr(self): return ' { "%s", %d },' % (self.name, self.type_index) def as_python_expr(self): return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) # ____________________________________________________________ class Recompiler: _num_externpy = 0 def __init__(self, ffi, module_name, target_is_python=False): self.ffi = ffi self.module_name = module_name self.target_is_python = target_is_python def collect_type_table(self): self._typesdict = {} self._generate("collecttype") # all_decls = sorted(self._typesdict, key=str) # # prepare all FUNCTION bytecode sequences first self.cffi_types = [] for tp in all_decls: if tp.is_raw_function: assert self._typesdict[tp] is None self._typesdict[tp] = len(self.cffi_types) self.cffi_types.append(tp) # placeholder for tp1 in tp.args: assert isinstance(tp1, (model.VoidType, model.BasePrimitiveType, model.PointerType, model.StructOrUnionOrEnum, model.FunctionPtrType)) if self._typesdict[tp1] is None: self._typesdict[tp1] = len(self.cffi_types) self.cffi_types.append(tp1) # placeholder self.cffi_types.append('END') # placeholder # # prepare all OTHER bytecode sequences for tp in all_decls: if not tp.is_raw_function and self._typesdict[tp] is None: self._typesdict[tp] = len(self.cffi_types) self.cffi_types.append(tp) # placeholder if tp.is_array_type and tp.length is not None: self.cffi_types.append('LEN') # placeholder assert None not in self._typesdict.values() # # collect all structs and unions and enums self._struct_unions = {} self._enums = {} for tp in all_decls: if isinstance(tp, model.StructOrUnion): self._struct_unions[tp] = None elif isinstance(tp, model.EnumType): self._enums[tp] = None for i, tp in enumerate(sorted(self._struct_unions, key=lambda tp: tp.name)): self._struct_unions[tp] = i for i, tp in enumerate(sorted(self._enums, key=lambda tp: tp.name)): self._enums[tp] = i # # emit all bytecode sequences now for tp in all_decls: method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) method(tp, self._typesdict[tp]) # # consistency check for op in self.cffi_types: assert isinstance(op, CffiOp) self.cffi_types = tuple(self.cffi_types) # don't change any more def _do_collect_type(self, tp): if not isinstance(tp, model.BaseTypeByIdentity): if isinstance(tp, tuple): for x in tp: self._do_collect_type(x) return if tp not in self._typesdict: self._typesdict[tp] = None if isinstance(tp, model.FunctionPtrType): self._do_collect_type(tp.as_raw_function()) elif isinstance(tp, model.StructOrUnion): if tp.fldtypes is not None and ( tp not in self.ffi._parser._included_declarations): for name1, tp1, _, _ in tp.enumfields(): self._do_collect_type(self._field_type(tp, name1, tp1)) else: for _, x in tp._get_items(): self._do_collect_type(x) def _generate(self, step_name): lst = self.ffi._parser._declarations.items() for name, (tp, quals) in sorted(lst): kind, realname = name.split(' ', 1) try: method = getattr(self, '_generate_cpy_%s_%s' % (kind, step_name)) except AttributeError: raise ffiplatform.VerificationError( "not implemented in recompile(): %r" % name) try: self._current_quals = quals method(tp, realname) except Exception as e: model.attach_exception_info(e, name) raise # ---------- ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] def collect_step_tables(self): # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. self._lsts = {} for step_name in self.ALL_STEPS: self._lsts[step_name] = [] self._seen_struct_unions = set() self._generate("ctx") self._add_missing_struct_unions() # for step_name in self.ALL_STEPS: lst = self._lsts[step_name] if step_name != "field": lst.sort(key=lambda entry: entry.name) self._lsts[step_name] = tuple(lst) # don't change any more # # check for a possible internal inconsistency: _cffi_struct_unions # should have been generated with exactly self._struct_unions lst = self._lsts["struct_union"] for tp, i in self._struct_unions.items(): assert i < len(lst) assert lst[i].name == tp.name assert len(lst) == len(self._struct_unions) # same with enums lst = self._lsts["enum"] for tp, i in self._enums.items(): assert i < len(lst) assert lst[i].name == tp.name assert len(lst) == len(self._enums) # ---------- def _prnt(self, what=''): self._f.write(what + '\n') def write_source_to_f(self, f, preamble): if self.target_is_python: assert preamble is None self.write_py_source_to_f(f) else: assert preamble is not None self.write_c_source_to_f(f, preamble) def _rel_readlines(self, filename): g = open(os.path.join(os.path.dirname(__file__), filename), 'r') lines = g.readlines() g.close() return lines def write_c_source_to_f(self, f, preamble): self._f = f prnt = self._prnt # # first the '#include' (actually done by inlining the file's content) lines = self._rel_readlines('_cffi_include.h') i = lines.index('#include "parse_c_type.h"\n') lines[i:i+1] = self._rel_readlines('parse_c_type.h') prnt(''.join(lines)) # # if we have ffi._embedding != None, we give it here as a macro # and include an extra file base_module_name = self.module_name.split('.')[-1] if self.ffi._embedding is not None: prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) prnt('#define _CFFI_PYTHON_STARTUP_CODE %s' % (self._string_literal(self.ffi._embedding),)) prnt('#ifdef PYPY_VERSION') prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( base_module_name,)) prnt('#elif PY_MAJOR_VERSION >= 3') prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( base_module_name,)) prnt('#else') prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % ( base_module_name,)) prnt('#endif') lines = self._rel_readlines('_embedding.h') prnt(''.join(lines)) version = VERSION_EMBEDDED else: version = VERSION # # then paste the C source given by the user, verbatim. prnt('/************************************************************/') prnt() prnt(preamble) prnt() prnt('/************************************************************/') prnt() # # the declaration of '_cffi_types' prnt('static void *_cffi_types[] = {') typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) for i, op in enumerate(self.cffi_types): comment = '' if i in typeindex2type: comment = ' // ' + typeindex2type[i]._get_c_name() prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) if not self.cffi_types: prnt(' 0') prnt('};') prnt() # # call generate_cpy_xxx_decl(), for every xxx found from # ffi._parser._declarations. This generates all the functions. self._seen_constants = set() self._generate("decl") # # the declaration of '_cffi_globals' and '_cffi_typenames' nums = {} for step_name in self.ALL_STEPS: lst = self._lsts[step_name] nums[step_name] = len(lst) if nums[step_name] > 0: prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( step_name, step_name)) for entry in lst: prnt(entry.as_c_expr()) prnt('};') prnt() # # the declaration of '_cffi_includes' if self.ffi._included_ffis: prnt('static const char * const _cffi_includes[] = {') for ffi_to_include in self.ffi._included_ffis: try: included_module_name, included_source = ( ffi_to_include._assigned_source[:2]) except AttributeError: raise ffiplatform.VerificationError( "ffi object %r includes %r, but the latter has not " "been prepared with set_source()" % ( self.ffi, ffi_to_include,)) if included_source is None: raise ffiplatform.VerificationError( "not implemented yet: ffi.include() of a Python-based " "ffi inside a C-based ffi") prnt(' "%s",' % (included_module_name,)) prnt(' NULL') prnt('};') prnt() # # the declaration of '_cffi_type_context' prnt('static const struct _cffi_type_context_s _cffi_type_context = {') prnt(' _cffi_types,') for step_name in self.ALL_STEPS: if nums[step_name] > 0: prnt(' _cffi_%ss,' % step_name) else: prnt(' NULL, /* no %ss */' % step_name) for step_name in self.ALL_STEPS: if step_name != "field": prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) if self.ffi._included_ffis: prnt(' _cffi_includes,') else: prnt(' NULL, /* no includes */') prnt(' %d, /* num_types */' % (len(self.cffi_types),)) flags = 0 if self._num_externpy: flags |= 1 # set to mean that we use extern "Python" prnt(' %d, /* flags */' % flags) prnt('};') prnt() # # the init function prnt('#ifdef PYPY_VERSION') prnt('PyMODINIT_FUNC') prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) prnt('{') if self._num_externpy: prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') prnt(' _cffi_call_python_org = ' '(void(*)(struct _cffi_externpy_s *, char *))p[1];') prnt(' }') prnt(' p[0] = (const void *)%s;' % version) prnt(' p[1] = &_cffi_type_context;') prnt('}') # on Windows, distutils insists on putting init_cffi_xyz in # 'export_symbols', so instead of fighting it, just give up and # give it one prnt('# ifdef _MSC_VER') prnt(' PyMODINIT_FUNC') prnt('# if PY_MAJOR_VERSION >= 3') prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) prnt('# else') prnt(' init%s(void) { }' % (base_module_name,)) prnt('# endif') prnt('# endif') prnt('#elif PY_MAJOR_VERSION >= 3') prnt('PyMODINIT_FUNC') prnt('PyInit_%s(void)' % (base_module_name,)) prnt('{') prnt(' return _cffi_init("%s", %s, &_cffi_type_context);' % ( self.module_name, version)) prnt('}') prnt('#else') prnt('PyMODINIT_FUNC') prnt('init%s(void)' % (base_module_name,)) prnt('{') prnt(' _cffi_init("%s", %s, &_cffi_type_context);' % ( self.module_name, version)) prnt('}') prnt('#endif') def _to_py(self, x): if isinstance(x, str): return "b'%s'" % (x,) if isinstance(x, (list, tuple)): rep = [self._to_py(item) for item in x] if len(rep) == 1: rep.append('') return "(%s)" % (','.join(rep),) return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. def write_py_source_to_f(self, f): self._f = f prnt = self._prnt # # header prnt("# auto-generated file") prnt("import _cffi_backend") # # the 'import' of the included ffis num_includes = len(self.ffi._included_ffis or ()) for i in range(num_includes): ffi_to_include = self.ffi._included_ffis[i] try: included_module_name, included_source = ( ffi_to_include._assigned_source[:2]) except AttributeError: raise ffiplatform.VerificationError( "ffi object %r includes %r, but the latter has not " "been prepared with set_source()" % ( self.ffi, ffi_to_include,)) if included_source is not None: raise ffiplatform.VerificationError( "not implemented yet: ffi.include() of a C-based " "ffi inside a Python-based ffi") prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) prnt() prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) prnt(" _version = %s," % (VERSION,)) # # the '_types' keyword argument self.cffi_types = tuple(self.cffi_types) # don't change any more types_lst = [op.as_python_bytes() for op in self.cffi_types] prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) # # the keyword arguments from ALL_STEPS for step_name in self.ALL_STEPS: lst = self._lsts[step_name] if len(lst) > 0 and step_name != "field": prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) # # the '_includes' keyword argument if num_includes > 0: prnt(' _includes = (%s,),' % ( ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) # # the footer prnt(')') # ---------- def _gettypenum(self, type): # a KeyError here is a bug. please report it! :-) return self._typesdict[type] def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): extraarg = '' if isinstance(tp, model.BasePrimitiveType): if tp.is_integer_type() and tp.name != '_Bool': converter = '_cffi_to_c_int' extraarg = ', %s' % tp.name elif isinstance(tp, model.UnknownFloatType): # don't check with is_float_type(): it may be a 'long # double' here, and _cffi_to_c_double would loose precision converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) else: converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), 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 = (%s)alloca((size_t)datasize);' % ( tovar, tp.get_c_name(''))) self._prnt(' memset((void *)%s, 0, (size_t)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.BasePrimitiveType): if tp.is_integer_type(): return '_cffi_from_c_int(%s, %s)' % (var, tp.name) elif isinstance(tp, model.UnknownFloatType): return '_cffi_from_c_double(%s)' % (var,) 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 def _generate_cpy_typedef_collecttype(self, tp, name): self._do_collect_type(tp) def _generate_cpy_typedef_decl(self, tp, name): pass def _typedef_ctx(self, tp, name): type_index = self._typesdict[tp] self._lsts["typename"].append(TypenameExpr(name, type_index)) def _generate_cpy_typedef_ctx(self, tp, name): self._typedef_ctx(tp, name) if getattr(tp, "origin", None) == "unknown_type": self._struct_ctx(tp, tp.name, approxname=None) elif isinstance(tp, model.NamedPointerType): self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, named_ptr=tp) # ---------- # function declarations def _generate_cpy_function_collecttype(self, tp, name): self._do_collect_type(tp.as_raw_function()) if tp.ellipsis and not self.target_is_python: self._do_collect_type(tp) def _generate_cpy_function_decl(self, tp, name): assert not self.target_is_python 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_constant_decl(tp, name) return prnt = self._prnt numargs = len(tp.args) if numargs == 0: argname = 'noarg' elif numargs == 1: argname = 'arg0' else: argname = 'args' # # ------------------------------ # the 'd' version of the function, only for addressof(lib, 'func') arguments = [] call_arguments = [] context = 'argument of %s' % name for i, type in enumerate(tp.args): arguments.append(type.get_c_name(' x%d' % i, context)) call_arguments.append('x%d' % i) repr_arguments = ', '.join(arguments) repr_arguments = repr_arguments or 'void' if tp.abi: abi = tp.abi + ' ' else: abi = '' name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) prnt('{') call_arguments = ', '.join(call_arguments) result_code = 'return ' if isinstance(tp.result, model.VoidType): result_code = '' prnt(' %s%s(%s);' % (result_code, name, call_arguments)) prnt('}') # prnt('#ifndef PYPY_VERSION') # ------------------------------ # 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): arg = type.get_c_name(' x%d' % i, context) prnt(' %s;' % arg) # 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 result_decl = ' %s;' % tp.result.get_c_name(' result', context) prnt(result_decl) else: result_decl = None result_code = '' # if len(tp.args) > 1: rng = range(len(tp.args)) for i in rng: prnt(' PyObject *arg%d;' % i) prnt(' PyObject **aa;') prnt() prnt(' aa = _cffi_unpack_args(args, %d, "%s");' % (len(rng), name)) prnt(' if (aa == NULL)') prnt(' return NULL;') for i in rng: prnt(' arg%d = aa[%d];' % (i, i)) 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();') call_arguments = ['x%d' % i for i in range(len(tp.args))] call_arguments = ', '.join(call_arguments) prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) prnt(' _cffi_save_errno();') prnt(' Py_END_ALLOW_THREADS') prnt() # prnt(' (void)self; /* unused */') if numargs == 0: prnt(' (void)noarg; /* unused */') 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('#else') # ------------------------------ # # the PyPy version: need to replace struct/union arguments with # pointers, and if the result is a struct/union, insert a first # arg that is a pointer to the result. difference = False arguments = [] call_arguments = [] context = 'argument of %s' % name for i, type in enumerate(tp.args): indirection = '' if isinstance(type, model.StructOrUnion): indirection = '*' difference = True arg = type.get_c_name(' %sx%d' % (indirection, i), context) arguments.append(arg) call_arguments.append('%sx%d' % (indirection, i)) tp_result = tp.result if isinstance(tp_result, model.StructOrUnion): context = 'result of %s' % name arg = tp_result.get_c_name(' *result', context) arguments.insert(0, arg) tp_result = model.void_type result_decl = None result_code = '*result = ' difference = True if difference: repr_arguments = ', '.join(arguments) repr_arguments = repr_arguments or 'void' name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, repr_arguments) prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) prnt('{') if result_decl: prnt(result_decl) call_arguments = ', '.join(call_arguments) prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) if result_decl: prnt(' return result;') prnt('}') else: prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) # prnt('#endif') # ------------------------------ prnt() def _generate_cpy_function_ctx(self, tp, name): if tp.ellipsis and not self.target_is_python: self._generate_cpy_constant_ctx(tp, name) return type_index = self._typesdict[tp.as_raw_function()] numargs = len(tp.args) if self.target_is_python: meth_kind = OP_DLOPEN_FUNC elif numargs == 0: meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' elif numargs == 1: meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' else: meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' self._lsts["global"].append( GlobalExpr(name, '_cffi_f_%s' % name, CffiOp(meth_kind, type_index), size='_cffi_d_%s' % name)) # ---------- # named structs or unions def _field_type(self, tp_struct, field_name, tp_field): if isinstance(tp_field, model.ArrayType): actual_length = tp_field.length if actual_length == '...': ptr_struct_name = tp_struct.get_c_name('*') actual_length = '_cffi_array_len(((%s)0)->%s)' % ( ptr_struct_name, field_name) tp_item = self._field_type(tp_struct, '%s[0]' % field_name, tp_field.item) tp_field = model.ArrayType(tp_item, actual_length) return tp_field def _struct_collecttype(self, tp): self._do_collect_type(tp) def _struct_decl(self, tp, cname, approxname): if tp.fldtypes is None: return prnt = self._prnt checkfuncname = '_cffi_checkfld_%s' % (approxname,) prnt('_CFFI_UNUSED_FN') prnt('static void %s(%s *p)' % (checkfuncname, cname)) prnt('{') prnt(' /* only to generate compile-time warnings or errors */') prnt(' (void)p;') for fname, ftype, fbitsize, fqual in tp.enumfields(): try: if ftype.is_integer_type() or fbitsize >= 0: # accept all integers, but complain on float or double prnt(" (void)((p->%s) << 1); /* check that '%s.%s' is " "an integer */" % (fname, cname, fname)) continue # only accept exactly the type declared, except that '[]' # is interpreted as a '*' and so will match any array length. # (It would also match '*', but that's harder to detect...) while (isinstance(ftype, model.ArrayType) and (ftype.length is None or ftype.length == '...')): ftype = ftype.item fname = fname + '[0]' prnt(' { %s = &p->%s; (void)tmp; }' % ( ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), fname)) except ffiplatform.VerificationError as e: prnt(' /* %s */' % str(e)) # cannot verify it, ignore prnt('}') prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) prnt() def _struct_ctx(self, tp, cname, approxname, named_ptr=None): type_index = self._typesdict[tp] reason_for_not_expanding = None flags = [] if isinstance(tp, model.UnionType): flags.append("_CFFI_F_UNION") if tp.fldtypes is None: flags.append("_CFFI_F_OPAQUE") reason_for_not_expanding = "opaque" if (tp not in self.ffi._parser._included_declarations and (named_ptr is None or named_ptr not in self.ffi._parser._included_declarations)): if tp.fldtypes is None: pass # opaque elif tp.partial or tp.has_anonymous_struct_fields(): pass # field layout obtained silently from the C compiler else: flags.append("_CFFI_F_CHECK_FIELDS") if tp.packed: flags.append("_CFFI_F_PACKED") else: flags.append("_CFFI_F_EXTERNAL") reason_for_not_expanding = "external" flags = '|'.join(flags) or '0' c_fields = [] if reason_for_not_expanding is None: enumfields = list(tp.enumfields()) for fldname, fldtype, fbitsize, fqual in enumfields: fldtype = self._field_type(tp, fldname, fldtype) # cname is None for _add_missing_struct_unions() only op = OP_NOOP if fbitsize >= 0: op = OP_BITFIELD size = '%d /* bits */' % fbitsize elif cname is None or ( isinstance(fldtype, model.ArrayType) and fldtype.length is None): size = '(size_t)-1' else: size = 'sizeof(((%s)0)->%s)' % ( tp.get_c_name('*') if named_ptr is None else named_ptr.name, fldname) if cname is None or fbitsize >= 0: offset = '(size_t)-1' elif named_ptr is not None: offset = '((char *)&((%s)0)->%s) - (char *)0' % ( named_ptr.name, fldname) else: offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) c_fields.append( FieldExpr(fldname, offset, size, fbitsize, CffiOp(op, self._typesdict[fldtype]))) first_field_index = len(self._lsts["field"]) self._lsts["field"].extend(c_fields) # if cname is None: # unknown name, for _add_missing_struct_unions size = '(size_t)-2' align = -2 comment = "unnamed" else: if named_ptr is not None: size = 'sizeof(*(%s)0)' % (named_ptr.name,) align = '-1 /* unknown alignment */' else: size = 'sizeof(%s)' % (cname,) align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) comment = None else: size = '(size_t)-1' align = -1 first_field_index = -1 comment = reason_for_not_expanding self._lsts["struct_union"].append( StructUnionExpr(tp.name, type_index, flags, size, align, comment, first_field_index, c_fields)) self._seen_struct_unions.add(tp) def _add_missing_struct_unions(self): # not very nice, but some struct declarations might be missing # because they don't have any known C name. Check that they are # not partial (we can't complete or verify them!) and emit them # anonymously. lst = list(self._struct_unions.items()) lst.sort(key=lambda tp_order: tp_order[1]) for tp, order in lst: if tp not in self._seen_struct_unions: if tp.partial: raise NotImplementedError("internal inconsistency: %r is " "partial but was not seen at " "this point" % (tp,)) if tp.name.startswith('$') and tp.name[1:].isdigit(): approxname = tp.name[1:] elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': approxname = 'FILE' self._typedef_ctx(tp, 'FILE') else: raise NotImplementedError("internal inconsistency: %r" % (tp,)) self._struct_ctx(tp, None, approxname) def _generate_cpy_struct_collecttype(self, tp, name): self._struct_collecttype(tp) _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype def _struct_names(self, tp): cname = tp.get_c_name('') if ' ' in cname: return cname, cname.replace(' ', '_') else: return cname, '_' + cname def _generate_cpy_struct_decl(self, tp, name): self._struct_decl(tp, *self._struct_names(tp)) _generate_cpy_union_decl = _generate_cpy_struct_decl def _generate_cpy_struct_ctx(self, tp, name): self._struct_ctx(tp, *self._struct_names(tp)) _generate_cpy_union_ctx = _generate_cpy_struct_ctx # ---------- # 'anonymous' declarations. These are produced for anonymous structs # or unions; the 'name' is obtained by a typedef. def _generate_cpy_anonymous_collecttype(self, tp, name): if isinstance(tp, model.EnumType): self._generate_cpy_enum_collecttype(tp, name) else: self._struct_collecttype(tp) def _generate_cpy_anonymous_decl(self, tp, name): if isinstance(tp, model.EnumType): self._generate_cpy_enum_decl(tp) else: self._struct_decl(tp, name, 'typedef_' + name) def _generate_cpy_anonymous_ctx(self, tp, name): if isinstance(tp, model.EnumType): self._enum_ctx(tp, name) else: self._struct_ctx(tp, name, 'typedef_' + name) # ---------- # constants, declared with "static const ..." def _generate_cpy_const(self, is_int, name, tp=None, category='const', check_value=None): if (category, name) in self._seen_constants: raise ffiplatform.VerificationError( "duplicate declaration of %s '%s'" % (category, name)) self._seen_constants.add((category, name)) # prnt = self._prnt funcname = '_cffi_%s_%s' % (category, name) if is_int: prnt('static int %s(unsigned long long *o)' % funcname) prnt('{') prnt(' int n = (%s) <= 0;' % (name,)) prnt(' *o = (unsigned long long)((%s) << 0);' ' /* check that %s is an integer */' % (name, name)) if check_value is not None: if check_value > 0: check_value = '%dU' % (check_value,) prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) prnt(' n |= 2;') prnt(' return n;') prnt('}') else: assert check_value is None prnt('static void %s(char *o)' % funcname) prnt('{') prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) prnt('}') prnt() def _generate_cpy_constant_collecttype(self, tp, name): is_int = tp.is_integer_type() if not is_int or self.target_is_python: self._do_collect_type(tp) def _generate_cpy_constant_decl(self, tp, name): is_int = tp.is_integer_type() self._generate_cpy_const(is_int, name, tp) def _generate_cpy_constant_ctx(self, tp, name): if not self.target_is_python and tp.is_integer_type(): type_op = CffiOp(OP_CONSTANT_INT, -1) else: if self.target_is_python: const_kind = OP_DLOPEN_CONST else: const_kind = OP_CONSTANT type_index = self._typesdict[tp] type_op = CffiOp(const_kind, type_index) self._lsts["global"].append( GlobalExpr(name, '_cffi_const_%s' % name, type_op)) # ---------- # enums def _generate_cpy_enum_collecttype(self, tp, name): self._do_collect_type(tp) def _generate_cpy_enum_decl(self, tp, name=None): for enumerator in tp.enumerators: self._generate_cpy_const(True, enumerator) def _enum_ctx(self, tp, cname): type_index = self._typesdict[tp] type_op = CffiOp(OP_ENUM, -1) if self.target_is_python: tp.check_not_partial() for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): self._lsts["global"].append( GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, check_value=enumvalue)) # if cname is not None and '$' not in cname and not self.target_is_python: size = "sizeof(%s)" % cname signed = "((%s)-1) <= 0" % cname else: basetp = tp.build_baseinttype(self.ffi, []) size = self.ffi.sizeof(basetp) signed = int(int(self.ffi.cast(basetp, -1)) < 0) allenums = ",".join(tp.enumerators) self._lsts["enum"].append( EnumExpr(tp.name, type_index, size, signed, allenums)) def _generate_cpy_enum_ctx(self, tp, name): self._enum_ctx(tp, tp._get_c_name()) # ---------- # macros: for now only for integers def _generate_cpy_macro_collecttype(self, tp, name): pass def _generate_cpy_macro_decl(self, tp, name): if tp == '...': check_value = None else: check_value = tp # an integer self._generate_cpy_const(True, name, check_value=check_value) def _generate_cpy_macro_ctx(self, tp, name): if tp == '...': if self.target_is_python: raise ffiplatform.VerificationError( "cannot use the syntax '...' in '#define %s ...' when " "using the ABI mode" % (name,)) check_value = None else: check_value = tp # an integer type_op = CffiOp(OP_CONSTANT_INT, -1) self._lsts["global"].append( GlobalExpr(name, '_cffi_const_%s' % name, type_op, check_value=check_value)) # ---------- # global variables def _global_type(self, tp, global_name): if isinstance(tp, model.ArrayType): actual_length = tp.length if actual_length == '...': actual_length = '_cffi_array_len(%s)' % (global_name,) tp_item = self._global_type(tp.item, '%s[0]' % global_name) tp = model.ArrayType(tp_item, actual_length) return tp def _generate_cpy_variable_collecttype(self, tp, name): self._do_collect_type(self._global_type(tp, name)) def _generate_cpy_variable_decl(self, tp, name): prnt = self._prnt tp = self._global_type(tp, name) if isinstance(tp, model.ArrayType) and tp.length is None: tp = tp.item ampersand = '' else: ampersand = '&' # This code assumes that casts from "tp *" to "void *" is a # no-op, i.e. a function that returns a "tp *" can be called # as if it returned a "void *". This should be generally true # on any modern machine. The only exception to that rule (on # uncommon architectures, and as far as I can tell) might be # if 'tp' were a function type, but that is not possible here. # (If 'tp' is a function _pointer_ type, then casts from "fn_t # **" to "void *" are again no-ops, as far as I can tell.) decl = '*_cffi_var_%s(void)' % (name,) prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) prnt('{') prnt(' return %s(%s);' % (ampersand, name)) prnt('}') prnt() def _generate_cpy_variable_ctx(self, tp, name): tp = self._global_type(tp, name) type_index = self._typesdict[tp] if self.target_is_python: op = OP_GLOBAL_VAR else: op = OP_GLOBAL_VAR_F self._lsts["global"].append( GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) # ---------- # extern "Python" def _generate_cpy_extern_python_collecttype(self, tp, name): assert isinstance(tp, model.FunctionPtrType) self._do_collect_type(tp) def _generate_cpy_dllexport_python_collecttype(self, tp, name): self._generate_cpy_extern_python_collecttype(tp, name) def _generate_cpy_extern_python_decl(self, tp, name, dllexport=False): prnt = self._prnt if isinstance(tp.result, model.VoidType): size_of_result = '0' else: context = 'result of %s' % name size_of_result = '(int)sizeof(%s)' % ( tp.result.get_c_name('', context),) prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) prnt(' { "%s", %s };' % (name, size_of_result)) prnt() # arguments = [] context = 'argument of %s' % name for i, type in enumerate(tp.args): arg = type.get_c_name(' a%d' % i, context) arguments.append(arg) # repr_arguments = ', '.join(arguments) repr_arguments = repr_arguments or 'void' name_and_arguments = '%s(%s)' % (name, repr_arguments) if tp.abi == "__stdcall": name_and_arguments = '_cffi_stdcall ' + name_and_arguments # def may_need_128_bits(tp): return (isinstance(tp, model.PrimitiveType) and tp.name == 'long double') # size_of_a = max(len(tp.args)*8, 8) if may_need_128_bits(tp.result): size_of_a = max(size_of_a, 16) if isinstance(tp.result, model.StructOrUnion): size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( tp.result.get_c_name(''), size_of_a, tp.result.get_c_name(''), size_of_a) if dllexport: tag = 'CFFI_DLLEXPORT' else: tag = 'static' prnt('%s %s' % (tag, tp.result.get_c_name(name_and_arguments))) prnt('{') prnt(' char a[%s];' % size_of_a) prnt(' char *p = a;') for i, type in enumerate(tp.args): arg = 'a%d' % i if (isinstance(type, model.StructOrUnion) or may_need_128_bits(type)): arg = '&' + arg type = model.PointerType(type) prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) if not isinstance(tp.result, model.VoidType): prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) prnt('}') prnt() self._num_externpy += 1 def _generate_cpy_dllexport_python_decl(self, tp, name): self._generate_cpy_extern_python_decl(tp, name, dllexport=True) def _generate_cpy_extern_python_ctx(self, tp, name): if self.target_is_python: raise ffiplatform.VerificationError( "cannot use 'extern \"Python\"' in the ABI mode") if tp.ellipsis: raise NotImplementedError("a vararg function is extern \"Python\"") type_index = self._typesdict[tp] type_op = CffiOp(OP_EXTERN_PYTHON, type_index) self._lsts["global"].append( GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) def _generate_cpy_dllexport_python_ctx(self, tp, name): self._generate_cpy_extern_python_ctx(tp, name) def _string_literal(self, s): def _char_repr(c): # escape with a '\' the characters '\', '"' or (for trigraphs) '?' if c in '\\"?': return '\\' + c if ' ' <= c < '\x7F': return c if c == '\n': return '\\n' return '\\%03o' % ord(c) lines = [] for line in s.splitlines(True): lines.append('"%s"' % ''.join([_char_repr(c) for c in line])) return ' \\\n'.join(lines) # ---------- # emitting the opcodes for individual types def _emit_bytecode_VoidType(self, tp, index): self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) def _emit_bytecode_PrimitiveType(self, tp, index): prim_index = PRIMITIVE_TO_INDEX[tp.name] self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) def _emit_bytecode_UnknownIntegerType(self, tp, index): s = ('_cffi_prim_int(sizeof(%s), (\n' ' ((%s)-1) << 0 /* check that %s is an integer type */\n' ' ) <= 0)' % (tp.name, tp.name, tp.name)) self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) def _emit_bytecode_UnknownFloatType(self, tp, index): s = ('_cffi_prim_float(sizeof(%s) *\n' ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' ' )' % (tp.name, tp.name)) self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) def _emit_bytecode_RawFunctionType(self, tp, index): self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) index += 1 for tp1 in tp.args: realindex = self._typesdict[tp1] if index != realindex: if isinstance(tp1, model.PrimitiveType): self._emit_bytecode_PrimitiveType(tp1, index) else: self.cffi_types[index] = CffiOp(OP_NOOP, realindex) index += 1 flags = int(tp.ellipsis) if tp.abi is not None: if tp.abi == '__stdcall': flags |= 2 else: raise NotImplementedError("abi=%r" % (tp.abi,)) self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) def _emit_bytecode_PointerType(self, tp, index): self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType def _emit_bytecode_FunctionPtrType(self, tp, index): raw = tp.as_raw_function() self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) def _emit_bytecode_ArrayType(self, tp, index): item_index = self._typesdict[tp.item] if tp.length is None: self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) elif tp.length == '...': raise ffiplatform.VerificationError( "type %s badly placed: the '...' array length can only be " "used on global arrays or on fields of structures" % ( str(tp).replace('/*...*/', '...'),)) else: assert self.cffi_types[index + 1] == 'LEN' self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) def _emit_bytecode_StructType(self, tp, index): struct_index = self._struct_unions[tp] self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) _emit_bytecode_UnionType = _emit_bytecode_StructType def _emit_bytecode_EnumType(self, tp, index): enum_index = self._enums[tp] self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) if sys.version_info >= (3,): NativeIO = io.StringIO else: class NativeIO(io.BytesIO): def write(self, s): if isinstance(s, unicode): s = s.encode('ascii') super(NativeIO, self).write(s) def _make_c_or_py_source(ffi, module_name, preamble, target_file): recompiler = Recompiler(ffi, module_name, target_is_python=(preamble is None)) recompiler.collect_type_table() recompiler.collect_step_tables() f = NativeIO() recompiler.write_source_to_f(f, preamble) output = f.getvalue() try: with open(target_file, 'r') as f1: if f1.read(len(output) + 1) != output: raise IOError return False # already up-to-date except IOError: tmp_file = '%s.~%d' % (target_file, os.getpid()) with open(tmp_file, 'w') as f1: f1.write(output) try: os.rename(tmp_file, target_file) except OSError: os.unlink(target_file) os.rename(tmp_file, target_file) return True def make_c_source(ffi, module_name, preamble, target_c_file): assert preamble is not None return _make_c_or_py_source(ffi, module_name, preamble, target_c_file) def make_py_source(ffi, module_name, target_py_file): return _make_c_or_py_source(ffi, module_name, None, target_py_file) def _modname_to_file(outputdir, modname, extension): parts = modname.split('.') try: os.makedirs(os.path.join(outputdir, *parts[:-1])) except OSError: pass parts[-1] += extension return os.path.join(outputdir, *parts), parts # Aaargh. Distutils is not tested at all for the purpose of compiling # DLLs that are not extension modules. Here are some hacks to work # around that, in the _patch_for_*() functions... def _patch_meth(patchlist, cls, name, new_meth): old = getattr(cls, name) patchlist.append((cls, name, old)) setattr(cls, name, new_meth) return old def _unpatch_meths(patchlist): for cls, name, old_meth in reversed(patchlist): setattr(cls, name, old_meth) def _patch_for_embedding(patchlist): if sys.platform == 'win32': # we must not remove the manifest when building for embedding! from distutils.msvc9compiler import MSVCCompiler _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', lambda self, manifest_file: manifest_file) if sys.platform == 'darwin': # we must not make a '-bundle', but a '-dynamiclib' instead from distutils.ccompiler import CCompiler def my_link_shared_object(self, *args, **kwds): if '-bundle' in self.linker_so: self.linker_so = list(self.linker_so) i = self.linker_so.index('-bundle') self.linker_so[i] = '-dynamiclib' return old_link_shared_object(self, *args, **kwds) old_link_shared_object = _patch_meth(patchlist, CCompiler, 'link_shared_object', my_link_shared_object) def _patch_for_target(patchlist, target): from distutils.command.build_ext import build_ext # if 'target' is different from '*', we need to patch some internal # method to just return this 'target' value, instead of having it # built from module_name if target.endswith('.*'): target = target[:-2] if sys.platform == 'win32': target += '.dll' elif sys.platform == 'darwin': target += '.dylib' else: target += '.so' _patch_meth(patchlist, build_ext, 'get_ext_filename', lambda self, ext_name: target) def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, c_file=None, source_extension='.c', extradir=None, compiler_verbose=1, target=None, **kwds): if not isinstance(module_name, str): module_name = module_name.encode('ascii') if ffi._windows_unicode: ffi._apply_windows_unicode(kwds) if preamble is not None: embedding = (ffi._embedding is not None) if embedding: ffi._apply_embedding_fix(kwds) if c_file is None: c_file, parts = _modname_to_file(tmpdir, module_name, source_extension) if extradir: parts = [extradir] + parts ext_c_file = os.path.join(*parts) else: ext_c_file = c_file # if target is None: if embedding: target = '%s.*' % module_name else: target = '*' # ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) updated = make_c_source(ffi, module_name, preamble, c_file) if call_c_compiler: patchlist = [] cwd = os.getcwd() try: if embedding: _patch_for_embedding(patchlist) if target != '*': _patch_for_target(patchlist, target) os.chdir(tmpdir) outputfilename = ffiplatform.compile('.', ext, compiler_verbose) finally: os.chdir(cwd) _unpatch_meths(patchlist) return outputfilename else: return ext, updated else: if c_file is None: c_file, _ = _modname_to_file(tmpdir, module_name, '.py') updated = make_py_source(ffi, module_name, c_file) if call_c_compiler: return c_file else: return None, updated def _verify(ffi, module_name, preamble, *args, **kwds): # FOR TESTS ONLY from testing.udir import udir import imp assert module_name not in sys.modules, "module name conflict: %r" % ( module_name,) kwds.setdefault('tmpdir', str(udir)) outputfilename = recompile(ffi, module_name, preamble, *args, **kwds) module = imp.load_dynamic(module_name, outputfilename) # # hack hack hack: copy all *bound methods* from module.ffi back to the # ffi instance. Then calls like ffi.new() will invoke module.ffi.new(). for name in dir(module.ffi): if not name.startswith('_'): attr = getattr(module.ffi, name) if attr is not getattr(ffi, name, object()): setattr(ffi, name, attr) def typeof_disabled(*args, **kwds): raise NotImplementedError ffi._typeof = typeof_disabled return module.lib cffi-1.5.2/LICENSE0000664000175000017500000000241612657646311013677 0ustar arigoarigo00000000000000 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-1.5.2/MANIFEST.in0000664000175000017500000000043012657646311014422 0ustar arigoarigo00000000000000recursive-include cffi *.py *.h recursive-include c *.c *.h *.asm *.py win64.obj recursive-include testing *.py *.c *.h recursive-include doc *.py *.rst Makefile *.bat recursive-include demo py.cleanup *.py embedding_test.c manual.c include AUTHORS LICENSE setup.py setup_base.py cffi-1.5.2/setup.cfg0000664000175000017500000000007312657646372014517 0ustar arigoarigo00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 cffi-1.5.2/PKG-INFO0000664000175000017500000000213112657646372013770 0ustar arigoarigo00000000000000Metadata-Version: 1.1 Name: cffi Version: 1.5.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 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy cffi-1.5.2/c/0000775000175000017500000000000012657646372013120 5ustar arigoarigo00000000000000cffi-1.5.2/c/realize_c_type.c0000664000175000017500000005737712657646311016276 0ustar arigoarigo00000000000000 typedef struct { struct _cffi_type_context_s ctx; /* inlined substructure */ PyObject *types_dict; PyObject *included_ffis; PyObject *included_libs; PyObject *_keepalive1; PyObject *_keepalive2; } builder_c_t; static PyObject *all_primitives[_CFFI__NUM_PRIM]; static CTypeDescrObject *g_ct_voidp, *g_ct_chararray; static PyObject *build_primitive_type(int num); /* forward */ #define primitive_in_range(num) ((num) >= 0 && (num) < _CFFI__NUM_PRIM) #define get_primitive_type(num) \ ((primitive_in_range(num) && all_primitives[num] != NULL) ? \ all_primitives[num] : build_primitive_type(num)) static int init_global_types_dict(PyObject *ffi_type_dict) { int err; PyObject *ct_void, *ct_char, *ct2, *pnull; /* XXX some leaks in case these functions fail, but well, MemoryErrors during importing an extension module are kind of bad anyway */ ct_void = get_primitive_type(_CFFI_PRIM_VOID); // 'void' if (ct_void == NULL) return -1; ct2 = new_pointer_type((CTypeDescrObject *)ct_void); // 'void *' if (ct2 == NULL) return -1; g_ct_voidp = (CTypeDescrObject *)ct2; ct_char = get_primitive_type(_CFFI_PRIM_CHAR); // 'char' if (ct_char == NULL) return -1; ct2 = new_pointer_type((CTypeDescrObject *)ct_char); // 'char *' if (ct2 == NULL) return -1; ct2 = new_array_type((CTypeDescrObject *)ct2, -1); // 'char[]' if (ct2 == NULL) return -1; g_ct_chararray = (CTypeDescrObject *)ct2; pnull = new_simple_cdata(NULL, g_ct_voidp); if (pnull == NULL) return -1; err = PyDict_SetItemString(ffi_type_dict, "NULL", pnull); Py_DECREF(pnull); return err; } static void free_builder_c(builder_c_t *builder, int ctx_is_static) { if (!ctx_is_static) { size_t i; const void *mem[] = {builder->ctx.types, builder->ctx.globals, builder->ctx.struct_unions, //builder->ctx.fields: allocated with struct_unions builder->ctx.enums, builder->ctx.typenames}; for (i = 0; i < sizeof(mem) / sizeof(*mem); i++) { if (mem[i] != NULL) PyMem_Free((void *)mem[i]); } } Py_XDECREF(builder->included_ffis); Py_XDECREF(builder->included_libs); Py_XDECREF(builder->types_dict); Py_XDECREF(builder->_keepalive1); Py_XDECREF(builder->_keepalive2); } static int init_builder_c(builder_c_t *builder, const struct _cffi_type_context_s *ctx) { PyObject *ldict = PyDict_New(); if (ldict == NULL) return -1; if (ctx) builder->ctx = *ctx; else memset(&builder->ctx, 0, sizeof(builder->ctx)); builder->types_dict = ldict; builder->included_ffis = NULL; builder->included_libs = NULL; builder->_keepalive1 = NULL; builder->_keepalive2 = NULL; return 0; } static PyObject *build_primitive_type(int num) { /* XXX too many translations between here and new_primitive_type() */ static const char *primitive_name[] = { NULL, "_Bool", "char", "signed char", "unsigned char", "short", "unsigned short", "int", "unsigned int", "long", "unsigned long", "long long", "unsigned long long", "float", "double", "long double", "wchar_t", "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", "int_least8_t", "uint_least8_t", "int_least16_t", "uint_least16_t", "int_least32_t", "uint_least32_t", "int_least64_t", "uint_least64_t", "int_fast8_t", "uint_fast8_t", "int_fast16_t", "uint_fast16_t", "int_fast32_t", "uint_fast32_t", "int_fast64_t", "uint_fast64_t", "intmax_t", "uintmax_t", }; PyObject *x; assert(sizeof(primitive_name) == sizeof(*primitive_name) * _CFFI__NUM_PRIM); if (num == _CFFI_PRIM_VOID) { x = new_void_type(); } else if (primitive_in_range(num) && primitive_name[num] != NULL) { x = new_primitive_type(primitive_name[num]); } else if (num == _CFFI__UNKNOWN_PRIM) { PyErr_SetString(FFIError, "primitive integer type with an unexpected " "size (or not an integer type at all)"); return NULL; } else if (num == _CFFI__UNKNOWN_FLOAT_PRIM) { PyErr_SetString(FFIError, "primitive floating-point type with an " "unexpected size (or not a float type at all)"); return NULL; } else if (num == _CFFI__UNKNOWN_LONG_DOUBLE) { PyErr_SetString(FFIError, "primitive floating-point type is " "'long double', not supported for now with " "the syntax 'typedef double... xxx;'"); return NULL; } else { PyErr_Format(PyExc_NotImplementedError, "prim=%d", num); return NULL; } all_primitives[num] = x; return x; } static PyObject *realize_global_int(builder_c_t *builder, int gindex) { int neg; char got[64]; unsigned long long value; struct _cffi_getconst_s gc; const struct _cffi_global_s *g = &builder->ctx.globals[gindex]; gc.ctx = &builder->ctx; gc.gindex = gindex; /* note: we cast g->address to this function type; we do the same in parse_c_type:parse_sequel() too. Note that the called function may be declared simply with "unsigned long long *" as argument, which is fine as it is the first field in _cffi_getconst_s. */ assert(&gc.value == (unsigned long long *)&gc); neg = ((int(*)(struct _cffi_getconst_s *))g->address)(&gc); value = gc.value; switch (neg) { case 0: if (value <= (unsigned long long)LONG_MAX) return PyInt_FromLong((long)value); else return PyLong_FromUnsignedLongLong(value); case 1: if ((long long)value >= (long long)LONG_MIN) return PyInt_FromLong((long)value); else return PyLong_FromLongLong((long long)value); default: break; } if (neg == 2) sprintf(got, "%llu (0x%llx)", value, value); else sprintf(got, "%lld", (long long)value); PyErr_Format(FFIError, "the C compiler says '%.200s' is equal to %s, " "but the cdef disagrees", g->name, got); return NULL; } static CTypeDescrObject * unwrap_fn_as_fnptr(PyObject *x) { assert(PyTuple_Check(x)); return (CTypeDescrObject *)PyTuple_GET_ITEM(x, 0); } static CTypeDescrObject * unexpected_fn_type(PyObject *x) { CTypeDescrObject *ct = unwrap_fn_as_fnptr(x); char *text1 = ct->ct_name; char *text2 = text1 + ct->ct_name_position + 1; assert(text2[-3] == '('); text2[-3] = '\0'; PyErr_Format(FFIError, "the type '%s%s' is a function type, not a " "pointer-to-function type", text1, text2); text2[-3] = '('; return NULL; } static PyObject * realize_c_type_or_func(builder_c_t *builder, _cffi_opcode_t opcodes[], int index); /* forward */ /* Interpret an opcodes[] array. If opcodes == ctx->types, store all the intermediate types back in the opcodes[]. Returns a new reference. */ static CTypeDescrObject * realize_c_type(builder_c_t *builder, _cffi_opcode_t opcodes[], int index) { PyObject *x = realize_c_type_or_func(builder, opcodes, index); if (x == NULL || CTypeDescr_Check(x)) return (CTypeDescrObject *)x; else return unexpected_fn_type(x); } static void _realize_name(char *target, const char *prefix, const char *srcname) { /* "xyz" => "struct xyz" "$xyz" => "xyz" "$1" => "struct $1" */ if (srcname[0] == '$' && srcname[1] != '$' && !('0' <= srcname[1] && srcname[1] <= '9')) { strcpy(target, &srcname[1]); } else { strcpy(target, prefix); strcat(target, srcname); } } static void _unrealize_name(char *target, const char *srcname) { /* reverse of _realize_name() */ if (strncmp(srcname, "struct ", 7) == 0) { strcpy(target, &srcname[7]); } else if (strncmp(srcname, "union ", 6) == 0) { strcpy(target, &srcname[6]); } else if (strncmp(srcname, "enum ", 5) == 0) { strcpy(target, &srcname[5]); } else { strcpy(target, "$"); strcat(target, srcname); } } static PyObject * /* forward */ _fetch_external_struct_or_union(const struct _cffi_struct_union_s *s, PyObject *included_ffis, int recursion); static PyObject * _realize_c_struct_or_union(builder_c_t *builder, int sindex) { PyObject *x; _cffi_opcode_t op2; const struct _cffi_struct_union_s *s; if (sindex == _CFFI__IO_FILE_STRUCT) { /* returns a single global cached opaque type */ static PyObject *file_struct = NULL; if (file_struct == NULL) file_struct = new_struct_or_union_type("FILE", CT_STRUCT | CT_IS_FILE); Py_XINCREF(file_struct); return file_struct; } s = &builder->ctx.struct_unions[sindex]; op2 = builder->ctx.types[s->type_index]; if ((((uintptr_t)op2) & 1) == 0) { x = (PyObject *)op2; /* found already in the "primary" slot */ Py_INCREF(x); } else { CTypeDescrObject *ct = NULL; if (!(s->flags & _CFFI_F_EXTERNAL)) { int flags = (s->flags & _CFFI_F_UNION) ? CT_UNION : CT_STRUCT; char *name = alloca(8 + strlen(s->name)); _realize_name(name, (s->flags & _CFFI_F_UNION) ? "union " : "struct ", s->name); if (strcmp(name, "struct _IO_FILE") == 0) x = _realize_c_struct_or_union(builder, _CFFI__IO_FILE_STRUCT); else x = new_struct_or_union_type(name, flags); if (x == NULL) return NULL; if (!(s->flags & _CFFI_F_OPAQUE)) { assert(s->first_field_index >= 0); ct = (CTypeDescrObject *)x; ct->ct_size = (Py_ssize_t)s->size; ct->ct_length = s->alignment; /* may be -1 */ ct->ct_flags &= ~CT_IS_OPAQUE; ct->ct_flags |= CT_LAZY_FIELD_LIST; ct->ct_extra = builder; } else assert(s->first_field_index < 0); } else { assert(s->first_field_index < 0); x = _fetch_external_struct_or_union(s, builder->included_ffis, 0); if (x == NULL) { if (!PyErr_Occurred()) PyErr_Format(FFIError, "'%s %.200s' should come from " "ffi.include() but was not found", (s->flags & _CFFI_F_UNION) ? "union" : "struct", s->name); return NULL; } if (!(s->flags & _CFFI_F_OPAQUE)) { if (((CTypeDescrObject *)x)->ct_flags & CT_IS_OPAQUE) { const char *prefix = (s->flags & _CFFI_F_UNION) ? "union" : "struct"; PyErr_Format(PyExc_NotImplementedError, "'%s %.200s' is opaque in the ffi.include(), " "but no longer in the ffi doing the include " "(workaround: don't use ffi.include() but " "duplicate the declarations of everything " "using %s %.200s)", prefix, s->name, prefix, s->name); Py_DECREF(x); return NULL; } } } /* Update the "primary" OP_STRUCT_UNION slot */ assert((((uintptr_t)x) & 1) == 0); assert(builder->ctx.types[s->type_index] == op2); Py_INCREF(x); builder->ctx.types[s->type_index] = x; if (ct != NULL && s->size == (size_t)-2) { /* oops, this struct is unnamed and we couldn't generate a C expression to get its size. We have to rely on complete_struct_or_union() to compute it now. */ if (do_realize_lazy_struct(ct) < 0) { builder->ctx.types[s->type_index] = op2; return NULL; } } } return x; } static PyObject * realize_c_type_or_func(builder_c_t *builder, _cffi_opcode_t opcodes[], int index) { PyObject *x, *y, *z; _cffi_opcode_t op = opcodes[index]; Py_ssize_t length = -1; if ((((uintptr_t)op) & 1) == 0) { x = (PyObject *)op; Py_INCREF(x); return x; } switch (_CFFI_GETOP(op)) { case _CFFI_OP_PRIMITIVE: x = get_primitive_type(_CFFI_GETARG(op)); Py_XINCREF(x); break; case _CFFI_OP_POINTER: y = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op)); if (y == NULL) return NULL; if (CTypeDescr_Check(y)) { x = new_pointer_type((CTypeDescrObject *)y); } else { assert(PyTuple_Check(y)); /* from _CFFI_OP_FUNCTION */ x = PyTuple_GET_ITEM(y, 0); Py_INCREF(x); } Py_DECREF(y); break; case _CFFI_OP_ARRAY: length = (Py_ssize_t)opcodes[index + 1]; /* fall-through */ case _CFFI_OP_OPEN_ARRAY: y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op)); if (y == NULL) return NULL; z = new_pointer_type((CTypeDescrObject *)y); Py_DECREF(y); if (z == NULL) return NULL; x = new_array_type((CTypeDescrObject *)z, length); Py_DECREF(z); break; case _CFFI_OP_STRUCT_UNION: x = _realize_c_struct_or_union(builder, _CFFI_GETARG(op)); break; case _CFFI_OP_ENUM: { const struct _cffi_enum_s *e; _cffi_opcode_t op2; e = &builder->ctx.enums[_CFFI_GETARG(op)]; op2 = builder->ctx.types[e->type_index]; if ((((uintptr_t)op2) & 1) == 0) { x = (PyObject *)op2; Py_INCREF(x); } else { PyObject *enumerators = NULL, *enumvalues = NULL, *tmp; Py_ssize_t i, j, n = 0; const char *p; int gindex; PyObject *args; PyObject *basetd = get_primitive_type(e->type_prim); if (basetd == NULL) return NULL; if (*e->enumerators != '\0') { n++; for (p = e->enumerators; *p != '\0'; p++) n += (*p == ','); } enumerators = PyTuple_New(n); if (enumerators == NULL) return NULL; enumvalues = PyTuple_New(n); if (enumvalues == NULL) { Py_DECREF(enumerators); return NULL; } p = e->enumerators; for (i = 0; i < n; i++) { j = 0; while (p[j] != ',' && p[j] != '\0') j++; tmp = PyText_FromStringAndSize(p, j); if (tmp == NULL) break; PyTuple_SET_ITEM(enumerators, i, tmp); gindex = search_in_globals(&builder->ctx, p, j); assert(gindex >= 0); assert(builder->ctx.globals[gindex].type_op == _CFFI_OP(_CFFI_OP_ENUM, -1)); tmp = realize_global_int(builder, gindex); if (tmp == NULL) break; PyTuple_SET_ITEM(enumvalues, i, tmp); p += j + 1; } args = NULL; if (!PyErr_Occurred()) { char *name = alloca(6 + strlen(e->name)); _realize_name(name, "enum ", e->name); args = Py_BuildValue("(sOOO)", name, enumerators, enumvalues, basetd); } Py_DECREF(enumerators); Py_DECREF(enumvalues); if (args == NULL) return NULL; x = b_new_enum_type(NULL, args); Py_DECREF(args); if (x == NULL) return NULL; /* Update the "primary" _CFFI_OP_ENUM slot, which may be the same or a different slot than the "current" one */ assert((((uintptr_t)x) & 1) == 0); assert(builder->ctx.types[e->type_index] == op2); Py_INCREF(x); builder->ctx.types[e->type_index] = x; /* Done, leave without updating the "current" slot because it may be done already above. If not, never mind, the next call to realize_c_type() will do it. */ return x; } break; } case _CFFI_OP_FUNCTION: { PyObject *fargs; int i, base_index, num_args, ellipsis, abi; y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op)); if (y == NULL) return NULL; base_index = index + 1; num_args = 0; /* note that if the arguments are already built, they have a pointer in the 'opcodes' array, and GETOP() returns a random even value. But OP_FUNCTION_END is odd, so the condition below still works correctly. */ while (_CFFI_GETOP(opcodes[base_index + num_args]) != _CFFI_OP_FUNCTION_END) num_args++; ellipsis = _CFFI_GETARG(opcodes[base_index + num_args]) & 0x01; abi = _CFFI_GETARG(opcodes[base_index + num_args]) & 0xFE; switch (abi) { case 0: abi = FFI_DEFAULT_ABI; break; case 2: #if defined(MS_WIN32) && !defined(_WIN64) abi = FFI_STDCALL; #else abi = FFI_DEFAULT_ABI; #endif break; default: PyErr_Format(FFIError, "abi number %d not supported", abi); Py_DECREF(y); return NULL; } fargs = PyTuple_New(num_args); if (fargs == NULL) { Py_DECREF(y); return NULL; } for (i = 0; i < num_args; i++) { z = (PyObject *)realize_c_type(builder, opcodes, base_index + i); if (z == NULL) { Py_DECREF(fargs); Py_DECREF(y); return NULL; } PyTuple_SET_ITEM(fargs, i, z); } z = new_function_type(fargs, (CTypeDescrObject *)y, ellipsis, abi); Py_DECREF(fargs); Py_DECREF(y); if (z == NULL) return NULL; x = PyTuple_Pack(1, z); /* hack: hide the CT_FUNCTIONPTR. it will be revealed again by the OP_POINTER */ Py_DECREF(z); break; } case _CFFI_OP_NOOP: x = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op)); break; case _CFFI_OP_TYPENAME: { /* essential: the TYPENAME opcode resolves the type index looked up in the 'ctx->typenames' array, but it does so in 'ctx->types' instead of in 'opcodes'! */ int type_index = builder->ctx.typenames[_CFFI_GETARG(op)].type_index; x = realize_c_type_or_func(builder, builder->ctx.types, type_index); break; } default: PyErr_Format(PyExc_NotImplementedError, "op=%d", (int)_CFFI_GETOP(op)); return NULL; } if (x != NULL && opcodes == builder->ctx.types && opcodes[index] != x) { assert((((uintptr_t)x) & 1) == 0); assert((((uintptr_t)opcodes[index]) & 1) == 1); Py_INCREF(x); opcodes[index] = x; } return x; }; static int do_realize_lazy_struct(CTypeDescrObject *ct) { /* This is called by force_lazy_struct() in _cffi_backend.c */ assert(ct->ct_flags & (CT_STRUCT | CT_UNION)); if (ct->ct_flags & CT_LAZY_FIELD_LIST) { builder_c_t *builder; char *p; int n, i, sflags; const struct _cffi_struct_union_s *s; const struct _cffi_field_s *fld; PyObject *fields, *args, *res; assert(!(ct->ct_flags & CT_IS_OPAQUE)); builder = ct->ct_extra; assert(builder != NULL); p = alloca(2 + strlen(ct->ct_name)); _unrealize_name(p, ct->ct_name); n = search_in_struct_unions(&builder->ctx, p, strlen(p)); if (n < 0) Py_FatalError("lost a struct/union!"); s = &builder->ctx.struct_unions[n]; fld = &builder->ctx.fields[s->first_field_index]; /* XXX painfully build all the Python objects that are the args to b_complete_struct_or_union() */ fields = PyList_New(s->num_fields); if (fields == NULL) return -1; for (i = 0; i < s->num_fields; i++, fld++) { _cffi_opcode_t op = fld->field_type_op; int fbitsize = -1; PyObject *f; CTypeDescrObject *ctf; switch (_CFFI_GETOP(op)) { case _CFFI_OP_BITFIELD: assert(fld->field_size >= 0); fbitsize = (int)fld->field_size; /* fall-through */ case _CFFI_OP_NOOP: ctf = realize_c_type(builder, builder->ctx.types, _CFFI_GETARG(op)); break; default: Py_DECREF(fields); PyErr_Format(PyExc_NotImplementedError, "field op=%d", (int)_CFFI_GETOP(op)); return -1; } if (fld->field_offset == (size_t)-1) { /* unnamed struct, with field positions and sizes entirely determined by complete_struct_or_union() and not checked. Or, bitfields (field_size >= 0), similarly not checked. */ assert(fld->field_size == (size_t)-1 || fbitsize >= 0); } else if (detect_custom_layout(ct, SF_STD_FIELD_POS, ctf->ct_size, fld->field_size, "wrong size for field '", fld->name, "'") < 0) { Py_DECREF(fields); return -1; } f = Py_BuildValue("(sOin)", fld->name, ctf, fbitsize, (Py_ssize_t)fld->field_offset); if (f == NULL) { Py_DECREF(fields); return -1; } PyList_SET_ITEM(fields, i, f); } sflags = 0; if (s->flags & _CFFI_F_CHECK_FIELDS) sflags |= SF_STD_FIELD_POS; if (s->flags & _CFFI_F_PACKED) sflags |= SF_PACKED; args = Py_BuildValue("(OOOnii)", ct, fields, Py_None, (Py_ssize_t)s->size, s->alignment, sflags); Py_DECREF(fields); if (args == NULL) return -1; ct->ct_extra = NULL; ct->ct_flags |= CT_IS_OPAQUE; res = b_complete_struct_or_union(NULL, args); ct->ct_flags &= ~CT_IS_OPAQUE; Py_DECREF(args); if (res == NULL) { ct->ct_extra = builder; return -1; } assert(ct->ct_stuff != NULL); ct->ct_flags &= ~CT_LAZY_FIELD_LIST; Py_DECREF(res); return 1; } else { assert(ct->ct_flags & CT_IS_OPAQUE); return 0; } } cffi-1.5.2/c/_cffi_backend.c0000664000175000017500000065003212657646311016000 0ustar arigoarigo00000000000000#define PY_SSIZE_T_CLEAN #include #include "structmember.h" #define CFFI_VERSION "1.5.2" #ifdef MS_WIN32 #include #include "misc_win32.h" #else #include #include #include #include #include #include #endif /* this block of #ifs should be kept exactly identical between c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py */ #if defined(_MSC_VER) # include /* for alloca() */ # if _MSC_VER < 1600 /* MSVC < 2010 */ 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 __int8 int_least8_t; typedef __int16 int_least16_t; typedef __int32 int_least32_t; typedef __int64 int_least64_t; typedef unsigned __int8 uint_least8_t; typedef unsigned __int16 uint_least16_t; typedef unsigned __int32 uint_least32_t; typedef unsigned __int64 uint_least64_t; typedef __int8 int_fast8_t; typedef __int16 int_fast16_t; typedef __int32 int_fast32_t; typedef __int64 int_fast64_t; typedef unsigned __int8 uint_fast8_t; typedef unsigned __int16 uint_fast16_t; typedef unsigned __int32 uint_fast32_t; typedef unsigned __int64 uint_fast64_t; typedef __int64 intmax_t; typedef unsigned __int64 uintmax_t; # else # include # endif # if _MSC_VER < 1800 /* MSVC < 2013 */ typedef unsigned char _Bool; # endif #else # include # if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) # 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 PyText_InternFromString PyUnicode_InternFromString # 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 PyText_InternFromString PyString_InternFromString # 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 # define PyInt_AsLong PyLong_AsLong #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 # undef PyCapsule_GetPointer # undef PyCapsule_New # define PyCapsule_GetPointer(capsule, name) \ (PyCObject_AsVoidPtr(capsule)) # 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_IS_UNSIZED_CHAR_A 2097152 #define CT_LAZY_FIELD_LIST 4194304 #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 */ PyObject *ct_unique_key; /* key in unique_cache (a string, but not human-readable) */ 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; static PyTypeObject CDataGCP_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 || \ Py_TYPE(ob) == &CDataGCP_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 { CDataObject head; Py_ssize_t length; /* same as CDataObject_own_length up to here */ Py_buffer *bufferview; } CDataObject_owngc_frombuf; typedef struct { CDataObject head; Py_ssize_t length; /* same as CDataObject_own_length up to here */ PyObject *origobj; PyObject *destructor; } CDataObject_gcp; 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 # include "misc_thread_posix.h" #endif #include "minibuffer.h" #if PY_MAJOR_VERSION >= 3 # include "file_emulator.h" #endif #ifdef HAVE_WCHAR_H # include "wchar_helper.h" #endif typedef struct _cffi_allocator_s { PyObject *ca_alloc, *ca_free; int ca_dont_clear; } cffi_allocator_t; static const cffi_allocator_t default_allocator = { NULL, NULL, 0 }; static PyObject *FFIError; static PyObject *unique_cache; /************************************************************/ 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; ct->ct_unique_key = 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); if (ct->ct_unique_key != NULL) { /* revive dead object temporarily for DelItem */ Py_REFCNT(ct) = 43; PyDict_DelItem(unique_cache, ct->ct_unique_key); assert(Py_REFCNT(ct) == 42); Py_REFCNT(ct) = 0; Py_DECREF(ct->ct_unique_key); } 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 */ #define force_lazy_struct(ct) \ ((ct)->ct_stuff != NULL ? 1 : do_realize_lazy_struct(ct)) static int do_realize_lazy_struct(CTypeDescrObject *ct); /* forward, implemented in realize_c_type.c */ 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; if (force_lazy_struct(ct) < 0) return NULL; 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; } #define _read_raw_data(type) \ do { \ if (size == sizeof(type)) { \ type r; \ memcpy(&r, target, sizeof(type)); \ return r; \ } \ } while(0) static PY_LONG_LONG read_raw_signed_data(char *target, int size) { _read_raw_data(signed char); _read_raw_data(short); _read_raw_data(int); _read_raw_data(long); _read_raw_data(PY_LONG_LONG); Py_FatalError("read_raw_signed_data: bad integer size"); return 0; } static unsigned PY_LONG_LONG read_raw_unsigned_data(char *target, int size) { _read_raw_data(unsigned char); _read_raw_data(unsigned short); _read_raw_data(unsigned int); _read_raw_data(unsigned long); _read_raw_data(unsigned PY_LONG_LONG); Py_FatalError("read_raw_unsigned_data: bad integer size"); return 0; } #define _write_raw_data(type) \ do { \ if (size == sizeof(type)) { \ type r = (type)source; \ memcpy(target, &r, sizeof(type)); \ return; \ } \ } while(0) static void write_raw_integer_data(char *target, unsigned PY_LONG_LONG source, int size) { _write_raw_data(unsigned char); _write_raw_data(unsigned short); _write_raw_data(unsigned int); _write_raw_data(unsigned long); _write_raw_data(unsigned PY_LONG_LONG); Py_FatalError("write_raw_integer_data: bad integer size"); } static double read_raw_float_data(char *target, int size) { _read_raw_data(float); _read_raw_data(double); Py_FatalError("read_raw_float_data: bad float size"); return 0; } static long double read_raw_longdouble_data(char *target) { int size = sizeof(long double); _read_raw_data(long double); Py_FatalError("read_raw_longdouble_data: bad long double size"); return 0; } static void write_raw_float_data(char *target, double source, int size) { _write_raw_data(float); _write_raw_data(double); Py_FatalError("write_raw_float_data: bad float size"); } static void write_raw_longdouble_data(char *target, long double source) { int size = sizeof(long double); _write_raw_data(long double); } 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)) { const char *ct_name_2 = ((CDataObject *)init)->c_type->ct_name; if (strcmp(ct_name, ct_name_2) != 0) PyErr_Format(PyExc_TypeError, "initializer for ctype '%s' must be a %s, " "not cdata '%s'", ct_name, expected, ct_name_2); else { /* in case we'd give the error message "initializer for ctype 'A' must be a pointer to same type, not cdata 'B'", but with A=B, then give instead a different error message to try to clear up the confusion */ PyErr_Format(PyExc_TypeError, "initializer for ctype '%s' appears indeed to be '%s'," " but the types are different (check that you are not" " e.g. mixing up different ffi instances)", ct_name, ct_name_2); } } 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 (force_lazy_struct(ct) <= 0) { if (!PyErr_Occurred()) PyErr_Format(PyExc_TypeError, "'%s' is opaque", ct->ct_name); return -1; } 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); } #ifdef __GNUC__ # if __GNUC__ >= 4 /* Don't go inlining this huge function. Needed because occasionally it gets inlined in places where is causes a warning: call to __builtin___memcpy_chk will always overflow destination buffer (which is places where the 'ct' should never represent such a large primitive type anyway). */ __attribute__((noinline)) # endif #endif 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; if (align == -1 && (ct->ct_flags & CT_LAZY_FIELD_LIST)) { force_lazy_struct(ct); 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 = ((CDataObject_own_structptr *)cd)->structobj; 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); } else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; PyBuffer_Release(view); PyObject_Free(view); } 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 = ((CDataObject_own_structptr *)cd)->structobj; 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); } else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; Py_VISIT(view->obj); } return 0; } static int cdataowninggc_clear(CDataObject *cd) { if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ CDataObject_own_structptr *cd1 = (CDataObject_own_structptr *)cd; PyObject *x = cd1->structobj; Py_INCREF(Py_None); cd1->structobj = Py_None; 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); } else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; PyBuffer_Release(view); } return 0; } /* forward */ static void _my_PyErr_WriteUnraisable(char *objdescr, PyObject *obj, char *extra_error_line); static void gcp_finalize(PyObject *destructor, PyObject *origobj) { /* NOTE: this decrements the reference count of the two arguments */ if (destructor != NULL) { PyObject *result; PyObject *error_type, *error_value, *error_traceback; /* Save the current exception */ PyErr_Fetch(&error_type, &error_value, &error_traceback); result = PyObject_CallFunctionObjArgs(destructor, origobj, NULL); if (result != NULL) { Py_DECREF(result); } else { _my_PyErr_WriteUnraisable("From callback for ffi.gc ", origobj, NULL); } Py_DECREF(destructor); /* Restore the saved exception */ PyErr_Restore(error_type, error_value, error_traceback); } Py_XDECREF(origobj); } #ifdef Py_TPFLAGS_HAVE_FINALIZE /* CPython >= 3.4 */ static void cdatagcp_finalize(CDataObject_gcp *cd) { PyObject *destructor = cd->destructor; PyObject *origobj = cd->origobj; cd->destructor = NULL; cd->origobj = NULL; gcp_finalize(destructor, origobj); } #endif static void cdatagcp_dealloc(CDataObject_gcp *cd) { PyObject *destructor = cd->destructor; PyObject *origobj = cd->origobj; cdata_dealloc((CDataObject *)cd); gcp_finalize(destructor, origobj); } static int cdatagcp_traverse(CDataObject_gcp *cd, visitproc visit, void *arg) { Py_VISIT(cd->destructor); Py_VISIT(cd->origobj); 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) 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 size = cd->c_type->ct_size; return PyText_FromFormat("", cd->c_type->ct_name, size); } static PyObject *cdataowninggc_repr(CDataObject *cd) { if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ PyObject *x = ((CDataObject_own_structptr *)cd)->structobj; return _cdata_repr2(cd, "handle to", x); } else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ 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)); } else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; Py_ssize_t buflen = get_array_length(cd); return PyText_FromFormat( "", cd->c_type->ct_name, buflen, view->obj ? Py_TYPE(view->obj)->tp_name : "(null)"); } return cdataowning_repr(cd); } 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)) { if (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_data == NULL) { PyErr_Format(PyExc_RuntimeError, "cannot dereference null pointer from cdata '%s'", 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, Py_ssize_t length); /* 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, -1); 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 */ memmove(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)) { switch (force_lazy_struct(ct)) { case 1: 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); } break; case -1: return NULL; default: break; } } 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)) { switch (force_lazy_struct(ct)) { case 1: 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; } } break; case -1: return -1; default: break; } } 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 *new_primitive_type(const char *name); /*forward*/ static CTypeDescrObject *_get_ct_int(void) { static CTypeDescrObject *ct_int = NULL; if (ct_int == NULL) { ct_int = (CTypeDescrObject *)new_primitive_type("int"); } 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 < (Py_ssize_t)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_owngc_frombuf), 0, (destructor)cdataowninggc_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)cdataowninggc_repr, /* 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 */ }; static PyTypeObject CDataGCP_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CDataGCP", sizeof(CDataObject_gcp), 0, (destructor)cdatagcp_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 */ #ifdef Py_TPFLAGS_HAVE_FINALIZE | Py_TPFLAGS_HAVE_FINALIZE #endif | Py_TPFLAGS_HAVE_GC, 0, /* tp_doc */ (traverseproc)cdatagcp_traverse, /* 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 */ #ifdef Py_TPFLAGS_HAVE_FINALIZE /* CPython >= 3.4 */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ 0, /* version_tag */ (destructor)cdatagcp_finalize, /* tp_finalize */ #endif }; /************************************************************/ 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 CDataObject *allocate_gcp_object(CDataObject *origobj, CTypeDescrObject *ct, PyObject *destructor) { CDataObject_gcp *cd = PyObject_GC_New(CDataObject_gcp, &CDataGCP_Type); if (cd == NULL) return NULL; Py_XINCREF(destructor); Py_INCREF(origobj); Py_INCREF(ct); cd->head.c_data = origobj->c_data; cd->head.c_type = ct; cd->head.c_weakreflist = NULL; cd->origobj = (PyObject *)origobj; cd->destructor = destructor; PyObject_GC_Track(cd); return (CDataObject *)cd; } static CDataObject *allocate_with_allocator(Py_ssize_t basesize, Py_ssize_t datasize, CTypeDescrObject *ct, const cffi_allocator_t *allocator) { CDataObject *cd; if (allocator->ca_alloc == NULL) { cd = allocate_owning_object(basesize + datasize, ct); if (cd == NULL) return NULL; cd->c_data = ((char *)cd) + basesize; } else { PyObject *res = PyObject_CallFunction(allocator->ca_alloc, "n", datasize); if (res == NULL) return NULL; if (!CData_Check(res)) { PyErr_Format(PyExc_TypeError, "alloc() must return a cdata object (got %.200s)", Py_TYPE(res)->tp_name); Py_DECREF(res); return NULL; } cd = (CDataObject *)res; if (!(cd->c_type->ct_flags & (CT_POINTER|CT_ARRAY))) { PyErr_Format(PyExc_TypeError, "alloc() must return a cdata pointer, not '%s'", cd->c_type->ct_name); Py_DECREF(res); return NULL; } if (!cd->c_data) { PyErr_SetString(PyExc_MemoryError, "alloc() returned NULL"); Py_DECREF(res); return NULL; } cd = allocate_gcp_object(cd, ct, allocator->ca_free); Py_DECREF(res); } if (!allocator->ca_dont_clear) memset(cd->c_data, 0, datasize); return cd; } static PyObject *direct_newp(CTypeDescrObject *ct, PyObject *init, const cffi_allocator_t *allocator) { CTypeDescrObject *ctitem; CDataObject *cd; Py_ssize_t dataoffset, datasize, explicitlength; 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_STRUCT | CT_UNION)) && init != Py_None) { if (force_lazy_struct(ctitem) < 0) /* for CT_WITH_VAR_ARRAY */ return NULL; if (ctitem->ct_flags & CT_WITH_VAR_ARRAY) { 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_with_allocator(dataoffset, datasize, ct->ct_itemdescr, allocator); 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); cd->c_data = cds->c_data; } else { cd = allocate_with_allocator(dataoffset, datasize, ct, allocator); if (cd == NULL) return NULL; if (explicitlength >= 0) ((CDataObject_own_length*)cd)->length = explicitlength; } 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 PyObject *b_newp(PyObject *self, PyObject *args) { CTypeDescrObject *ct; PyObject *init = Py_None; if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init)) return NULL; return direct_newp(ct, init, &default_allocator); } 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 *do_cast(CTypeDescrObject *ct, PyObject *ob) { CDataObject *cd; 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); Py_DECREF(io); 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; } static PyObject *b_cast(PyObject *self, PyObject *args) { CTypeDescrObject *ct; PyObject *ob; if (!PyArg_ParseTuple(args, "O!O:cast", &CTypeDescr_Type, &ct, &ob)) return NULL; return do_cast(ct, ob); } /************************************************************/ 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 *get_unique_type(CTypeDescrObject *x, const void *unique_key[], long keylength) { /* Replace the CTypeDescrObject 'x' with a standardized one. This either just returns x, or x is decrefed and a new reference to the already-existing equivalent is returned. In this function, 'x' always contains a reference that must be either decrefed or returned. Keys: void ["void"] primitive [&static_struct] pointer [ctype] array [ctype, length] funcptr [ctresult, ellipsis+abi, num_args, ctargs...] */ PyObject *key, *y; void *pkey; key = PyBytes_FromStringAndSize(NULL, keylength * sizeof(void *)); if (key == NULL) goto error; pkey = PyBytes_AS_STRING(key); memcpy(pkey, unique_key, keylength * sizeof(void *)); y = PyDict_GetItem(unique_cache, key); if (y != NULL) { Py_DECREF(key); Py_INCREF(y); Py_DECREF(x); return y; } if (PyDict_SetItem(unique_cache, key, (PyObject *)x) < 0) { Py_DECREF(key); goto error; } /* Haaaack for our reference count hack: gcmodule.c must not see this dictionary. The problem is that any PyDict_SetItem() notices that 'x' is tracked and re-tracks the unique_cache dictionary. So here we re-untrack it again... */ PyObject_GC_UnTrack(unique_cache); assert(x->ct_unique_key == NULL); x->ct_unique_key = key; /* the key will be freed in ctypedescr_dealloc() */ Py_DECREF(x); /* the 'value' in unique_cache doesn't count as 1 */ return (PyObject *)x; error: Py_DECREF(x); return NULL; } static PyObject *new_primitive_type(const char *name) { #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(il8, int_least8_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(ul8, uint_least8_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(il16, int_least16_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(ul16, uint_least16_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(il32, int_least32_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(ul32, uint_least32_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(il64, int_least64_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(ul64, uint_least64_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(if8, int_fast8_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(uf8, uint_fast8_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(if16, int_fast16_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(uf16, uint_fast16_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(if32, int_fast32_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(uf32, uint_fast32_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(if64, int_fast64_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(uf64, uint_fast64_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(ip, intptr_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(up, uintptr_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(im, intmax_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(um, uintmax_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; 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; const void *unique_key[1]; int name_size; ffi_type *ffitype; 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) { /* assume that if sizeof(double) == sizeof(long double), then the two types are equivalent for C. libffi bugs on Win64 if a function's return type is ffi_type_longdouble... */ if (sizeof(double) == sizeof(long double)) ffitype = &ffi_type_double; else 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 <= (Py_ssize_t)sizeof(long)) td->ct_flags |= CT_PRIMITIVE_FITS_LONG; } else if (td->ct_flags & CT_PRIMITIVE_UNSIGNED) { if (td->ct_size < (Py_ssize_t)sizeof(long)) td->ct_flags |= CT_PRIMITIVE_FITS_LONG; } td->ct_name_position = strlen(td->ct_name); unique_key[0] = ptypes; return get_unique_type(td, unique_key, 1); bad_ffi_type: PyErr_Format(PyExc_NotImplementedError, "primitive type '%s' has size %d; " "the supported sizes are 1, 2, 4, 8", name, (int)ptypes->size); return NULL; } static PyObject *b_new_primitive_type(PyObject *self, PyObject *args) { char *name; if (!PyArg_ParseTuple(args, "s:new_primitive_type", &name)) return NULL; return new_primitive_type(name); } static PyObject *new_pointer_type(CTypeDescrObject *ctitem) { CTypeDescrObject *td; const char *extra; const void *unique_key[1]; 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 */ unique_key[0] = ctitem; return get_unique_type(td, unique_key, 1); } static PyObject *b_new_pointer_type(PyObject *self, PyObject *args) { CTypeDescrObject *ctitem; if (!PyArg_ParseTuple(args, "O!:new_pointer_type", &CTypeDescr_Type, &ctitem)) return NULL; return new_pointer_type(ctitem); } static PyObject *b_new_array_type(PyObject *self, PyObject *args) { PyObject *lengthobj; Py_ssize_t length; CTypeDescrObject *ctptr; if (!PyArg_ParseTuple(args, "O!O:new_array_type", &CTypeDescr_Type, &ctptr, &lengthobj)) return NULL; if (lengthobj == Py_None) { length = -1; } else { length = PyNumber_AsSsize_t(lengthobj, PyExc_OverflowError); if (length < 0) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "negative array length"); return NULL; } } return new_array_type(ctptr, length); } static PyObject * new_array_type(CTypeDescrObject *ctptr, Py_ssize_t length) { CTypeDescrObject *td, *ctitem; char extra_text[32]; Py_ssize_t arraysize; int flags = CT_ARRAY; const void *unique_key[2]; 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 (length < 0) { sprintf(extra_text, "[]"); length = -1; arraysize = -1; if ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) && ctitem->ct_size == sizeof(char)) flags |= CT_IS_UNSIZED_CHAR_A; } else { 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 = flags; unique_key[0] = ctptr; unique_key[1] = (void *)length; return get_unique_type(td, unique_key, 2); } static PyObject *new_void_type(void) { int name_size = strlen("void") + 1; const void *unique_key[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"); unique_key[0] = "void"; return get_unique_type(td, unique_key, 1); } static PyObject *b_new_void_type(PyObject *self, PyObject *args) { return new_void_type(); } static PyObject *new_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; td->ct_extra = NULL; 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 new_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 new_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 #define SF_STD_FIELD_POS 0x80 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 # if defined(__arm__) || defined(__aarch64__) 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 int detect_custom_layout(CTypeDescrObject *ct, int sflags, Py_ssize_t cdef_value, Py_ssize_t compiler_value, const char *msg1, const char *txt, const char *msg2) { if (compiler_value != cdef_value) { if (sflags & SF_STD_FIELD_POS) { PyErr_Format(FFIError, "%s: %s%s%s (cdef says %zd, but C compiler says %zd)." " fix it or use \"...;\" in the cdef for %s to " "make it flexible", ct->ct_name, msg1, txt, msg2, cdef_value, compiler_value, ct->ct_name); return -1; } ct->ct_flags |= CT_CUSTOM_FIELD_POS; } return 0; } 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, alignedsize; 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; } ct->ct_flags &= ~CT_CUSTOM_FIELD_POS; 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 (detect_custom_layout(ct, sflags, boffset / 8, foffset, "wrong offset for field '", PyText_AS_UTF8(fname), "'") < 0) goto error; 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 */ alignedsize = (boffsetmax + alignment - 1) & ~(alignment-1); if (alignedsize == 0) alignedsize = 1; if (totalsize < 0) { totalsize = alignedsize; } else { if (detect_custom_layout(ct, sflags, alignedsize, totalsize, "wrong total size", "", "") < 0) goto error; 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; } } if (totalalignment < 0) { totalalignment = alignment; } else { if (detect_custom_layout(ct, sflags, alignment, totalalignment, "wrong total alignment", "", "") < 0) goto error; } ct->ct_size = totalsize; ct->ct_length = totalalignment; ct->ct_stuff = interned_fields; ct->ct_flags &= ~CT_IS_OPAQUE; Py_INCREF(Py_None); return Py_None; error: ct->ct_extra = NULL; 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) { const char *place = is_result_type ? "return value" : "argument"; 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 (force_lazy_struct(ct) < 0) return NULL; if (ct->ct_flags & CT_CUSTOM_FIELD_POS) { /* these NotImplementedErrors may be caught and ignored until a real call is made to a function of this type */ PyErr_Format(PyExc_NotImplementedError, "ctype '%s' not supported as %s (it is a struct declared " "with \"...;\", but the C calling convention may depend " "on the missing fields)", ct->ct_name, place); 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_Format(PyExc_NotImplementedError, "ctype '%s' not supported as %s" " (it is a struct with bit fields)", ct->ct_name, place); return NULL; } flat = 1; ct1 = cf->cf_type; while (ct1->ct_flags & CT_ARRAY) { flat *= ct1->ct_length; ct1 = ct1->ct_itemdescr; } if (flat <= 0) { PyErr_Format(PyExc_NotImplementedError, "ctype '%s' not supported as %s" " (it is a struct with a zero-length array)", ct->ct_name, place); 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' (size %zd) not supported as %s", ct->ct_name, ct->ct_size, place); 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 */ /* we also align it to the next multiple of 8, in an attempt to work around bugs(?) of libffi like #241 */ cif_descr->exchange_size = ALIGN_ARG(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, int fabi) { 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, "(", 1); i = 2; #if defined(MS_WIN32) && !defined(_WIN64) if (fabi == FFI_STDCALL) { fb_cat_name(fb, "__stdcall ", 10); i += 10; } #endif fb_cat_name(fb, "*)(", 3); if (fb->fct) { i = fresult->ct_name_position + i; /* 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, int fabi) { 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, fabi) < 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, fabi) < 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 *new_function_type(PyObject *fargs, /* tuple */ CTypeDescrObject *fresult, int ellipsis, int fabi) { PyObject *fabiobj; CTypeDescrObject *fct; struct funcbuilder_s funcbuilder; Py_ssize_t i; const void **unique_key; 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, fabi); 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) { if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) { PyErr_Clear(); /* will get the exception if we see an actual call */ } else 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); } /* [ctresult, ellipsis+abi, num_args, ctargs...] */ unique_key = alloca((3 + funcbuilder.nargs) * sizeof(void *)); unique_key[0] = fresult; unique_key[1] = (const void *)(Py_ssize_t)((fabi << 1) | !!ellipsis); unique_key[2] = (const void *)(Py_ssize_t)(funcbuilder.nargs); for (i=0; ict_stuff, 2 + i); return get_unique_type(fct, unique_key, 3 + funcbuilder.nargs); error: Py_DECREF(fct); return NULL; } static PyObject *b_new_function_type(PyObject *self, PyObject *args) { PyObject *fargs; CTypeDescrObject *fresult; int ellipsis = 0, fabi = FFI_DEFAULT_ABI; if (!PyArg_ParseTuple(args, "O!O!|ii:new_function_type", &PyTuple_Type, &fargs, &CTypeDescr_Type, &fresult, &ellipsis, &fabi)) return NULL; return new_function_type(fargs, fresult, ellipsis, fabi); } static int convert_from_object_fficallback(char *result, CTypeDescrObject *ctype, PyObject *pyobj, int encode_result_for_libffi) { /* 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 (!encode_result_for_libffi) goto skip; 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 } } skip: return convert_from_object(result, ctype, pyobj); } static void _my_PyErr_WriteUnraisable(char *objdescr, 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) { if (obj != NULL) { PyFile_WriteString(objdescr, 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 general_invoke_callback(int decode_args_from_libffi, void *result, char *args, void *userdata) { 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; PyObject *onerror_cb; 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_flags & (CT_IS_LONGDOUBLE | CT_STRUCT | CT_UNION)) a_src = *(char **)a_src; } a = convert_to_object(a_src, a_ct); if (a == NULL) goto error; PyTuple_SET_ITEM(py_args, i, a); } py_res = PyObject_Call(py_ob, py_args, NULL); if (py_res == NULL) goto error; if (convert_from_object_fficallback(result, SIGNATURE(1), py_res, decode_args_from_libffi) < 0) { extra_error_line = "Trying to convert the result back to C:\n"; goto error; } done: Py_XDECREF(py_args); Py_XDECREF(py_res); Py_DECREF(cb_args); return; error: if (SIGNATURE(1)->ct_size > 0) { py_rawerr = PyTuple_GET_ITEM(cb_args, 2); memcpy(result, PyBytes_AS_STRING(py_rawerr), PyBytes_GET_SIZE(py_rawerr)); } onerror_cb = PyTuple_GET_ITEM(cb_args, 3); if (onerror_cb == Py_None) { _my_PyErr_WriteUnraisable("From cffi callback ", py_ob, extra_error_line); } else { PyObject *exc1, *val1, *tb1, *res1, *exc2, *val2, *tb2; PyErr_Fetch(&exc1, &val1, &tb1); PyErr_NormalizeException(&exc1, &val1, &tb1); res1 = PyObject_CallFunctionObjArgs(onerror_cb, exc1 ? exc1 : Py_None, val1 ? val1 : Py_None, tb1 ? tb1 : Py_None, NULL); if (res1 != NULL) { if (res1 != Py_None) convert_from_object_fficallback(result, SIGNATURE(1), res1, decode_args_from_libffi); Py_DECREF(res1); } if (!PyErr_Occurred()) { Py_XDECREF(exc1); Py_XDECREF(val1); Py_XDECREF(tb1); } else { /* double exception! print a double-traceback... */ PyErr_Fetch(&exc2, &val2, &tb2); PyErr_Restore(exc1, val1, tb1); _my_PyErr_WriteUnraisable("From cffi callback ", py_ob, extra_error_line); PyErr_Restore(exc2, val2, tb2); extra_error_line = ("\nDuring the call to 'onerror', " "another exception occurred:\n\n"); _my_PyErr_WriteUnraisable(NULL, NULL, extra_error_line); } } goto done; #undef SIGNATURE } static void invoke_callback(ffi_cif *cif, void *result, void **args, void *userdata) { save_errno(); { PyGILState_STATE state = gil_ensure(); general_invoke_callback(1, result, (char *)args, userdata); gil_release(state); } restore_errno(); } static PyObject *prepare_callback_info_tuple(CTypeDescrObject *ct, PyObject *ob, PyObject *error_ob, PyObject *onerror_ob, int decode_args_from_libffi) { CTypeDescrObject *ctresult; PyObject *py_rawerr, *infotuple; Py_ssize_t size; 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; } if (onerror_ob != Py_None && !PyCallable_Check(onerror_ob)) { PyErr_Format(PyExc_TypeError, "expected a callable object for 'onerror', not %.200s", Py_TYPE(onerror_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, decode_args_from_libffi) < 0) { Py_DECREF(py_rawerr); return NULL; } } infotuple = Py_BuildValue("OOOO", ct, ob, py_rawerr, onerror_ob); Py_DECREF(py_rawerr); #ifdef WITH_THREAD /* We must setup the GIL here, in case the callback is invoked in some other non-Pythonic thread. This is the same as ctypes. */ PyEval_InitThreads(); #endif return infotuple; } static PyObject *b_callback(PyObject *self, PyObject *args) { CTypeDescrObject *ct; CDataObject *cd; PyObject *ob, *error_ob = Py_None, *onerror_ob = Py_None; PyObject *infotuple; cif_description_t *cif_descr; ffi_closure *closure; if (!PyArg_ParseTuple(args, "O!O|OO:callback", &CTypeDescr_Type, &ct, &ob, &error_ob, &onerror_ob)) return NULL; infotuple = prepare_callback_info_tuple(ct, ob, error_ob, onerror_ob, 1); 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_Format(PyExc_NotImplementedError, "%s: callback with unsupported argument or " "return type or with '...'", ct->ct_name); 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); 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 CTypeDescrObject *direct_typeoffsetof(CTypeDescrObject *ct, PyObject *fieldname, int following, Py_ssize_t *offset) { /* Does not return a new reference! */ CTypeDescrObject *res; CFieldObject *cf; if (PyTextAny_Check(fieldname)) { if (!following && (ct->ct_flags & CT_POINTER)) ct = ct->ct_itemdescr; if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) { PyErr_SetString(PyExc_TypeError, "with a field name argument, expected a " "struct or union ctype"); return NULL; } if (force_lazy_struct(ct) <= 0) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_TypeError, "struct/union is opaque"); 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 = cf->cf_type; *offset = cf->cf_offset; } else { ssize_t index = PyInt_AsSsize_t(fieldname); if (index < 0 && PyErr_Occurred()) { PyErr_SetString(PyExc_TypeError, "field name or array index expected"); return NULL; } if (!(ct->ct_flags & (CT_ARRAY|CT_POINTER)) || ct->ct_itemdescr->ct_size < 0) { PyErr_SetString(PyExc_TypeError, "with an integer argument, " "expected an array ctype or a " "pointer to non-opaque"); return NULL; } res = ct->ct_itemdescr; *offset = index * ct->ct_itemdescr->ct_size; if ((*offset / ct->ct_itemdescr->ct_size) != index) { PyErr_SetString(PyExc_OverflowError, "array offset would overflow a Py_ssize_t"); return NULL; } } return res; } static PyObject *b_typeoffsetof(PyObject *self, PyObject *args) { PyObject *res, *fieldname; CTypeDescrObject *ct; Py_ssize_t offset; int following = 0; if (!PyArg_ParseTuple(args, "O!O|i:typeoffsetof", &CTypeDescr_Type, &ct, &fieldname, &following)) return NULL; res = (PyObject *)direct_typeoffsetof(ct, fieldname, following, &offset); if (res == NULL) return NULL; return Py_BuildValue("(On)", res, offset); } static PyObject *b_rawaddressof(PyObject *self, PyObject *args) { CTypeDescrObject *ct; CDataObject *cd; Py_ssize_t offset; int accepted_flags; if (!PyArg_ParseTuple(args, "O!O!n:rawaddressof", &CTypeDescr_Type, &ct, &CData_Type, &cd, &offset)) return NULL; accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER; if ((cd->c_type->ct_flags & accepted_flags) == 0) { PyErr_SetString(PyExc_TypeError, "expected a cdata struct/union/array/pointer 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, PyObject *kwds) { CDataObject *cd; Py_ssize_t maxlen = -1; static char *keywords[] = {"cdata", "maxlen", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:string", keywords, &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, PyObject *kwds) { CDataObject *cd; Py_ssize_t size = -1; static char *keywords[] = {"cdata", "size", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:buffer", keywords, &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 *arg) { long ival = PyInt_AsLong(arg); if (ival == -1 && PyErr_Occurred()) return NULL; else if (ival < INT_MIN || ival > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "errno value too large"); return NULL; } errno = (int)ival; save_errno_only(); errno = 0; Py_INCREF(Py_None); return Py_None; } static PyObject *newp_handle(CTypeDescrObject *ct_voidp, PyObject *x) { CDataObject_own_structptr *cd; cd = (CDataObject_own_structptr *)PyObject_GC_New(CDataObject_own_structptr, &CDataOwningGC_Type); if (cd == NULL) return NULL; Py_INCREF(ct_voidp); cd->head.c_type = ct_voidp; cd->head.c_data = (char *)cd; cd->head.c_weakreflist = NULL; Py_INCREF(x); cd->structobj = x; PyObject_GC_Track(cd); return (PyObject *)cd; } static PyObject *b_newp_handle(PyObject *self, PyObject *args) { CTypeDescrObject *ct; 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; } return newp_handle(ct, x); } static PyObject *b_from_handle(PyObject *self, PyObject *arg) { CTypeDescrObject *ct; CDataObject_own_structptr *orgcd; PyObject *x; if (!CData_Check(arg)) { PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); return NULL; } ct = ((CDataObject *)arg)->c_type; 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; } orgcd = (CDataObject_own_structptr *)((CDataObject *)arg)->c_data; if (!orgcd) { PyErr_SetString(PyExc_RuntimeError, "cannot use from_handle() on NULL pointer"); return NULL; } if (Py_REFCNT(orgcd) <= 0 || Py_TYPE(orgcd) != &CDataOwningGC_Type) { Py_FatalError("ffi.from_handle() detected that the address passed " "points to garbage. If it is really the result of " "ffi.new_handle(), then the Python object has already " "been garbage collected"); } x = orgcd->structobj; Py_INCREF(x); return x; } static int _my_PyObject_GetContiguousBuffer(PyObject *x, Py_buffer *view, int writable_only) { #if PY_MAJOR_VERSION < 3 /* Some objects only support the buffer interface and CPython doesn't translate it into the memoryview interface, mess. Hack a very minimal content for 'view'. Don't care if the other fields are uninitialized: we only call PyBuffer_Release(), which only reads 'view->obj'. */ PyBufferProcs *pb = x->ob_type->tp_as_buffer; if (pb && !pb->bf_releasebuffer) { /* we used to try all three in some vaguely sensible order, i.e. first the write. But trying to call the write on a read-only buffer fails with TypeError. So we use a less- sensible order now. See test_from_buffer_more_cases. If 'writable_only', we only try bf_getwritebuffer. */ readbufferproc proc = NULL; if (!writable_only) { proc = (readbufferproc)pb->bf_getreadbuffer; if (!proc) proc = (readbufferproc)pb->bf_getcharbuffer; } if (!proc) proc = (readbufferproc)pb->bf_getwritebuffer; if (proc && pb->bf_getsegcount) { if ((*pb->bf_getsegcount)(x, NULL) != 1) { PyErr_SetString(PyExc_TypeError, "expected a single-segment buffer object"); return -1; } view->len = (*proc)(x, 0, &view->buf); if (view->len < 0) return -1; view->obj = x; Py_INCREF(x); return 0; } } #endif if (PyObject_GetBuffer(x, view, writable_only ? PyBUF_WRITABLE : PyBUF_SIMPLE) < 0) return -1; if (!PyBuffer_IsContiguous(view, 'A')) { PyBuffer_Release(view); PyErr_SetString(PyExc_TypeError, "contiguous buffer expected"); return -1; } return 0; } static int invalid_input_buffer_type(PyObject *x) { #if PY_MAJOR_VERSION < 3 if (PyBuffer_Check(x)) { /* XXX fish fish fish in an inofficial way */ typedef struct { PyObject_HEAD PyObject *b_base; } _my_PyBufferObject; _my_PyBufferObject *b = (_my_PyBufferObject *)x; x = b->b_base; if (x == NULL) return 0; } else #endif #if PY_MAJOR_VERSION > 2 || PY_MINOR_VERSION > 6 if (PyMemoryView_Check(x)) { x = PyMemoryView_GET_BASE(x); if (x == NULL) return 0; } else #endif ; if (PyBytes_Check(x) || PyUnicode_Check(x)) return 1; if (PyByteArray_Check(x)) /* <= this one here for PyPy compatibility */ return 1; return 0; } static PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x) { CDataObject *cd; Py_buffer *view; if (invalid_input_buffer_type(x)) { PyErr_SetString(PyExc_TypeError, "from_buffer() cannot return the address of the " "raw string within a "STR_OR_BYTES" or unicode or " "bytearray object"); return NULL; } view = PyObject_Malloc(sizeof(Py_buffer)); if (view == NULL) { PyErr_NoMemory(); return NULL; } if (_my_PyObject_GetContiguousBuffer(x, view, 0) < 0) goto error1; cd = (CDataObject *)PyObject_GC_New(CDataObject_owngc_frombuf, &CDataOwningGC_Type); if (cd == NULL) goto error2; Py_INCREF(ct); cd->c_type = ct; cd->c_data = view->buf; cd->c_weakreflist = NULL; ((CDataObject_owngc_frombuf *)cd)->length = view->len; ((CDataObject_owngc_frombuf *)cd)->bufferview = view; PyObject_GC_Track(cd); return (PyObject *)cd; error2: PyBuffer_Release(view); error1: PyObject_Free(view); return NULL; } static PyObject *b_from_buffer(PyObject *self, PyObject *args) { CTypeDescrObject *ct; PyObject *x; if (!PyArg_ParseTuple(args, "O!O", &CTypeDescr_Type, &ct, &x)) return NULL; if (!(ct->ct_flags & CT_IS_UNSIZED_CHAR_A)) { PyErr_Format(PyExc_TypeError, "needs 'char[]', got '%s'", ct->ct_name); return NULL; } return direct_from_buffer(ct, x); } static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only) { if (CData_Check(x)) { CTypeDescrObject *ct = ((CDataObject *)x)->c_type; if (!(ct->ct_flags & (CT_POINTER|CT_ARRAY))) { PyErr_Format(PyExc_TypeError, "expected a pointer or array ctype, got '%s'", ct->ct_name); return -1; } view->buf = ((CDataObject *)x)->c_data; view->obj = NULL; return 0; } else { return _my_PyObject_GetContiguousBuffer(x, view, writable_only); } } static PyObject *b_memmove(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *dest_obj, *src_obj; Py_buffer dest_view, src_view; Py_ssize_t n; static char *keywords[] = {"dest", "src", "n", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOn", keywords, &dest_obj, &src_obj, &n)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "negative size"); return NULL; } if (_fetch_as_buffer(src_obj, &src_view, 0) < 0) { return NULL; } if (_fetch_as_buffer(dest_obj, &dest_view, 1) < 0) { PyBuffer_Release(&src_view); return NULL; } memmove(dest_view.buf, src_view.buf, n); PyBuffer_Release(&dest_view); PyBuffer_Release(&src_view); Py_INCREF(Py_None); return Py_None; } static PyObject *b__get_types(PyObject *self, PyObject *noarg) { return PyTuple_Pack(2, (PyObject *)&CData_Type, (PyObject *)&CTypeDescr_Type); } /* forward, in commontypes.c */ static PyObject *b__get_common_types(PyObject *self, PyObject *arg); static PyObject *b_gcp(PyObject *self, PyObject *args, PyObject *kwds) { CDataObject *cd; CDataObject *origobj; PyObject *destructor; static char *keywords[] = {"cdata", "destructor", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O:gc", keywords, &CData_Type, &origobj, &destructor)) return NULL; cd = allocate_gcp_object(origobj, origobj->c_type, destructor); return (PyObject *)cd; } /************************************************************/ 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); } #if PY_MAJOR_VERSION < 3 static Py_ssize_t _test_segcountproc(PyObject *o, Py_ssize_t *ignored) { return 1; } static Py_ssize_t _test_getreadbuf(PyObject *o, Py_ssize_t i, void **r) { static char buf[] = "RDB"; *r = buf; return 3; } static Py_ssize_t _test_getwritebuf(PyObject *o, Py_ssize_t i, void **r) { static char buf[] = "WRB"; *r = buf; return 3; } static Py_ssize_t _test_getcharbuf(PyObject *o, Py_ssize_t i, char **r) { static char buf[] = "CHB"; *r = buf; return 3; } #endif static int _test_getbuf(PyObject *self, Py_buffer *view, int flags) { static char buf[] = "GTB"; return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/0, flags); } static int _test_getbuf_ro(PyObject *self, Py_buffer *view, int flags) { static char buf[] = "ROB"; return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/1, flags); } static PyObject *b__testbuff(PyObject *self, PyObject *args) { /* for testing only */ int methods; PyTypeObject *obj; if (!PyArg_ParseTuple(args, "O!i|_testbuff", &PyType_Type, &obj, &methods)) return NULL; assert(obj->tp_as_buffer != NULL); #if PY_MAJOR_VERSION < 3 obj->tp_as_buffer->bf_getsegcount = &_test_segcountproc; obj->tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER; obj->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; if (methods & 1) obj->tp_as_buffer->bf_getreadbuffer = &_test_getreadbuf; if (methods & 2) obj->tp_as_buffer->bf_getwritebuffer = &_test_getwritebuf; if (methods & 4) obj->tp_as_buffer->bf_getcharbuffer = &_test_getcharbuf; #endif if (methods & 8) obj->tp_as_buffer->bf_getbuffer = &_test_getbuf; if (methods & 16) obj->tp_as_buffer->bf_getbuffer = &_test_getbuf_ro; Py_INCREF(Py_None); return Py_None; } static PyObject *b_init_cffi_1_0_external_module(PyObject *, PyObject *); /* forward, see cffi1_module.c */ 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", (PyCFunction)b_string, METH_VARARGS | METH_KEYWORDS}, {"buffer", (PyCFunction)b_buffer, METH_VARARGS | METH_KEYWORDS}, {"get_errno", b_get_errno, METH_NOARGS}, {"set_errno", b_set_errno, METH_O}, {"newp_handle", b_newp_handle, METH_VARARGS}, {"from_handle", b_from_handle, METH_O}, {"from_buffer", b_from_buffer, METH_VARARGS}, {"memmove", (PyCFunction)b_memmove, METH_VARARGS | METH_KEYWORDS}, {"gcp", (PyCFunction)b_gcp, METH_VARARGS | METH_KEYWORDS}, #ifdef MS_WIN32 {"getwinerror", (PyCFunction)b_getwinerror, METH_VARARGS | METH_KEYWORDS}, #endif {"_get_types", b__get_types, METH_NOARGS}, {"_get_common_types", b__get_common_types, METH_O}, {"_testfunc", b__testfunc, METH_VARARGS}, {"_testbuff", b__testbuff, METH_VARARGS}, {"_init_cffi_1_0_external_module", b_init_cffi_1_0_external_module, METH_O}, {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)(0ULL-(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 struct _cffi_externpy_s; /* forward declaration */ static void cffi_call_python(struct _cffi_externpy_s *, char *args); static void *cffi_exports[] = { NULL, _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, cffi_call_python, }; static struct { const char *name; int value; } all_dlopen_flags[] = { { "RTLD_LAZY", RTLD_LAZY }, { "RTLD_NOW", RTLD_NOW }, { "RTLD_GLOBAL", RTLD_GLOBAL }, #ifdef RTLD_LOCAL { "RTLD_LOCAL", RTLD_LOCAL }, #else { "RTLD_LOCAL", 0 }, #endif #ifdef RTLD_NODELETE { "RTLD_NODELETE", RTLD_NODELETE }, #endif #ifdef RTLD_NOLOAD { "RTLD_NOLOAD", RTLD_NOLOAD }, #endif #ifdef RTLD_DEEPBIND { "RTLD_DEEPBIND", RTLD_DEEPBIND }, #endif { NULL, 0 } }; /************************************************************/ #include "cffi1_module.c" /************************************************************/ #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; int i; static char init_done = 0; 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 (unique_cache == NULL) { unique_cache = PyDict_New(); if (unique_cache == 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(&CDataGCP_Type) < 0) INITERROR; if (PyType_Ready(&CDataIter_Type) < 0) INITERROR; if (PyType_Ready(&MiniBuffer_Type) < 0) INITERROR; if (!init_done) { 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; init_done = 1; } /* this is for backward compatibility only */ v = PyCapsule_New((void *)cffi_exports, "cffi", NULL); if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0) INITERROR; v = PyText_FromString(CFFI_VERSION); 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 PyModule_AddIntConstant(m, "FFI_CDECL", FFI_DEFAULT_ABI) < 0 || #ifdef MS_WIN32 # ifdef _WIN64 PyModule_AddIntConstant(m, "_WIN", 64) < 0 || /* win64 */ # else PyModule_AddIntConstant(m, "_WIN", 32) < 0 || /* win32 */ # endif #endif 0) INITERROR; for (i = 0; all_dlopen_flags[i].name != NULL; i++) { if (PyModule_AddIntConstant(m, all_dlopen_flags[i].name, all_dlopen_flags[i].value) < 0) INITERROR; } init_cffi_tls(); if (PyErr_Occurred()) INITERROR; if (init_ffi_lib(m) < 0) INITERROR; #if PY_MAJOR_VERSION >= 3 if (init_file_emulator() < 0) INITERROR; return m; #endif } cffi-1.5.2/c/cdlopen.c0000664000175000017500000003242712657646311014711 0ustar arigoarigo00000000000000/* ffi.dlopen() interface with dlopen()/dlsym()/dlclose() */ static void *cdlopen_fetch(PyObject *libname, void *libhandle, char *symbol) { void *address; if (libhandle == NULL) { PyErr_Format(FFIError, "library '%s' has been closed", PyText_AS_UTF8(libname)); return NULL; } dlerror(); /* clear error condition */ address = dlsym(libhandle, symbol); if (address == NULL) { const char *error = dlerror(); PyErr_Format(FFIError, "symbol '%s' not found in library '%s': %s", symbol, PyText_AS_UTF8(libname), error); } return address; } static void cdlopen_close_ignore_errors(void *libhandle) { if (libhandle != NULL) dlclose(libhandle); } static int cdlopen_close(PyObject *libname, void *libhandle) { if (libhandle != NULL && dlclose(libhandle) != 0) { const char *error = dlerror(); PyErr_Format(FFIError, "closing library '%s': %s", PyText_AS_UTF8(libname), error); return -1; } return 0; } static PyObject *ffi_dlopen(PyObject *self, PyObject *args) { char *filename_or_null, *printable_filename; void *handle; 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; } return (PyObject *)lib_internal_new((FFIObject *)self, printable_filename, handle); } static PyObject *ffi_dlclose(PyObject *self, PyObject *args) { LibObject *lib; void *libhandle; if (!PyArg_ParseTuple(args, "O!", &Lib_Type, &lib)) return NULL; libhandle = lib->l_libhandle; lib->l_libhandle = NULL; if (libhandle == NULL) { PyErr_Format(FFIError, "library '%s' is already closed " "or was not created with ffi.dlopen()", PyText_AS_UTF8(lib->l_libname)); return NULL; } /* Clear the dict to force further accesses to do cdlopen_fetch() again, and fail because the library was closed. */ PyDict_Clear(lib->l_dict); if (cdlopen_close(lib->l_libname, libhandle) < 0) return NULL; Py_INCREF(Py_None); return Py_None; } static Py_ssize_t cdl_4bytes(char *src) { /* read 4 bytes in little-endian order; return it as a signed integer */ signed char *ssrc = (signed char *)src; unsigned char *usrc = (unsigned char *)src; return (ssrc[0] << 24) | (usrc[1] << 16) | (usrc[2] << 8) | usrc[3]; } static _cffi_opcode_t cdl_opcode(char *src) { return (_cffi_opcode_t)cdl_4bytes(src); } typedef struct { unsigned long long value; int neg; } cdl_intconst_t; static int _cdl_realize_global_int(struct _cffi_getconst_s *gc) { /* The 'address' field of 'struct _cffi_global_s' is set to point to this function in case ffiobj_init() sees constant integers. This fishes around after the 'ctx->globals' array, which is initialized to contain another array, this time of 'cdl_intconst_t' structures. We get the nth one and it tells us what to return. */ cdl_intconst_t *ic; ic = (cdl_intconst_t *)(gc->ctx->globals + gc->ctx->num_globals); ic += gc->gindex; gc->value = ic->value; return ic->neg; } static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds) { FFIObject *ffi; static char *keywords[] = {"module_name", "_version", "_types", "_globals", "_struct_unions", "_enums", "_typenames", "_includes", NULL}; char *ffiname = "?", *types = NULL, *building = NULL; Py_ssize_t version = -1; Py_ssize_t types_len = 0; PyObject *globals = NULL, *struct_unions = NULL, *enums = NULL; PyObject *typenames = NULL, *includes = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sns#O!O!O!O!O!:FFI", keywords, &ffiname, &version, &types, &types_len, &PyTuple_Type, &globals, &PyTuple_Type, &struct_unions, &PyTuple_Type, &enums, &PyTuple_Type, &typenames, &PyTuple_Type, &includes)) return -1; ffi = (FFIObject *)self; if (ffi->ctx_is_nonempty) { PyErr_SetString(PyExc_ValueError, "cannot call FFI.__init__() more than once"); return -1; } ffi->ctx_is_nonempty = 1; if (version == -1 && types_len == 0) return 0; if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) { PyErr_Format(PyExc_ImportError, "cffi out-of-line Python module '%s' has unknown " "version %p", ffiname, (void *)version); return -1; } if (types_len > 0) { /* unpack a string of 4-byte entries into an array of _cffi_opcode_t */ _cffi_opcode_t *ntypes; Py_ssize_t i, n = types_len / 4; building = PyMem_Malloc(n * sizeof(_cffi_opcode_t)); if (building == NULL) goto error; ntypes = (_cffi_opcode_t *)building; for (i = 0; i < n; i++) { ntypes[i] = cdl_opcode(types); types += 4; } ffi->types_builder.ctx.types = ntypes; ffi->types_builder.ctx.num_types = n; building = NULL; } if (globals != NULL) { /* unpack a tuple alternating strings and ints, each two together describing one global_s entry with no specified address or size. The int is only used with integer constants. */ struct _cffi_global_s *nglobs; cdl_intconst_t *nintconsts; Py_ssize_t i, n = PyTuple_GET_SIZE(globals) / 2; i = n * (sizeof(struct _cffi_global_s) + sizeof(cdl_intconst_t)); building = PyMem_Malloc(i); if (building == NULL) goto error; memset(building, 0, i); nglobs = (struct _cffi_global_s *)building; nintconsts = (cdl_intconst_t *)(nglobs + n); for (i = 0; i < n; i++) { char *g = PyBytes_AS_STRING(PyTuple_GET_ITEM(globals, i * 2)); nglobs[i].type_op = cdl_opcode(g); g += 4; nglobs[i].name = g; if (_CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_CONSTANT_INT || _CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_ENUM) { PyObject *o = PyTuple_GET_ITEM(globals, i * 2 + 1); nglobs[i].address = &_cdl_realize_global_int; #if PY_MAJOR_VERSION < 3 if (PyInt_Check(o)) { nintconsts[i].neg = PyInt_AS_LONG(o) <= 0; nintconsts[i].value = (long long)PyInt_AS_LONG(o); } else #endif { nintconsts[i].neg = PyObject_RichCompareBool(o, Py_False, Py_LE); nintconsts[i].value = PyLong_AsUnsignedLongLongMask(o); if (PyErr_Occurred()) goto error; } } } ffi->types_builder.ctx.globals = nglobs; ffi->types_builder.ctx.num_globals = n; building = NULL; } if (struct_unions != NULL) { /* unpack a tuple of struct/unions, each described as a sub-tuple; the item 0 of each sub-tuple describes the struct/union, and the items 1..N-1 describe the fields, if any */ struct _cffi_struct_union_s *nstructs; struct _cffi_field_s *nfields; Py_ssize_t i, n = PyTuple_GET_SIZE(struct_unions); Py_ssize_t nf = 0; /* total number of fields */ for (i = 0; i < n; i++) { nf += PyTuple_GET_SIZE(PyTuple_GET_ITEM(struct_unions, i)) - 1; } i = (n * sizeof(struct _cffi_struct_union_s) + nf * sizeof(struct _cffi_field_s)); building = PyMem_Malloc(i); if (building == NULL) goto error; memset(building, 0, i); nstructs = (struct _cffi_struct_union_s *)building; nfields = (struct _cffi_field_s *)(nstructs + n); nf = 0; for (i = 0; i < n; i++) { /* 'desc' is the tuple of strings (desc_struct, desc_field_1, ..) */ PyObject *desc = PyTuple_GET_ITEM(struct_unions, i); Py_ssize_t j, nf1 = PyTuple_GET_SIZE(desc) - 1; char *s = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, 0)); /* 's' is the first string, describing the struct/union */ nstructs[i].type_index = cdl_4bytes(s); s += 4; nstructs[i].flags = cdl_4bytes(s); s += 4; nstructs[i].name = s; if (nstructs[i].flags & (_CFFI_F_OPAQUE | _CFFI_F_EXTERNAL)) { nstructs[i].size = (size_t)-1; nstructs[i].alignment = -1; nstructs[i].first_field_index = -1; nstructs[i].num_fields = 0; assert(nf1 == 0); } else { nstructs[i].size = (size_t)-2; nstructs[i].alignment = -2; nstructs[i].first_field_index = nf; nstructs[i].num_fields = nf1; } for (j = 0; j < nf1; j++) { char *f = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, j + 1)); /* 'f' is one of the other strings beyond the first one, describing one field each */ nfields[nf].field_type_op = cdl_opcode(f); f += 4; nfields[nf].field_offset = (size_t)-1; if (_CFFI_GETOP(nfields[nf].field_type_op) != _CFFI_OP_NOOP) { nfields[nf].field_size = cdl_4bytes(f); f += 4; } else { nfields[nf].field_size = (size_t)-1; } nfields[nf].name = f; nf++; } } ffi->types_builder.ctx.struct_unions = nstructs; ffi->types_builder.ctx.fields = nfields; ffi->types_builder.ctx.num_struct_unions = n; building = NULL; } if (enums != NULL) { /* unpack a tuple of strings, each of which describes one enum_s entry */ struct _cffi_enum_s *nenums; Py_ssize_t i, n = PyTuple_GET_SIZE(enums); i = n * sizeof(struct _cffi_enum_s); building = PyMem_Malloc(i); if (building == NULL) goto error; memset(building, 0, i); nenums = (struct _cffi_enum_s *)building; for (i = 0; i < n; i++) { char *e = PyBytes_AS_STRING(PyTuple_GET_ITEM(enums, i)); /* 'e' is a string describing the enum */ nenums[i].type_index = cdl_4bytes(e); e += 4; nenums[i].type_prim = cdl_4bytes(e); e += 4; nenums[i].name = e; e += strlen(e) + 1; nenums[i].enumerators = e; } ffi->types_builder.ctx.enums = nenums; ffi->types_builder.ctx.num_enums = n; building = NULL; } if (typenames != NULL) { /* unpack a tuple of strings, each of which describes one typename_s entry */ struct _cffi_typename_s *ntypenames; Py_ssize_t i, n = PyTuple_GET_SIZE(typenames); i = n * sizeof(struct _cffi_typename_s); building = PyMem_Malloc(i); if (building == NULL) goto error; memset(building, 0, i); ntypenames = (struct _cffi_typename_s *)building; for (i = 0; i < n; i++) { char *t = PyBytes_AS_STRING(PyTuple_GET_ITEM(typenames, i)); /* 't' is a string describing the typename */ ntypenames[i].type_index = cdl_4bytes(t); t += 4; ntypenames[i].name = t; } ffi->types_builder.ctx.typenames = ntypenames; ffi->types_builder.ctx.num_typenames = n; building = NULL; } if (includes != NULL) { PyObject *included_libs; included_libs = PyTuple_New(PyTuple_GET_SIZE(includes)); if (included_libs == NULL) return -1; Py_INCREF(includes); ffi->types_builder.included_ffis = includes; ffi->types_builder.included_libs = included_libs; } /* Above, we took directly some "char *" strings out of the strings, typically from somewhere inside tuples. Keep them alive by incref'ing the whole input arguments. */ Py_INCREF(args); Py_XINCREF(kwds); ffi->types_builder._keepalive1 = args; ffi->types_builder._keepalive2 = kwds; return 0; error: if (building != NULL) PyMem_Free(building); if (!PyErr_Occurred()) PyErr_NoMemory(); return -1; } cffi-1.5.2/c/malloc_closure.h0000664000175000017500000001135412657646311016271 0ustar arigoarigo00000000000000/* * 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 /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. This is, apparently, an undocumented change to ffi_prep_closure(): depending on the Linux kernel we're running on, we must give it a mmap that is either PROT_READ|PROT_WRITE|PROT_EXEC or only PROT_READ|PROT_WRITE. In the latter case, just trying to obtain a mmap with PROT_READ|PROT_WRITE|PROT_EXEC would kill our process(!), but in that situation libffi is fine with only PROT_READ|PROT_WRITE. There is nothing in the libffi API to know that, though, so we have to guess by parsing /proc/self/status. "Meh." */ #ifdef __linux__ #include static int emutramp_enabled = -1; static int emutramp_enabled_check (void) { char *buf = NULL; size_t len = 0; FILE *f; int ret; f = fopen ("/proc/self/status", "r"); if (f == NULL) return 0; ret = 0; while (getline (&buf, &len, f) != -1) if (!strncmp (buf, "PaX:", 4)) { char emutramp; if (sscanf (buf, "%*s %*c%c", &emutramp) == 1) ret = (emutramp == 'E'); break; } free (buf); fclose (f); return ret; } #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \ : (emutramp_enabled = emutramp_enabled_check ())) #else #define is_emutramp_enabled() 0 #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 { int prot = PROT_READ | PROT_WRITE | PROT_EXEC; if (is_emutramp_enabled ()) prot &= ~PROT_EXEC; item = (union mmaped_block *)mmap(NULL, allocate_num_pages * _pagesize, prot, 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-1.5.2/c/cglob.c0000664000175000017500000000642712657646311014354 0ustar arigoarigo00000000000000 typedef void *(*gs_fetch_addr_fn)(void); typedef struct { PyObject_HEAD PyObject *gs_name; CTypeDescrObject *gs_type; char *gs_data; gs_fetch_addr_fn gs_fetch_addr; } GlobSupportObject; static void glob_support_dealloc(GlobSupportObject *gs) { Py_DECREF(gs->gs_name); Py_DECREF(gs->gs_type); PyObject_Del(gs); } static PyTypeObject GlobSupport_Type = { PyVarObject_HEAD_INIT(NULL, 0) "FFIGlobSupport", sizeof(GlobSupportObject), 0, (destructor)glob_support_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 */ }; #define GlobSupport_Check(ob) (Py_TYPE(ob) == &GlobSupport_Type) static PyObject *make_global_var(PyObject *name, CTypeDescrObject *type, char *addr, gs_fetch_addr_fn fetch_addr) { GlobSupportObject *gs = PyObject_New(GlobSupportObject, &GlobSupport_Type); if (gs == NULL) return NULL; Py_INCREF(name); Py_INCREF(type); gs->gs_name = name; gs->gs_type = type; gs->gs_data = addr; gs->gs_fetch_addr = fetch_addr; return (PyObject *)gs; } static void *fetch_global_var_addr(GlobSupportObject *gs) { void *data; if (gs->gs_data != NULL) { data = gs->gs_data; } else { Py_BEGIN_ALLOW_THREADS restore_errno(); data = gs->gs_fetch_addr(); save_errno(); Py_END_ALLOW_THREADS } if (data == NULL) { PyErr_Format(FFIError, "global variable '%s' is at address NULL", PyText_AS_UTF8(gs->gs_name)); return NULL; } return data; } static PyObject *read_global_var(GlobSupportObject *gs) { void *data = fetch_global_var_addr(gs); if (data == NULL) return NULL; return convert_to_object(data, gs->gs_type); } static int write_global_var(GlobSupportObject *gs, PyObject *obj) { void *data = fetch_global_var_addr(gs); if (data == NULL) return -1; return convert_from_object(data, gs->gs_type, obj); } static PyObject *cg_addressof_global_var(GlobSupportObject *gs) { void *data; PyObject *x, *ptrtype = new_pointer_type(gs->gs_type); if (ptrtype == NULL) return NULL; data = fetch_global_var_addr(gs); if (data != NULL) x = new_simple_cdata(data, (CTypeDescrObject *)ptrtype); else x = NULL; Py_DECREF(ptrtype); return x; } cffi-1.5.2/c/libffi_msvc/0000775000175000017500000000000012657646372015403 5ustar arigoarigo00000000000000cffi-1.5.2/c/libffi_msvc/ffitarget.h0000664000175000017500000000521312657646311017521 0ustar arigoarigo00000000000000/* -----------------------------------------------------------------*-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-1.5.2/c/libffi_msvc/types.c0000664000175000017500000000674412657646311016717 0ustar arigoarigo00000000000000/* ----------------------------------------------------------------------- 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-1.5.2/c/libffi_msvc/win32.c0000664000175000017500000001115112657646311016501 0ustar arigoarigo00000000000000/* ----------------------------------------------------------------------- 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-1.5.2/c/libffi_msvc/ffi_common.h0000664000175000017500000000410212657646311017656 0ustar arigoarigo00000000000000/* ----------------------------------------------------------------------- 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-1.5.2/c/libffi_msvc/win64.obj0000664000175000017500000000223012657646311017034 0ustar arigoarigo00000000000000d†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ƒì0Hl$ ‹E0HƒÀHƒàđèH+àHD$ 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ÀHe]Ă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-1.5.2/c/libffi_msvc/ffi.h0000664000175000017500000002170612657646311016317 0ustar arigoarigo00000000000000/* -----------------------------------------------------------------*-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-1.5.2/c/libffi_msvc/prep_cif.c0000664000175000017500000001276412657646311017341 0ustar arigoarigo00000000000000/* ----------------------------------------------------------------------- prep_cif.c - Copyright (c) 1996, 1998 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. ----------------------------------------------------------------------- */ #include #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-1.5.2/c/libffi_msvc/win64.asm0000664000175000017500000000603012657646311017044 0ustar arigoarigo00000000000000PUBLIC 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-1.5.2/c/libffi_msvc/ffi.c0000664000175000017500000002750012657646311016310 0ustar arigoarigo00000000000000/* ----------------------------------------------------------------------- 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 > (long)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-1.5.2/c/libffi_msvc/fficonfig.h0000664000175000017500000000563512657646311017510 0ustar arigoarigo00000000000000/* 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-1.5.2/c/wchar_helper.h0000664000175000017500000000657212657646311015737 0ustar arigoarigo00000000000000/* * 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; ilocal_thread_state != NULL) { /* We need to re-acquire the GIL temporarily to free the thread state. I hope it is not a problem to do it in a thread-local destructor. */ PyEval_RestoreThread(tls->local_thread_state); PyThreadState_DeleteCurrent(); } free(tls); } /* USE__THREAD is defined by setup.py if it finds that it is syntactically valid to use "__thread" with this C compiler. */ #ifdef USE__THREAD static __thread int cffi_saved_errno = 0; static void save_errno_only(void) { cffi_saved_errno = errno; } static void restore_errno_only(void) { errno = cffi_saved_errno; } #else static void save_errno_only(void) { int saved = errno; struct cffi_tls_s *tls = get_cffi_tls(); if (tls != NULL) tls->saved_errno = saved; } static void restore_errno_only(void) { struct cffi_tls_s *tls = get_cffi_tls(); if (tls != NULL) errno = tls->saved_errno; } #endif /* Seems that CPython 3.5.1 made our job harder. Did not find out how to do that without these hacks. We can't use PyThreadState_GET(), because that calls PyThreadState_Get() which fails an assert if the result is NULL. */ #if PY_MAJOR_VERSION >= 3 && !defined(_Py_atomic_load_relaxed) /* this was abruptly un-defined in 3.5.1 */ void *volatile _PyThreadState_Current; /* XXX simple volatile access is assumed atomic */ # define _Py_atomic_load_relaxed(pp) (*(pp)) #endif static PyThreadState *get_current_ts(void) { #if PY_MAJOR_VERSION >= 3 return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current); #else return _PyThreadState_Current; #endif } static PyGILState_STATE gil_ensure(void) { /* Called at the start of a callback. Replacement for PyGILState_Ensure(). */ PyGILState_STATE result; struct cffi_tls_s *tls; PyThreadState *ts = PyGILState_GetThisThreadState(); if (ts != NULL) { ts->gilstate_counter++; if (ts != get_current_ts()) { /* common case: 'ts' is our non-current thread state and we have to make it current and acquire the GIL */ PyEval_RestoreThread(ts); return PyGILState_UNLOCKED; } else { return PyGILState_LOCKED; } } else { /* no thread state here so far. */ result = PyGILState_Ensure(); assert(result == PyGILState_UNLOCKED); ts = PyGILState_GetThisThreadState(); assert(ts != NULL); assert(ts == get_current_ts()); assert(ts->gilstate_counter >= 1); /* Save the now-current thread state inside our 'local_thread_state' field, to be removed at thread shutdown */ tls = get_cffi_tls(); if (tls != NULL) { tls->local_thread_state = ts; ts->gilstate_counter++; } return result; } } static void gil_release(PyGILState_STATE oldstate) { PyGILState_Release(oldstate); } cffi-1.5.2/c/misc_thread_posix.h0000664000175000017500000000260312657646311016767 0ustar arigoarigo00000000000000/* Logic for a better replacement of PyGILState_Ensure(). This version is ready to handle the case of a non-Python-started thread in which we do a large number of calls to CFFI callbacks. If we were to rely on PyGILState_Ensure() for that, we would constantly be creating and destroying PyThreadStates---it is slow, and PyThreadState_Delete() will actually walk the list of all thread states, making it O(n). :-( This version only creates one PyThreadState object the first time we see a given thread, and keep it alive until the thread is really shut down, using a destructor on the tls key. */ #include #include "misc_thread_common.h" static pthread_key_t cffi_tls_key; static void init_cffi_tls(void) { if (pthread_key_create(&cffi_tls_key, &cffi_thread_shutdown) != 0) PyErr_SetString(PyExc_OSError, "pthread_key_create() failed"); } static struct cffi_tls_s *_make_cffi_tls(void) { void *p = calloc(1, sizeof(struct cffi_tls_s)); if (p == NULL) return NULL; if (pthread_setspecific(cffi_tls_key, p) != 0) { free(p); return NULL; } return p; } static struct cffi_tls_s *get_cffi_tls(void) { void *p = pthread_getspecific(cffi_tls_key); if (p == NULL) p = _make_cffi_tls(); return (struct cffi_tls_s *)p; } #define save_errno save_errno_only #define restore_errno restore_errno_only cffi-1.5.2/c/parse_c_type.c0000664000175000017500000006207512657646311015744 0ustar arigoarigo00000000000000#include #include #include #include #define _CFFI_INTERNAL #include "../cffi/parse_c_type.h" enum token_e { TOK_STAR='*', TOK_OPEN_PAREN='(', TOK_CLOSE_PAREN=')', TOK_OPEN_BRACKET='[', TOK_CLOSE_BRACKET=']', TOK_COMMA=',', TOK_START=256, TOK_END, TOK_ERROR, TOK_IDENTIFIER, TOK_INTEGER, TOK_DOTDOTDOT, /* keywords */ TOK__BOOL, TOK_CHAR, //TOK__COMPLEX, TOK_CONST, TOK_DOUBLE, TOK_ENUM, TOK_FLOAT, //TOK__IMAGINARY, TOK_INT, TOK_LONG, TOK_SHORT, TOK_SIGNED, TOK_STRUCT, TOK_UNION, TOK_UNSIGNED, TOK_VOID, TOK_VOLATILE, TOK_CDECL, TOK_STDCALL, }; typedef struct { struct _cffi_parse_info_s *info; const char *input, *p; size_t size; // the next token is at 'p' and of length 'size' enum token_e kind; _cffi_opcode_t *output; size_t output_index; } token_t; static int is_space(char x) { return (x == ' ' || x == '\f' || x == '\n' || x == '\r' || x == '\t' || x == '\v'); } static int is_ident_first(char x) { return (('A' <= x && x <= 'Z') || ('a' <= x && x <= 'z') || x == '_' || x == '$'); /* '$' in names is supported here, for the struct names invented by cparser */ } static int is_digit(char x) { return ('0' <= x && x <= '9'); } static int is_hex_digit(char x) { return (('0' <= x && x <= '9') || ('A' <= x && x <= 'F') || ('a' <= x && x <= 'f')); } static int is_ident_next(char x) { return (is_ident_first(x) || is_digit(x)); } static char get_following_char(token_t *tok) { const char *p = tok->p + tok->size; if (tok->kind == TOK_ERROR) return 0; while (is_space(*p)) p++; return *p; } static int number_of_commas(token_t *tok) { const char *p = tok->p; int result = 0; int nesting = 0; while (1) { switch (*p++) { case ',': result += !nesting; break; case '(': nesting++; break; case ')': if ((--nesting) < 0) return result; break; case 0: return result; default: break; } } } static void next_token(token_t *tok) { const char *p = tok->p + tok->size; if (tok->kind == TOK_ERROR) return; while (!is_ident_first(*p)) { if (is_space(*p)) { p++; } else if (is_digit(*p)) { tok->kind = TOK_INTEGER; tok->p = p; tok->size = 1; if (p[1] == 'x' || p[1] == 'X') tok->size = 2; while (is_hex_digit(p[tok->size])) tok->size++; return; } else if (p[0] == '.' && p[1] == '.' && p[2] == '.') { tok->kind = TOK_DOTDOTDOT; tok->p = p; tok->size = 3; return; } else if (*p) { tok->kind = *p; tok->p = p; tok->size = 1; return; } else { tok->kind = TOK_END; tok->p = p; tok->size = 0; return; } } tok->kind = TOK_IDENTIFIER; tok->p = p; tok->size = 1; while (is_ident_next(p[tok->size])) tok->size++; switch (*p) { case '_': if (tok->size == 5 && !memcmp(p, "_Bool", 5)) tok->kind = TOK__BOOL; if (tok->size == 7 && !memcmp(p,"__cdecl",7)) tok->kind = TOK_CDECL; if (tok->size == 9 && !memcmp(p,"__stdcall",9))tok->kind = TOK_STDCALL; break; case 'c': if (tok->size == 4 && !memcmp(p, "char", 4)) tok->kind = TOK_CHAR; if (tok->size == 5 && !memcmp(p, "const", 5)) tok->kind = TOK_CONST; break; case 'd': if (tok->size == 6 && !memcmp(p, "double", 6)) tok->kind = TOK_DOUBLE; break; case 'e': if (tok->size == 4 && !memcmp(p, "enum", 4)) tok->kind = TOK_ENUM; break; case 'f': if (tok->size == 5 && !memcmp(p, "float", 5)) tok->kind = TOK_FLOAT; break; case 'i': if (tok->size == 3 && !memcmp(p, "int", 3)) tok->kind = TOK_INT; break; case 'l': if (tok->size == 4 && !memcmp(p, "long", 4)) tok->kind = TOK_LONG; break; case 's': if (tok->size == 5 && !memcmp(p, "short", 5)) tok->kind = TOK_SHORT; if (tok->size == 6 && !memcmp(p, "signed", 6)) tok->kind = TOK_SIGNED; if (tok->size == 6 && !memcmp(p, "struct", 6)) tok->kind = TOK_STRUCT; break; case 'u': if (tok->size == 5 && !memcmp(p, "union", 5)) tok->kind = TOK_UNION; if (tok->size == 8 && !memcmp(p,"unsigned",8)) tok->kind = TOK_UNSIGNED; break; case 'v': if (tok->size == 4 && !memcmp(p, "void", 4)) tok->kind = TOK_VOID; if (tok->size == 8 && !memcmp(p,"volatile",8)) tok->kind = TOK_VOLATILE; break; } } static int parse_error(token_t *tok, const char *msg) { if (tok->kind != TOK_ERROR) { tok->kind = TOK_ERROR; tok->info->error_location = tok->p - tok->input; tok->info->error_message = msg; } return -1; } static int write_ds(token_t *tok, _cffi_opcode_t ds) { size_t index = tok->output_index; if (index >= tok->info->output_size) { parse_error(tok, "internal type complexity limit reached"); return -1; } tok->output[index] = ds; tok->output_index = index + 1; return index; } #define MAX_SSIZE_T (((size_t)-1) >> 1) static int parse_complete(token_t *tok); static const char *get_common_type(const char *search, size_t search_len); static int parse_common_type_replacement(token_t *tok, const char *replacement); static int parse_sequel(token_t *tok, int outer) { /* Emit opcodes for the "sequel", which is the optional part of a type declaration that follows the type name, i.e. everything with '*', '[ ]', '( )'. Returns the entry point index pointing the innermost opcode (the one that corresponds to the complete type). The 'outer' argument is the index of the opcode outside this "sequel". */ int check_for_grouping, abi=0; _cffi_opcode_t result, *p_current; header: switch (tok->kind) { case TOK_STAR: outer = write_ds(tok, _CFFI_OP(_CFFI_OP_POINTER, outer)); next_token(tok); goto header; case TOK_CONST: /* ignored for now */ next_token(tok); goto header; case TOK_VOLATILE: /* ignored for now */ next_token(tok); goto header; case TOK_CDECL: case TOK_STDCALL: /* must be in a function; checked below */ abi = tok->kind; next_token(tok); goto header; default: break; } check_for_grouping = 1; if (tok->kind == TOK_IDENTIFIER) { next_token(tok); /* skip a potential variable name */ check_for_grouping = 0; } result = 0; p_current = &result; while (tok->kind == TOK_OPEN_PAREN) { next_token(tok); if (tok->kind == TOK_CDECL || tok->kind == TOK_STDCALL) { abi = tok->kind; next_token(tok); } if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR || tok->kind == TOK_CONST || tok->kind == TOK_VOLATILE || tok->kind == TOK_OPEN_BRACKET)) { /* just parentheses for grouping. Use a OP_NOOP to simplify */ int x; assert(p_current == &result); x = tok->output_index; p_current = tok->output + x; write_ds(tok, _CFFI_OP(_CFFI_OP_NOOP, 0)); x = parse_sequel(tok, x); result = _CFFI_OP(_CFFI_GETOP(0), x); } else { /* function type */ int arg_total, base_index, arg_next, flags=0; if (abi == TOK_STDCALL) { flags = 2; /* note that an ellipsis below will overwrite this flags, which is the goal: variadic functions are always cdecl */ } abi = 0; if (tok->kind == TOK_VOID && get_following_char(tok) == ')') { next_token(tok); } /* (over-)estimate 'arg_total'. May return 1 when it is really 0 */ arg_total = number_of_commas(tok) + 1; *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index); p_current = tok->output + tok->output_index; base_index = write_ds(tok, _CFFI_OP(_CFFI_OP_FUNCTION, 0)); if (base_index < 0) return -1; /* reserve (arg_total + 1) slots for the arguments and the final FUNCTION_END */ for (arg_next = 0; arg_next <= arg_total; arg_next++) if (write_ds(tok, _CFFI_OP(0, 0)) < 0) return -1; arg_next = base_index + 1; if (tok->kind != TOK_CLOSE_PAREN) { while (1) { int arg; _cffi_opcode_t oarg; if (tok->kind == TOK_DOTDOTDOT) { flags = 1; /* ellipsis */ next_token(tok); break; } arg = parse_complete(tok); switch (_CFFI_GETOP(tok->output[arg])) { case _CFFI_OP_ARRAY: case _CFFI_OP_OPEN_ARRAY: arg = _CFFI_GETARG(tok->output[arg]); /* fall-through */ case _CFFI_OP_FUNCTION: oarg = _CFFI_OP(_CFFI_OP_POINTER, arg); break; default: oarg = _CFFI_OP(_CFFI_OP_NOOP, arg); break; } assert(arg_next - base_index <= arg_total); tok->output[arg_next++] = oarg; if (tok->kind != TOK_COMMA) break; next_token(tok); } } tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, flags); } if (tok->kind != TOK_CLOSE_PAREN) return parse_error(tok, "expected ')'"); next_token(tok); } if (abi != 0) return parse_error(tok, "expected '('"); while (tok->kind == TOK_OPEN_BRACKET) { *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index); p_current = tok->output + tok->output_index; next_token(tok); if (tok->kind != TOK_CLOSE_BRACKET) { size_t length; int gindex; char *endptr; switch (tok->kind) { case TOK_INTEGER: errno = 0; if (sizeof(length) > sizeof(unsigned long)) { #ifdef MS_WIN32 # ifdef _WIN64 length = _strtoui64(tok->p, &endptr, 0); # else abort(); /* unreachable */ # endif #else length = strtoull(tok->p, &endptr, 0); #endif } else length = strtoul(tok->p, &endptr, 0); if (endptr != tok->p + tok->size) return parse_error(tok, "invalid number"); if (errno == ERANGE || length > MAX_SSIZE_T) return parse_error(tok, "number too large"); break; case TOK_IDENTIFIER: gindex = search_in_globals(tok->info->ctx, tok->p, tok->size); if (gindex >= 0) { const struct _cffi_global_s *g; g = &tok->info->ctx->globals[gindex]; if (_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT_INT || _CFFI_GETOP(g->type_op) == _CFFI_OP_ENUM) { int neg; struct _cffi_getconst_s gc; gc.ctx = tok->info->ctx; gc.gindex = gindex; neg = ((int(*)(struct _cffi_getconst_s*))g->address) (&gc); if (neg == 0 && gc.value > MAX_SSIZE_T) return parse_error(tok, "integer constant too large"); if (neg == 0 || gc.value == 0) { length = (size_t)gc.value; break; } if (neg != 1) return parse_error(tok, "disagreement about" " this constant's value"); } } /* fall-through to the default case */ default: return parse_error(tok, "expected a positive integer constant"); } next_token(tok); write_ds(tok, _CFFI_OP(_CFFI_OP_ARRAY, 0)); write_ds(tok, (_cffi_opcode_t)length); } else write_ds(tok, _CFFI_OP(_CFFI_OP_OPEN_ARRAY, 0)); if (tok->kind != TOK_CLOSE_BRACKET) return parse_error(tok, "expected ']'"); next_token(tok); } *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), outer); return _CFFI_GETARG(result); } static int search_sorted(const char *const *base, size_t item_size, int array_len, const char *search, size_t search_len) { int left = 0, right = array_len; const char *baseptr = (const char *)base; while (left < right) { int middle = (left + right) / 2; const char *src = *(const char *const *)(baseptr + middle * item_size); int diff = strncmp(src, search, search_len); if (diff == 0 && src[search_len] == '\0') return middle; else if (diff >= 0) right = middle; else left = middle + 1; } return -1; } #define MAKE_SEARCH_FUNC(FIELD) \ static \ int search_in_##FIELD(const struct _cffi_type_context_s *ctx, \ const char *search, size_t search_len) \ { \ return search_sorted(&ctx->FIELD->name, sizeof(*ctx->FIELD), \ ctx->num_##FIELD, search, search_len); \ } MAKE_SEARCH_FUNC(globals) MAKE_SEARCH_FUNC(struct_unions) MAKE_SEARCH_FUNC(typenames) MAKE_SEARCH_FUNC(enums) #undef MAKE_SEARCH_FUNC static int search_standard_typename(const char *p, size_t size) { if (size < 6 || p[size-2] != '_' || p[size-1] != 't') return -1; switch (p[4]) { case '1': if (size == 8 && !memcmp(p, "uint16", 6)) return _CFFI_PRIM_UINT16; break; case '2': if (size == 7 && !memcmp(p, "int32", 5)) return _CFFI_PRIM_INT32; break; case '3': if (size == 8 && !memcmp(p, "uint32", 6)) return _CFFI_PRIM_UINT32; break; case '4': if (size == 7 && !memcmp(p, "int64", 5)) return _CFFI_PRIM_INT64; break; case '6': if (size == 8 && !memcmp(p, "uint64", 6)) return _CFFI_PRIM_UINT64; if (size == 7 && !memcmp(p, "int16", 5)) return _CFFI_PRIM_INT16; break; case '8': if (size == 7 && !memcmp(p, "uint8", 5)) return _CFFI_PRIM_UINT8; break; case 'a': if (size == 8 && !memcmp(p, "intmax", 6)) return _CFFI_PRIM_INTMAX; break; case 'e': if (size == 7 && !memcmp(p, "ssize", 5)) return _CFFI_PRIM_SSIZE; break; case 'f': if (size == 11 && !memcmp(p, "int_fast8", 9)) return _CFFI_PRIM_INT_FAST8; if (size == 12 && !memcmp(p, "int_fast16", 10)) return _CFFI_PRIM_INT_FAST16; if (size == 12 && !memcmp(p, "int_fast32", 10)) return _CFFI_PRIM_INT_FAST32; if (size == 12 && !memcmp(p, "int_fast64", 10)) return _CFFI_PRIM_INT_FAST64; break; case 'i': if (size == 9 && !memcmp(p, "ptrdiff", 7)) return _CFFI_PRIM_PTRDIFF; break; case 'l': if (size == 12 && !memcmp(p, "int_least8", 10)) return _CFFI_PRIM_INT_LEAST8; if (size == 13 && !memcmp(p, "int_least16", 11)) return _CFFI_PRIM_INT_LEAST16; if (size == 13 && !memcmp(p, "int_least32", 11)) return _CFFI_PRIM_INT_LEAST32; if (size == 13 && !memcmp(p, "int_least64", 11)) return _CFFI_PRIM_INT_LEAST64; break; case 'm': if (size == 9 && !memcmp(p, "uintmax", 7)) return _CFFI_PRIM_UINTMAX; break; case 'p': if (size == 9 && !memcmp(p, "uintptr", 7)) return _CFFI_PRIM_UINTPTR; break; case 'r': if (size == 7 && !memcmp(p, "wchar", 5)) return _CFFI_PRIM_WCHAR; break; case 't': if (size == 8 && !memcmp(p, "intptr", 6)) return _CFFI_PRIM_INTPTR; break; case '_': if (size == 6 && !memcmp(p, "size", 4)) return _CFFI_PRIM_SIZE; if (size == 6 && !memcmp(p, "int8", 4)) return _CFFI_PRIM_INT8; if (size >= 12) { switch (p[10]) { case '1': if (size == 14 && !memcmp(p, "uint_least16", 12)) return _CFFI_PRIM_UINT_LEAST16; break; case '2': if (size == 13 && !memcmp(p, "uint_fast32", 11)) return _CFFI_PRIM_UINT_FAST32; break; case '3': if (size == 14 && !memcmp(p, "uint_least32", 12)) return _CFFI_PRIM_UINT_LEAST32; break; case '4': if (size == 13 && !memcmp(p, "uint_fast64", 11)) return _CFFI_PRIM_UINT_FAST64; break; case '6': if (size == 14 && !memcmp(p, "uint_least64", 12)) return _CFFI_PRIM_UINT_LEAST64; if (size == 13 && !memcmp(p, "uint_fast16", 11)) return _CFFI_PRIM_UINT_FAST16; break; case '8': if (size == 13 && !memcmp(p, "uint_least8", 11)) return _CFFI_PRIM_UINT_LEAST8; break; case '_': if (size == 12 && !memcmp(p, "uint_fast8", 10)) return _CFFI_PRIM_UINT_FAST8; break; default: break; } } break; default: break; } return -1; } static int parse_complete(token_t *tok) { unsigned int t0; _cffi_opcode_t t1; int modifiers_length, modifiers_sign; qualifiers: switch (tok->kind) { case TOK_CONST: /* ignored for now */ next_token(tok); goto qualifiers; case TOK_VOLATILE: /* ignored for now */ next_token(tok); goto qualifiers; default: ; } modifiers_length = 0; modifiers_sign = 0; modifiers: switch (tok->kind) { case TOK_SHORT: if (modifiers_length != 0) return parse_error(tok, "'short' after another 'short' or 'long'"); modifiers_length--; next_token(tok); goto modifiers; case TOK_LONG: if (modifiers_length < 0) return parse_error(tok, "'long' after 'short'"); if (modifiers_length >= 2) return parse_error(tok, "'long long long' is too long"); modifiers_length++; next_token(tok); goto modifiers; case TOK_SIGNED: if (modifiers_sign) return parse_error(tok, "multiple 'signed' or 'unsigned'"); modifiers_sign++; next_token(tok); goto modifiers; case TOK_UNSIGNED: if (modifiers_sign) return parse_error(tok, "multiple 'signed' or 'unsigned'"); modifiers_sign--; next_token(tok); goto modifiers; default: break; } if (modifiers_length || modifiers_sign) { switch (tok->kind) { case TOK_VOID: case TOK__BOOL: case TOK_FLOAT: case TOK_STRUCT: case TOK_UNION: case TOK_ENUM: return parse_error(tok, "invalid combination of types"); case TOK_DOUBLE: if (modifiers_sign != 0 || modifiers_length != 1) return parse_error(tok, "invalid combination of types"); next_token(tok); t0 = _CFFI_PRIM_LONGDOUBLE; break; case TOK_CHAR: if (modifiers_length != 0) return parse_error(tok, "invalid combination of types"); modifiers_length = -2; /* fall-through */ case TOK_INT: next_token(tok); /* fall-through */ default: if (modifiers_sign >= 0) switch (modifiers_length) { case -2: t0 = _CFFI_PRIM_SCHAR; break; case -1: t0 = _CFFI_PRIM_SHORT; break; case 1: t0 = _CFFI_PRIM_LONG; break; case 2: t0 = _CFFI_PRIM_LONGLONG; break; default: t0 = _CFFI_PRIM_INT; break; } else switch (modifiers_length) { case -2: t0 = _CFFI_PRIM_UCHAR; break; case -1: t0 = _CFFI_PRIM_USHORT; break; case 1: t0 = _CFFI_PRIM_ULONG; break; case 2: t0 = _CFFI_PRIM_ULONGLONG; break; default: t0 = _CFFI_PRIM_UINT; break; } } t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, t0); } else { switch (tok->kind) { case TOK_INT: t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT); break; case TOK_CHAR: t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_CHAR); break; case TOK_VOID: t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_VOID); break; case TOK__BOOL: t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_BOOL); break; case TOK_FLOAT: t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOAT); break; case TOK_DOUBLE: t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLE); break; case TOK_IDENTIFIER: { const char *replacement; int n = search_in_typenames(tok->info->ctx, tok->p, tok->size); if (n >= 0) { t1 = _CFFI_OP(_CFFI_OP_TYPENAME, n); break; } n = search_standard_typename(tok->p, tok->size); if (n >= 0) { t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, n); break; } replacement = get_common_type(tok->p, tok->size); if (replacement != NULL) { n = parse_common_type_replacement(tok, replacement); if (n < 0) return parse_error(tok, "internal error, please report!"); t1 = _CFFI_OP(_CFFI_OP_NOOP, n); break; } return parse_error(tok, "undefined type name"); } case TOK_STRUCT: case TOK_UNION: { int n, kind = tok->kind; next_token(tok); if (tok->kind != TOK_IDENTIFIER) return parse_error(tok, "struct or union name expected"); n = search_in_struct_unions(tok->info->ctx, tok->p, tok->size); if (n < 0) { if (kind == TOK_STRUCT && tok->size == 8 && !memcmp(tok->p, "_IO_FILE", 8)) n = _CFFI__IO_FILE_STRUCT; else return parse_error(tok, "undefined struct/union name"); } else if (((tok->info->ctx->struct_unions[n].flags & _CFFI_F_UNION) != 0) ^ (kind == TOK_UNION)) return parse_error(tok, "wrong kind of tag: struct vs union"); t1 = _CFFI_OP(_CFFI_OP_STRUCT_UNION, n); break; } case TOK_ENUM: { int n; next_token(tok); if (tok->kind != TOK_IDENTIFIER) return parse_error(tok, "enum name expected"); n = search_in_enums(tok->info->ctx, tok->p, tok->size); if (n < 0) return parse_error(tok, "undefined enum name"); t1 = _CFFI_OP(_CFFI_OP_ENUM, n); break; } default: return parse_error(tok, "identifier expected"); } next_token(tok); } return parse_sequel(tok, write_ds(tok, t1)); } static int parse_c_type_from(struct _cffi_parse_info_s *info, size_t *output_index, const char *input) { int result; token_t token; token.info = info; token.kind = TOK_START; token.input = input; token.p = input; token.size = 0; token.output = info->output; token.output_index = *output_index; next_token(&token); result = parse_complete(&token); *output_index = token.output_index; if (token.kind != TOK_END) return parse_error(&token, "unexpected symbol"); return result; } static int parse_c_type(struct _cffi_parse_info_s *info, const char *input) { size_t output_index = 0; return parse_c_type_from(info, &output_index, input); } static int parse_common_type_replacement(token_t *tok, const char *replacement) { return parse_c_type_from(tok->info, &tok->output_index, replacement); } cffi-1.5.2/c/misc_win32.h0000664000175000017500000001442012657646311015240 0ustar arigoarigo00000000000000#include /* for alloca() */ /************************************************************/ /* errno and GetLastError support */ #include "misc_thread_common.h" static DWORD cffi_tls_index = TLS_OUT_OF_INDEXES; BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD reason_for_call, LPVOID reserved) { LPVOID p; switch (reason_for_call) { case DLL_THREAD_DETACH: if (cffi_tls_index != TLS_OUT_OF_INDEXES) { p = TlsGetValue(cffi_tls_index); if (p != NULL) { TlsSetValue(cffi_tls_index, NULL); cffi_thread_shutdown(p); } } break; default: break; } return TRUE; } static void init_cffi_tls(void) { if (cffi_tls_index == TLS_OUT_OF_INDEXES) { cffi_tls_index = TlsAlloc(); if (cffi_tls_index == TLS_OUT_OF_INDEXES) PyErr_SetString(PyExc_WindowsError, "TlsAlloc() failed"); } } static struct cffi_tls_s *get_cffi_tls(void) { LPVOID p = TlsGetValue(cffi_tls_index); if (p == NULL) { p = malloc(sizeof(struct cffi_tls_s)); if (p == NULL) return NULL; memset(p, 0, sizeof(struct cffi_tls_s)); TlsSetValue(cffi_tls_index, p); } return (struct cffi_tls_s *)p; } #ifdef USE__THREAD # error "unexpected USE__THREAD on Windows" #endif static void save_errno(void) { int current_err = errno; int current_lasterr = GetLastError(); struct cffi_tls_s *p = get_cffi_tls(); if (p != NULL) { p->saved_errno = current_err; p->saved_lasterror = current_lasterr; } /* else: cannot report the error */ } static void restore_errno(void) { struct cffi_tls_s *p = get_cffi_tls(); if (p != NULL) { SetLastError(p->saved_lasterror); errno = p->saved_errno; } /* else: cannot report the error */ } /************************************************************/ #if PY_MAJOR_VERSION >= 3 static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds) { int err = -1; int len; WCHAR *s_buf = NULL; /* Free via LocalFree */ PyObject *v, *message; static char *keywords[] = {"code", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err)) return NULL; if (err == -1) { struct cffi_tls_s *p = get_cffi_tls(); 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, PyObject *kwds) { int err = -1; int len; char *s; char *s_buf = NULL; /* Free via LocalFree */ char s_small_buf[40]; /* Room for "Windows Error 0xFFFFFFFFFFFFFFFF" */ PyObject *v; static char *keywords[] = {"code", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err)) return NULL; if (err == -1) { struct cffi_tls_s *p = get_cffi_tls(); 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) { void *address = GetProcAddress((HMODULE)handle, symbol); #ifndef MS_WIN64 if (!address) { /* If 'symbol' is not found, then try '_symbol@N' for N in (0, 4, 8, 12, ..., 124). Unlike ctypes, we try to do that for any symbol, although in theory it should only be done for __stdcall functions. */ int i; char *mangled_name = alloca(1 + strlen(symbol) + 1 + 3 + 1); if (!mangled_name) return NULL; for (i = 0; i < 32; i++) { sprintf(mangled_name, "_%s@%d", symbol, i * 4); address = GetProcAddress((HMODULE)handle, mangled_name); if (address) break; } } #endif return address; } static int dlclose(void *handle) { return FreeLibrary((HMODULE)handle) ? 0 : -1; } 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; } /************************************************************/ /* obscure */ #define ffi_prep_closure(a,b,c,d) ffi_prep_closure_loc(a,b,c,d,a) cffi-1.5.2/c/minibuffer.h0000664000175000017500000002137012657646311015413 0ustar arigoarigo00000000000000 /* 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, (PyObject *)self, self->mb_data, self->mb_size, /*readonly=*/0, flags); } 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-1.5.2/c/lib_obj.c0000664000175000017500000005121212657646311014656 0ustar arigoarigo00000000000000 /* A Lib object is what is in the "lib" attribute of a C extension module originally created by recompile(). A Lib object is special in the sense that it has a custom __getattr__ which returns C globals, functions and constants. It raises AttributeError for anything else, even attrs like '__class__'. A Lib object has got a reference to the _cffi_type_context_s structure, which is used to create lazily the objects returned by __getattr__. */ struct CPyExtFunc_s { PyMethodDef md; void *direct_fn; int type_index; }; static const char cpyextfunc_doc[] = "direct call to the C function of the same name"; struct LibObject_s { PyObject_HEAD builder_c_t *l_types_builder; /* same as the one on the ffi object */ PyObject *l_dict; /* content, built lazily */ PyObject *l_libname; /* some string that gives the name of the lib */ FFIObject *l_ffi; /* reference back to the ffi object */ void *l_libhandle; /* the dlopen()ed handle, if any */ }; static struct CPyExtFunc_s *_cpyextfunc_get(PyObject *x) { struct CPyExtFunc_s *exf; if (!PyCFunction_Check(x)) return NULL; if (!LibObject_Check(PyCFunction_GET_SELF(x))) return NULL; exf = (struct CPyExtFunc_s *)(((PyCFunctionObject *)x) -> m_ml); if (exf->md.ml_doc != cpyextfunc_doc) return NULL; return exf; } static PyObject *_cpyextfunc_type(LibObject *lib, struct CPyExtFunc_s *exf) { PyObject *tuple, *result; tuple = realize_c_type_or_func(lib->l_types_builder, lib->l_types_builder->ctx.types, exf->type_index); if (tuple == NULL) return NULL; /* 'tuple' is a tuple of length 1 containing the real CT_FUNCTIONPTR object */ result = PyTuple_GetItem(tuple, 0); Py_XINCREF(result); Py_DECREF(tuple); return result; } static PyObject *_cpyextfunc_type_index(PyObject *x) { struct CPyExtFunc_s *exf; LibObject *lib; assert(PyErr_Occurred()); exf = _cpyextfunc_get(x); if (exf == NULL) return NULL; /* still the same exception is set */ PyErr_Clear(); lib = (LibObject *)PyCFunction_GET_SELF(x); return _cpyextfunc_type(lib, exf); } static void cdlopen_close_ignore_errors(void *libhandle); /* forward */ static void *cdlopen_fetch(PyObject *libname, void *libhandle, char *symbol); static void lib_dealloc(LibObject *lib) { cdlopen_close_ignore_errors(lib->l_libhandle); Py_DECREF(lib->l_dict); Py_DECREF(lib->l_libname); Py_DECREF(lib->l_ffi); PyObject_Del(lib); } static int lib_traverse(LibObject *lib, visitproc visit, void *arg) { Py_VISIT(lib->l_dict); Py_VISIT(lib->l_libname); Py_VISIT(lib->l_ffi); return 0; } static PyObject *lib_repr(LibObject *lib) { return PyText_FromFormat("", PyText_AS_UTF8(lib->l_libname)); } static PyObject *lib_build_cpython_func(LibObject *lib, const struct _cffi_global_s *g, const char *s, int flags) { /* First make sure the argument types and return type are really built. The C extension code can then assume that they are, by calling _cffi_type(). */ CTypeDescrObject *ct; struct CPyExtFunc_s *xfunc; int i, type_index = _CFFI_GETARG(g->type_op); _cffi_opcode_t *opcodes = lib->l_types_builder->ctx.types; if ((((uintptr_t)opcodes[type_index]) & 1) == 0) { /* the function type was already built. No need to force the arg and return value to be built again. */ } else { assert(_CFFI_GETOP(opcodes[type_index]) == _CFFI_OP_FUNCTION); /* return type: */ ct = realize_c_type(lib->l_types_builder, opcodes, _CFFI_GETARG(opcodes[type_index])); if (ct == NULL) return NULL; Py_DECREF(ct); /* argument types: */ i = type_index + 1; while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) { ct = realize_c_type(lib->l_types_builder, opcodes, i); if (ct == NULL) return NULL; Py_DECREF(ct); i++; } } /* xxx the few bytes of memory we allocate here leak, but it's a minor concern because it should only occur for CPYTHON_BLTN. There is one per real C function in a CFFI C extension module. CPython never unloads its C extension modules anyway. */ xfunc = PyMem_Malloc(sizeof(struct CPyExtFunc_s)); if (xfunc == NULL) { PyErr_NoMemory(); return NULL; } memset((char *)xfunc, 0, sizeof(struct CPyExtFunc_s)); assert(g->address); xfunc->md.ml_meth = (PyCFunction)g->address; xfunc->md.ml_flags = flags; xfunc->md.ml_name = g->name; xfunc->md.ml_doc = cpyextfunc_doc; xfunc->direct_fn = g->size_or_direct_fn; xfunc->type_index = type_index; return PyCFunction_NewEx(&xfunc->md, (PyObject *)lib, lib->l_libname); } static PyObject *lib_build_and_cache_attr(LibObject *lib, PyObject *name, int recursion) { /* does not return a new reference! */ PyObject *x; int index; const struct _cffi_global_s *g; CTypeDescrObject *ct; builder_c_t *types_builder = lib->l_types_builder; char *s = PyText_AsUTF8(name); if (s == NULL) return NULL; index = search_in_globals(&types_builder->ctx, s, strlen(s)); if (index < 0) { if (types_builder->included_libs != NULL) { Py_ssize_t i; PyObject *included_ffis = types_builder->included_ffis; PyObject *included_libs = types_builder->included_libs; if (recursion > 100) { PyErr_SetString(PyExc_RuntimeError, "recursion overflow in ffi.include() delegations"); return NULL; } for (i = 0; i < PyTuple_GET_SIZE(included_libs); i++) { LibObject *lib1; lib1 = (LibObject *)PyTuple_GET_ITEM(included_libs, i); if (lib1 != NULL) { x = PyDict_GetItem(lib1->l_dict, name); if (x != NULL) { Py_INCREF(x); goto found; } x = lib_build_and_cache_attr(lib1, name, recursion + 1); if (x != NULL) { Py_INCREF(x); goto found; } } else { FFIObject *ffi1; ffi1 = (FFIObject *)PyTuple_GetItem(included_ffis, i); if (ffi1 == NULL) return NULL; x = ffi_fetch_int_constant(ffi1, s, recursion + 1); if (x != NULL) goto found; } if (PyErr_Occurred()) return NULL; } } if (recursion > 0) return NULL; /* no error set, continue looking elsewhere */ PyErr_Format(PyExc_AttributeError, "cffi library '%.200s' has no function, constant " "or global variable named '%.200s'", PyText_AS_UTF8(lib->l_libname), s); return NULL; } g = &types_builder->ctx.globals[index]; switch (_CFFI_GETOP(g->type_op)) { case _CFFI_OP_CPYTHON_BLTN_V: x = lib_build_cpython_func(lib, g, s, METH_VARARGS); break; case _CFFI_OP_CPYTHON_BLTN_N: x = lib_build_cpython_func(lib, g, s, METH_NOARGS); break; case _CFFI_OP_CPYTHON_BLTN_O: x = lib_build_cpython_func(lib, g, s, METH_O); break; case _CFFI_OP_CONSTANT_INT: case _CFFI_OP_ENUM: { /* a constant integer whose value, in an "unsigned long long", is obtained by calling the function at g->address */ x = realize_global_int(types_builder, index); break; } case _CFFI_OP_CONSTANT: case _CFFI_OP_DLOPEN_CONST: { /* a constant which is not of integer type */ char *data; ct = realize_c_type(types_builder, types_builder->ctx.types, _CFFI_GETARG(g->type_op)); if (ct == NULL) return NULL; if (ct->ct_size <= 0) { PyErr_Format(FFIError, "constant '%s' is of type '%s', " "whose size is not known", s, ct->ct_name); return NULL; } if (g->address == NULL) { /* for dlopen() style */ assert(_CFFI_GETOP(g->type_op) == _CFFI_OP_DLOPEN_CONST); data = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s); if (data == NULL) return NULL; } else { /* xxx the few bytes of memory we allocate here leak, but it's a minor concern because it should only occur for OP_CONSTANT. There is one per real non-integer C constant in a CFFI C extension module. CPython never unloads its C extension modules anyway. Note that we used to do alloca(), but see issue #198. */ assert(_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT); data = PyMem_Malloc(ct->ct_size); if (data == NULL) { PyErr_NoMemory(); return NULL; } ((void(*)(char*))g->address)(data); } x = convert_to_object(data, ct); Py_DECREF(ct); break; } case _CFFI_OP_GLOBAL_VAR: { /* global variable of the exact type specified here (nowadays, only used by the ABI mode or backward compatibility; see _CFFI_OP_GLOBAL_VAR_F for the API mode) */ Py_ssize_t g_size = (Py_ssize_t)g->size_or_direct_fn; ct = realize_c_type(types_builder, types_builder->ctx.types, _CFFI_GETARG(g->type_op)); if (ct == NULL) return NULL; if (g_size != ct->ct_size && g_size != 0 && ct->ct_size > 0) { PyErr_Format(FFIError, "global variable '%.200s' should be %zd bytes " "according to the cdef, but is actually %zd", s, ct->ct_size, g_size); x = NULL; } else { void *address = g->address; if (address == NULL) { /* for dlopen() style */ address = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s); if (address == NULL) return NULL; } x = make_global_var(name, ct, address, NULL); } Py_DECREF(ct); break; } case _CFFI_OP_GLOBAL_VAR_F: ct = realize_c_type(types_builder, types_builder->ctx.types, _CFFI_GETARG(g->type_op)); if (ct == NULL) return NULL; x = make_global_var(name, ct, NULL, (gs_fetch_addr_fn)g->address); Py_DECREF(ct); break; case _CFFI_OP_DLOPEN_FUNC: { /* For dlopen(): the function of the given 'name'. We use dlsym() to get the address of something in the dynamic library, which we interpret as being exactly a function of the specified type. */ PyObject *ct1; void *address = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s); if (address == NULL) return NULL; ct1 = realize_c_type_or_func(types_builder, types_builder->ctx.types, _CFFI_GETARG(g->type_op)); if (ct1 == NULL) return NULL; assert(!CTypeDescr_Check(ct1)); /* must be a function */ x = new_simple_cdata(address, unwrap_fn_as_fnptr(ct1)); Py_DECREF(ct1); break; } case _CFFI_OP_EXTERN_PYTHON: /* for reading 'lib.bar' where bar is declared with extern "Python" */ ct = realize_c_type(types_builder, types_builder->ctx.types, _CFFI_GETARG(g->type_op)); if (ct == NULL) return NULL; x = convert_to_object((char *)&g->size_or_direct_fn, ct); Py_DECREF(ct); break; default: PyErr_Format(PyExc_NotImplementedError, "in lib_build_attr: op=%d", (int)_CFFI_GETOP(g->type_op)); return NULL; } found: if (x != NULL) { int err = PyDict_SetItem(lib->l_dict, name, x); Py_DECREF(x); if (err < 0) /* else there is still one ref left in the dict */ return NULL; } return x; } #define LIB_GET_OR_CACHE_ADDR(x, lib, name, error) \ do { \ x = PyDict_GetItem(lib->l_dict, name); \ if (x == NULL) { \ x = lib_build_and_cache_attr(lib, name, 0); \ if (x == NULL) { \ error; \ } \ } \ } while (0) static PyObject *_lib_dir1(LibObject *lib, int ignore_global_vars) { const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals; int i, count = 0, total = lib->l_types_builder->ctx.num_globals; PyObject *s, *lst = PyList_New(total); if (lst == NULL) return NULL; for (i = 0; i < total; i++) { if (ignore_global_vars) { int op = _CFFI_GETOP(g[i].type_op); if (op == _CFFI_OP_GLOBAL_VAR || op == _CFFI_OP_GLOBAL_VAR_F) continue; } s = PyText_FromString(g[i].name); if (s == NULL) goto error; PyList_SET_ITEM(lst, count, s); count++; } if (PyList_SetSlice(lst, count, total, NULL) < 0) goto error; return lst; error: Py_DECREF(lst); return NULL; } static PyObject *_lib_dict(LibObject *lib) { const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals; int i, total = lib->l_types_builder->ctx.num_globals; PyObject *name, *x, *d = PyDict_New(); if (d == NULL) return NULL; for (i = 0; i < total; i++) { name = PyText_FromString(g[i].name); if (name == NULL) goto error; LIB_GET_OR_CACHE_ADDR(x, lib, name, goto error); if (PyDict_SetItem(d, name, x) < 0) goto error; Py_DECREF(name); } return d; error: Py_XDECREF(name); Py_DECREF(d); return NULL; } static PyObject *lib_getattr(LibObject *lib, PyObject *name) { char *p; PyObject *x; LIB_GET_OR_CACHE_ADDR(x, lib, name, goto missing); if (GlobSupport_Check(x)) { return read_global_var((GlobSupportObject *)x); } Py_INCREF(x); return x; missing: p = PyText_AsUTF8(name); if (p == NULL) return NULL; if (strcmp(p, "__all__") == 0) { PyErr_Clear(); return _lib_dir1(lib, 1); } if (strcmp(p, "__dict__") == 0) { PyErr_Clear(); return _lib_dict(lib); } if (strcmp(p, "__class__") == 0) { PyErr_Clear(); x = (PyObject *)Py_TYPE(lib); Py_INCREF(x); return x; } /* this hack is for Python 3.5 */ if (strcmp(p, "__name__") == 0) { PyErr_Clear(); return lib_repr(lib); } return NULL; } static int lib_setattr(LibObject *lib, PyObject *name, PyObject *val) { PyObject *x; LIB_GET_OR_CACHE_ADDR(x, lib, name, return -1); if (val == NULL) { PyErr_SetString(PyExc_AttributeError, "C attribute cannot be deleted"); return -1; } if (GlobSupport_Check(x)) { return write_global_var((GlobSupportObject *)x, val); } PyErr_Format(PyExc_AttributeError, "cannot write to function or constant '%.200s'", PyText_Check(name) ? PyText_AS_UTF8(name) : "?"); return -1; } static PyObject *lib_dir(PyObject *self, PyObject *noarg) { return _lib_dir1((LibObject *)self, 0); } static PyMethodDef lib_methods[] = { {"__dir__", lib_dir, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; static PyTypeObject Lib_Type = { PyVarObject_HEAD_INIT(NULL, 0) "CompiledLib", sizeof(LibObject), 0, (destructor)lib_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)lib_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ (getattrofunc)lib_getattr, /* tp_getattro */ (setattrofunc)lib_setattr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ (traverseproc)lib_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ lib_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ offsetof(LibObject, l_dict), /* tp_dictoffset */ }; static LibObject *lib_internal_new(FFIObject *ffi, char *module_name, void *dlopen_libhandle) { LibObject *lib; PyObject *libname, *dict; libname = PyText_FromString(module_name); if (libname == NULL) goto err1; dict = PyDict_New(); if (dict == NULL) goto err2; lib = PyObject_New(LibObject, &Lib_Type); if (lib == NULL) goto err3; lib->l_types_builder = &ffi->types_builder; lib->l_dict = dict; lib->l_libname = libname; Py_INCREF(ffi); lib->l_ffi = ffi; lib->l_libhandle = dlopen_libhandle; return lib; err3: Py_DECREF(dict); err2: Py_DECREF(libname); err1: cdlopen_close_ignore_errors(dlopen_libhandle); return NULL; } static PyObject *address_of_global_var(PyObject *args) { LibObject *lib; PyObject *x, *o_varname; char *varname; if (!PyArg_ParseTuple(args, "O!s", &Lib_Type, &lib, &varname)) return NULL; /* rebuild a string from 'varname', to do typechecks and to force a unicode back to a plain string (on python 2) */ o_varname = PyText_FromString(varname); if (o_varname == NULL) return NULL; LIB_GET_OR_CACHE_ADDR(x, lib, o_varname, goto error); Py_DECREF(o_varname); if (GlobSupport_Check(x)) { return cg_addressof_global_var((GlobSupportObject *)x); } else { struct CPyExtFunc_s *exf = _cpyextfunc_get(x); if (exf != NULL) { /* an OP_CPYTHON_BLTN: '&func' returns a cdata */ PyObject *ct; if (exf->direct_fn == NULL) { Py_INCREF(x); /* backward compatibility */ return x; } ct = _cpyextfunc_type(lib, exf); if (ct == NULL) return NULL; x = new_simple_cdata(exf->direct_fn, (CTypeDescrObject *)ct); Py_DECREF(ct); return x; } if (CData_Check(x) && /* a constant functionptr cdata: 'f == &f' */ (((CDataObject *)x)->c_type->ct_flags & CT_FUNCTIONPTR) != 0) { Py_INCREF(x); return x; } else { PyErr_Format(PyExc_AttributeError, "cannot take the address of the constant '%.200s'", varname); return NULL; } } error: Py_DECREF(o_varname); return NULL; } cffi-1.5.2/c/commontypes.c0000664000175000017500000001416012657646311015634 0ustar arigoarigo00000000000000/* This file must be kept in alphabetical order. See test_commontypes.py */ #define EQ(key, value) key "\0" value /* string concatenation */ #ifdef _WIN64 # define W32_64(X,Y) Y # else # define W32_64(X,Y) X # endif static const char *common_simple_types[] = { #ifdef MS_WIN32 /* Windows types */ EQ("ATOM", "WORD"), EQ("BOOL", "int"), EQ("BOOLEAN", "BYTE"), EQ("BYTE", "unsigned char"), EQ("CCHAR", "char"), EQ("CHAR", "char"), EQ("COLORREF", "DWORD"), EQ("DWORD", "unsigned long"), EQ("DWORD32", "unsigned int"), EQ("DWORD64", "unsigned long long"), EQ("DWORDLONG", "ULONGLONG"), EQ("DWORD_PTR", "ULONG_PTR"), #endif EQ("FILE", "struct _IO_FILE"), #ifdef MS_WIN32 /* more Windows types */ EQ("FLOAT", "float"), EQ("HACCEL", "HANDLE"), EQ("HALF_PTR", W32_64("short","int")), EQ("HANDLE", "PVOID"), EQ("HBITMAP", "HANDLE"), EQ("HBRUSH", "HANDLE"), EQ("HCOLORSPACE", "HANDLE"), EQ("HCONV", "HANDLE"), EQ("HCONVLIST", "HANDLE"), EQ("HCURSOR", "HICON"), EQ("HDC", "HANDLE"), EQ("HDDEDATA", "HANDLE"), EQ("HDESK", "HANDLE"), EQ("HDROP", "HANDLE"), EQ("HDWP", "HANDLE"), EQ("HENHMETAFILE", "HANDLE"), EQ("HFILE", "int"), EQ("HFONT", "HANDLE"), EQ("HGDIOBJ", "HANDLE"), EQ("HGLOBAL", "HANDLE"), EQ("HHOOK", "HANDLE"), EQ("HICON", "HANDLE"), EQ("HINSTANCE", "HANDLE"), EQ("HKEY", "HANDLE"), EQ("HKL", "HANDLE"), EQ("HLOCAL", "HANDLE"), EQ("HMENU", "HANDLE"), EQ("HMETAFILE", "HANDLE"), EQ("HMODULE", "HINSTANCE"), EQ("HMONITOR", "HANDLE"), EQ("HPALETTE", "HANDLE"), EQ("HPEN", "HANDLE"), EQ("HRESULT", "LONG"), EQ("HRGN", "HANDLE"), EQ("HRSRC", "HANDLE"), EQ("HSZ", "HANDLE"), EQ("HWND", "HANDLE"), EQ("INT", "int"), EQ("INT16", "short"), EQ("INT32", "int"), EQ("INT64", "long long"), EQ("INT8", "signed char"), EQ("INT_PTR", W32_64("int","long long")), EQ("LANGID", "WORD"), EQ("LCID", "DWORD"), EQ("LCTYPE", "DWORD"), EQ("LGRPID", "DWORD"), EQ("LONG", "long"), EQ("LONG32", "int"), EQ("LONG64", "long long"), EQ("LONGLONG", "long long"), EQ("LONG_PTR", W32_64("long","long long")), EQ("LPARAM", "LONG_PTR"), EQ("LPBOOL", "BOOL *"), EQ("LPBYTE", "BYTE *"), EQ("LPCOLORREF", "DWORD *"), EQ("LPCSTR", "const char *"), EQ("LPCVOID", "const void *"), EQ("LPCWSTR", "const WCHAR *"), EQ("LPDWORD", "DWORD *"), EQ("LPHANDLE", "HANDLE *"), EQ("LPINT", "int *"), EQ("LPLONG", "long *"), EQ("LPSTR", "CHAR *"), EQ("LPVOID", "void *"), EQ("LPWORD", "WORD *"), EQ("LPWSTR", "WCHAR *"), EQ("LRESULT", "LONG_PTR"), EQ("PBOOL", "BOOL *"), EQ("PBOOLEAN", "BOOLEAN *"), EQ("PBYTE", "BYTE *"), EQ("PCHAR", "CHAR *"), EQ("PCSTR", "const CHAR *"), EQ("PCWSTR", "const WCHAR *"), EQ("PDWORD", "DWORD *"), EQ("PDWORD32", "DWORD32 *"), EQ("PDWORD64", "DWORD64 *"), EQ("PDWORDLONG", "DWORDLONG *"), EQ("PDWORD_PTR", "DWORD_PTR *"), EQ("PFLOAT", "FLOAT *"), EQ("PHALF_PTR", "HALF_PTR *"), EQ("PHANDLE", "HANDLE *"), EQ("PHKEY", "HKEY *"), EQ("PINT", "int *"), EQ("PINT16", "INT16 *"), EQ("PINT32", "INT32 *"), EQ("PINT64", "INT64 *"), EQ("PINT8", "INT8 *"), EQ("PINT_PTR", "INT_PTR *"), EQ("PLCID", "PDWORD"), EQ("PLONG", "LONG *"), EQ("PLONG32", "LONG32 *"), EQ("PLONG64", "LONG64 *"), EQ("PLONGLONG", "LONGLONG *"), EQ("PLONG_PTR", "LONG_PTR *"), EQ("PSHORT", "SHORT *"), EQ("PSIZE_T", "SIZE_T *"), EQ("PSSIZE_T", "SSIZE_T *"), EQ("PSTR", "CHAR *"), EQ("PUCHAR", "UCHAR *"), EQ("PUHALF_PTR", "UHALF_PTR *"), EQ("PUINT", "UINT *"), EQ("PUINT16", "UINT16 *"), EQ("PUINT32", "UINT32 *"), EQ("PUINT64", "UINT64 *"), EQ("PUINT8", "UINT8 *"), EQ("PUINT_PTR", "UINT_PTR *"), EQ("PULONG", "ULONG *"), EQ("PULONG32", "ULONG32 *"), EQ("PULONG64", "ULONG64 *"), EQ("PULONGLONG", "ULONGLONG *"), EQ("PULONG_PTR", "ULONG_PTR *"), EQ("PUSHORT", "USHORT *"), EQ("PVOID", "void *"), EQ("PWCHAR", "WCHAR *"), EQ("PWORD", "WORD *"), EQ("PWSTR", "WCHAR *"), EQ("QWORD", "unsigned long long"), EQ("SC_HANDLE", "HANDLE"), EQ("SC_LOCK", "LPVOID"), EQ("SERVICE_STATUS_HANDLE", "HANDLE"), EQ("SHORT", "short"), EQ("SIZE_T", "ULONG_PTR"), EQ("SSIZE_T", "LONG_PTR"), EQ("UCHAR", "unsigned char"), EQ("UHALF_PTR", W32_64("unsigned short","unsigned int")), EQ("UINT", "unsigned int"), EQ("UINT16", "unsigned short"), EQ("UINT32", "unsigned int"), EQ("UINT64", "unsigned long long"), EQ("UINT8", "unsigned char"), EQ("UINT_PTR", W32_64("unsigned int","unsigned long long")), EQ("ULONG", "unsigned long"), EQ("ULONG32", "unsigned int"), EQ("ULONG64", "unsigned long long"), EQ("ULONGLONG", "unsigned long long"), EQ("ULONG_PTR", W32_64("unsigned long","unsigned long long")), EQ("USHORT", "unsigned short"), EQ("USN", "LONGLONG"), EQ("VOID", "void"), EQ("WCHAR", "wchar_t"), EQ("WINSTA", "HANDLE"), EQ("WORD", "unsigned short"), EQ("WPARAM", "UINT_PTR"), #endif EQ("bool", "_Bool"), }; #undef EQ #undef W32_64 #define num_common_simple_types \ (sizeof(common_simple_types) / sizeof(common_simple_types[0])) static const char *get_common_type(const char *search, size_t search_len) { const char *entry; int index = search_sorted(common_simple_types, sizeof(const char *), num_common_simple_types, search, search_len); if (index < 0) return NULL; entry = common_simple_types[index]; return entry + strlen(entry) + 1; } static PyObject *b__get_common_types(PyObject *self, PyObject *arg) { int err; size_t i; for (i = 0; i < num_common_simple_types; i++) { const char *s = common_simple_types[i]; PyObject *o = PyText_FromString(s + strlen(s) + 1); if (o == NULL) return NULL; err = PyDict_SetItemString(arg, s, o); Py_DECREF(o); if (err < 0) return NULL; } Py_INCREF(Py_None); return Py_None; } cffi-1.5.2/c/call_python.c0000664000175000017500000001747712657646311015611 0ustar arigoarigo00000000000000 static PyObject *_get_interpstate_dict(void) { /* hack around to return a dict that is subinterpreter-local */ int err; PyObject *m, *modules = PyThreadState_GET()->interp->modules; if (modules == NULL) { PyErr_SetString(FFIError, "subinterpreter already gone?"); return NULL; } m = PyDict_GetItemString(modules, "_cffi_backend._extern_py"); if (m == NULL) { m = PyModule_New("_cffi_backend._extern_py"); if (m == NULL) return NULL; err = PyDict_SetItemString(modules, "_cffi_backend._extern_py", m); Py_DECREF(m); /* sys.modules keeps one reference to m */ if (err < 0) return NULL; } return PyModule_GetDict(m); } static PyObject *_ffi_def_extern_decorator(PyObject *outer_args, PyObject *fn) { char *s; PyObject *error, *onerror, *infotuple, *old1; int index, err; const struct _cffi_global_s *g; struct _cffi_externpy_s *externpy; CTypeDescrObject *ct; FFIObject *ffi; builder_c_t *types_builder; PyObject *name = NULL; PyObject *interpstate_dict; PyObject *interpstate_key; if (!PyArg_ParseTuple(outer_args, "OzOO", &ffi, &s, &error, &onerror)) return NULL; if (s == NULL) { name = PyObject_GetAttrString(fn, "__name__"); if (name == NULL) return NULL; s = PyText_AsUTF8(name); if (s == NULL) { Py_DECREF(name); return NULL; } } types_builder = &ffi->types_builder; index = search_in_globals(&types_builder->ctx, s, strlen(s)); if (index < 0) goto not_found; g = &types_builder->ctx.globals[index]; if (_CFFI_GETOP(g->type_op) != _CFFI_OP_EXTERN_PYTHON) goto not_found; Py_XDECREF(name); ct = realize_c_type(types_builder, types_builder->ctx.types, _CFFI_GETARG(g->type_op)); if (ct == NULL) return NULL; infotuple = prepare_callback_info_tuple(ct, fn, error, onerror, 0); Py_DECREF(ct); if (infotuple == NULL) return NULL; /* don't directly attach infotuple to externpy: in the presence of subinterpreters, each time we switch to a different subinterpreter and call the C function, it will notice the change and look up infotuple from the interpstate_dict. */ interpstate_dict = _get_interpstate_dict(); if (interpstate_dict == NULL) { Py_DECREF(infotuple); return NULL; } externpy = (struct _cffi_externpy_s *)g->address; interpstate_key = PyLong_FromVoidPtr((void *)externpy); if (interpstate_key == NULL) { Py_DECREF(infotuple); return NULL; } err = PyDict_SetItem(interpstate_dict, interpstate_key, infotuple); Py_DECREF(interpstate_key); Py_DECREF(infotuple); /* interpstate_dict owns the last ref */ if (err < 0) return NULL; /* force _update_cache_to_call_python() to be called the next time the C function invokes cffi_call_python, to update the cache */ old1 = externpy->reserved1; externpy->reserved1 = Py_None; /* a non-NULL value */ Py_INCREF(Py_None); Py_XDECREF(old1); /* return the function object unmodified */ Py_INCREF(fn); return fn; not_found: PyErr_Format(FFIError, "ffi.def_extern('%s'): no 'extern \"Python\"' " "function with this name", s); Py_XDECREF(name); return NULL; } static int _update_cache_to_call_python(struct _cffi_externpy_s *externpy) { PyObject *interpstate_dict, *interpstate_key, *infotuple, *old1, *new1; PyObject *old2; interpstate_dict = _get_interpstate_dict(); if (interpstate_dict == NULL) goto error; interpstate_key = PyLong_FromVoidPtr((void *)externpy); if (interpstate_key == NULL) goto error; infotuple = PyDict_GetItem(interpstate_dict, interpstate_key); Py_DECREF(interpstate_key); if (infotuple == NULL) return 3; /* no ffi.def_extern() from this subinterpreter */ new1 = PyThreadState_GET()->interp->modules; Py_INCREF(new1); Py_INCREF(infotuple); old1 = (PyObject *)externpy->reserved1; old2 = (PyObject *)externpy->reserved2; externpy->reserved1 = new1; /* holds a reference */ externpy->reserved2 = infotuple; /* holds a reference (issue #246) */ Py_XDECREF(old1); Py_XDECREF(old2); return 0; /* no error */ error: PyErr_Clear(); return 2; /* out of memory? */ } #if (defined(WITH_THREAD) && !defined(_MSC_VER) && \ !defined(__amd64__) && !defined(__x86_64__) && \ !defined(__i386__) && !defined(__i386)) # define read_barrier() __sync_synchronize() #else # define read_barrier() (void)0 #endif static void cffi_call_python(struct _cffi_externpy_s *externpy, char *args) { /* Invoked by the helpers generated from extern "Python" in the cdef. 'externpy' is a static structure that describes which of the extern "Python" functions is called. It has got fields 'name' and 'type_index' describing the function, and more reserved fields that are initially zero. These reserved fields are set up by ffi.def_extern(), which invokes _ffi_def_extern_decorator() above. 'args' is a pointer to an array of 8-byte entries. Each entry contains an argument. If an argument is less than 8 bytes, only the part at the beginning of the entry is initialized. If an argument is 'long double' or a struct/union, then it is passed by reference. 'args' is also used as the place to write the result to (directly, even if more than 8 bytes). In all cases, 'args' is at least 8 bytes in size. */ int err = 0; /* This read barrier is needed for _embedding.h. It is paired with the write_barrier() there. Without this barrier, we can in theory see the following situation: the Python initialization code already ran (in another thread), and the '_cffi_call_python' function pointer directed execution here; but any number of other data could still be seen as uninitialized below. For example, 'externpy' would still contain NULLs even though it was correctly set up, or 'interpreter_lock' (the GIL inside CPython) would still be seen as NULL, or 'autoInterpreterState' (used by PyGILState_Ensure()) would be NULL or contain bogus fields. */ read_barrier(); save_errno(); /* We need the infotuple here. We could always go through interp->modules['..'][externpy], but to avoid the extra dict lookups, we cache in (reserved1, reserved2) the last seen pair (interp->modules, infotuple). */ if (externpy->reserved1 == NULL) { /* Not initialized! We didn't call @ffi.def_extern() on this externpy object from any subinterpreter at all. */ err = 1; } else { PyGILState_STATE state = gil_ensure(); if (externpy->reserved1 != PyThreadState_GET()->interp->modules) { /* Update the (reserved1, reserved2) cache. This will fail if we didn't call @ffi.def_extern() in this particular subinterpreter. */ err = _update_cache_to_call_python(externpy); } if (!err) { general_invoke_callback(0, args, args, externpy->reserved2); } gil_release(state); } if (err) { static const char *msg[] = { "no code was attached to it yet with @ffi.def_extern()", "got internal exception (out of memory / shutdown issue)", "@ffi.def_extern() was not called in the current subinterpreter", }; fprintf(stderr, "extern \"Python\": function %s() called, " "but %s. Returning 0.\n", externpy->name, msg[err-1]); memset(args, 0, externpy->size_of_result); } restore_errno(); } cffi-1.5.2/c/file_emulator.h0000664000175000017500000000410612657646311016112 0ustar arigoarigo00000000000000 /* Emulation of PyFile_Check() and PyFile_AsFile() for Python 3. */ static PyObject *PyIOBase_TypeObj; static int init_file_emulator(void) { if (PyIOBase_TypeObj == NULL) { 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-1.5.2/c/test_c.py0000664000175000017500000036644312657646311014764 0ustar arigoarigo00000000000000import 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, _get_common_types, __version__ # ____________________________________________________________ import sys assert __version__ == "1.5.2", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") 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, "x[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") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BArray0)]) BFunc = new_function_type((BStruct,), BInt, False) py.test.raises(NotImplementedError, cast(BFunc, 123), cast(BStructP, 123)) BFunc2 = new_function_type((BInt,), BStruct, False) py.test.raises(NotImplementedError, cast(BFunc2, 123), 123) 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)]) BFunc = new_function_type((BStruct,), BDouble) # internally not callable dummy_func = cast(BFunc, 42) e = py.test.raises(NotImplementedError, dummy_func, "?") msg = ("ctype \'struct foo\' not supported as argument (it is a struct " 'declared with "...;", but the C calling convention may depend on ' 'the missing fields)') assert str(e.value) == msg 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 not sys.platform.startswith("linux"): 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 not sys.platform.startswith("linux"): 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 cffi callback : Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' """) sys.stderr = cStringIO.StringIO() bigvalue = 20000 assert len(seen) == 0 assert ff(bigvalue) == -42 assert sys.stderr.getvalue() == "" assert len(seen) == 1 exc, val, tb = seen[0] assert exc is OverflowError assert str(val) == "integer 60000 does not fit 'short'" # sys.stderr = cStringIO.StringIO() bigvalue = 20000 del seen[:] oops_result = 81 assert ff(bigvalue) == 81 oops_result = None assert sys.stderr.getvalue() == "" assert len(seen) == 1 exc, val, tb = seen[0] assert exc is OverflowError assert str(val) == "integer 60000 does not fit 'short'" # sys.stderr = cStringIO.StringIO() bigvalue = 20000 del seen[:] oops_result = "xy" # not None and not an int! assert ff(bigvalue) == -42 oops_result = None assert matches(sys.stderr.getvalue(), """\ From cffi callback : Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' During the call to 'onerror', another exception occurred: TypeError: $integer$ """) # sys.stderr = cStringIO.StringIO() seen = "not a list" # this makes the oops() function crash assert ff(bigvalue) == -42 assert matches(sys.stderr.getvalue(), """\ From cffi callback : Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' During the call to 'onerror', another exception occurred: Traceback (most recent call last): File "$", line $, in oops $ AttributeError: 'str' object has no attribute 'append' """) 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, []) BFunc = new_function_type((), BUnion) py.test.raises(NotImplementedError, cast(BFunc, 123)) 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) py.test.raises((TypeError, RuntimeError), "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_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, 0) 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_errno_saved(): set_errno(42) # a random function that will reset errno to 0 (at least on non-windows) import os; os.stat('.') # res = get_errno() assert res == 42 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', 'int_least8_t', 'uint_least8_t', 'int_least16_t', 'uint_least16_t', 'int_least32_t', 'uint_least32_t', 'int_least64_t', 'uint_least64_t', 'int_fast8_t', 'uint_fast8_t', 'int_fast16_t', 'uint_fast16_t', 'int_fast32_t', 'uint_fast32_t', 'int_fast64_t', 'uint_fast64_t', 'intmax_t', 'uintmax_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_from_buffer(): import array a = array.array('H', [10000, 20000, 30000]) BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) c = from_buffer(BCharA, a) assert typeof(c) is BCharA assert len(c) == 6 assert repr(c) == "" p = new_pointer_type(new_primitive_type("unsigned short")) cast(p, c)[1] += 500 assert list(a) == [10000, 20500, 30000] def test_from_buffer_not_str_unicode_bytearray(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) py.test.raises(TypeError, from_buffer, BCharA, b"foo") py.test.raises(TypeError, from_buffer, BCharA, u+"foo") py.test.raises(TypeError, from_buffer, BCharA, bytearray(b"foo")) try: from __builtin__ import buffer except ImportError: pass else: py.test.raises(TypeError, from_buffer, BCharA, buffer(b"foo")) py.test.raises(TypeError, from_buffer, BCharA, buffer(u+"foo")) py.test.raises(TypeError, from_buffer, BCharA, buffer(bytearray(b"foo"))) try: from __builtin__ import memoryview except ImportError: pass else: py.test.raises(TypeError, from_buffer, BCharA, memoryview(b"foo")) py.test.raises(TypeError, from_buffer, BCharA, memoryview(bytearray(b"foo"))) def test_from_buffer_more_cases(): try: from _cffi_backend import _testbuff except ImportError: py.test.skip("not for pypy") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) # def check1(bufobj, expected): c = from_buffer(BCharA, bufobj) assert typeof(c) is BCharA if sys.version_info >= (3,): expected = [bytes(c, "ascii") for c in expected] assert list(c) == list(expected) # def check(methods, expected, expected_for_memoryview=None): if sys.version_info >= (3,): if methods <= 7: return if expected_for_memoryview is not None: expected = expected_for_memoryview class X(object): pass _testbuff(X, methods) bufobj = X() check1(bufobj, expected) try: from __builtin__ import buffer bufobjb = buffer(bufobj) except (TypeError, ImportError): pass else: check1(bufobjb, expected) try: bufobjm = memoryview(bufobj) except (TypeError, NameError): pass else: check1(bufobjm, expected_for_memoryview or expected) # check(1, "RDB") check(2, "WRB") check(4, "CHB") check(8, "GTB") check(16, "ROB") # check(1 | 2, "RDB") check(1 | 4, "RDB") check(2 | 4, "CHB") check(1 | 8, "RDB", "GTB") check(1 | 16, "RDB", "ROB") check(2 | 8, "WRB", "GTB") check(2 | 16, "WRB", "ROB") check(4 | 8, "CHB", "GTB") check(4 | 16, "CHB", "ROB") def test_memmove(): Short = new_primitive_type("short") ShortA = new_array_type(new_pointer_type(Short), None) Char = new_primitive_type("char") CharA = new_array_type(new_pointer_type(Char), None) p = newp(ShortA, [-1234, -2345, -3456, -4567, -5678]) memmove(p, p + 1, 4) assert list(p) == [-2345, -3456, -3456, -4567, -5678] p[2] = 999 memmove(p + 2, p, 6) assert list(p) == [-2345, -3456, -2345, -3456, 999] memmove(p + 4, newp(CharA, b"\x71\x72"), 2) if sys.byteorder == 'little': assert list(p) == [-2345, -3456, -2345, -3456, 0x7271] else: assert list(p) == [-2345, -3456, -2345, -3456, 0x7172] def test_memmove_buffer(): import array Short = new_primitive_type("short") ShortA = new_array_type(new_pointer_type(Short), None) a = array.array('H', [10000, 20000, 30000]) p = newp(ShortA, 5) memmove(p, a, 6) assert list(p) == [10000, 20000, 30000, 0, 0] memmove(p + 1, a, 6) assert list(p) == [10000, 10000, 20000, 30000, 0] b = array.array('h', [-1000, -2000, -3000]) memmove(b, a, 4) assert b.tolist() == [10000, 20000, -3000] assert a.tolist() == [10000, 20000, 30000] p[0] = 999 p[1] = 998 p[2] = 997 p[3] = 996 p[4] = 995 memmove(b, p, 2) assert b.tolist() == [999, 20000, -3000] memmove(b, p + 2, 4) assert b.tolist() == [997, 996, -3000] p[2] = -p[2] p[3] = -p[3] memmove(b, p + 2, 6) assert b.tolist() == [-997, -996, 995] def test_memmove_readonly_readwrite(): SignedChar = new_primitive_type("signed char") SignedCharA = new_array_type(new_pointer_type(SignedChar), None) p = newp(SignedCharA, 5) memmove(p, b"abcde", 3) assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0] memmove(p, bytearray(b"ABCDE"), 2) assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0] py.test.raises((TypeError, BufferError), memmove, b"abcde", p, 3) ba = bytearray(b"xxxxx") memmove(dest=ba, src=p, n=3) assert ba == bytearray(b"ABcxx") memmove(ba, b"EFGH", 4) assert ba == bytearray(b"EFGHx") def test_memmove_sign_check(): SignedChar = new_primitive_type("signed char") SignedCharA = new_array_type(new_pointer_type(SignedChar), None) p = newp(SignedCharA, 5) py.test.raises(ValueError, memmove, p, p + 1, -1) # not segfault def test_memmove_bad_cdata(): BInt = new_primitive_type("int") p = cast(BInt, 42) py.test.raises(TypeError, memmove, p, bytearray(b'a'), 1) py.test.raises(TypeError, memmove, bytearray(b'a'), p, 1) def test_dereference_null_ptr(): BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) p = cast(BIntPtr, 0) py.test.raises(RuntimeError, "p[0]") py.test.raises(RuntimeError, "p[0] = 42") py.test.raises(RuntimeError, "p[42]") py.test.raises(RuntimeError, "p[42] = -1") def test_mixup(): BStruct1 = new_struct_type("foo") BStruct2 = new_struct_type("foo") # <= same name as BStruct1 BStruct3 = new_struct_type("bar") BStruct1Ptr = new_pointer_type(BStruct1) BStruct2Ptr = new_pointer_type(BStruct2) BStruct3Ptr = new_pointer_type(BStruct3) BStruct1PtrPtr = new_pointer_type(BStruct1Ptr) BStruct2PtrPtr = new_pointer_type(BStruct2Ptr) BStruct3PtrPtr = new_pointer_type(BStruct3Ptr) pp1 = newp(BStruct1PtrPtr) pp2 = newp(BStruct2PtrPtr) pp3 = newp(BStruct3PtrPtr) pp1[0] = pp1[0] e = py.test.raises(TypeError, "pp3[0] = pp1[0]") assert str(e.value).startswith("initializer for ctype 'bar *' must be a ") assert str(e.value).endswith(", not cdata 'foo *'") e = py.test.raises(TypeError, "pp2[0] = pp1[0]") assert str(e.value) == ("initializer for ctype 'foo *' appears indeed to " "be 'foo *', but the types are different (check " "that you are not e.g. mixing up different ffi " "instances)") def test_stdcall_function_type(): assert FFI_CDECL == FFI_DEFAULT_ABI try: stdcall = FFI_STDCALL except NameError: stdcall = FFI_DEFAULT_ABI BInt = new_primitive_type("int") BFunc = new_function_type((BInt, BInt), BInt, False, stdcall) if stdcall != FFI_DEFAULT_ABI: assert repr(BFunc) == "" else: assert repr(BFunc) == "" def test_get_common_types(): d = {} _get_common_types(d) assert d['bool'] == '_Bool' cffi-1.5.2/c/ffi_obj.c0000664000175000017500000011771312657646311014665 0ustar arigoarigo00000000000000 /* An FFI object has methods like ffi.new(). It is also a container for the type declarations (typedefs and structs) that you can use, say in ffi.new(). CTypeDescrObjects are internally stored in the dict 'types_dict'. The types_dict is lazily filled with CTypeDescrObjects made from reading a _cffi_type_context_s structure. In "modern" mode, the FFI instance is made by the C extension module originally created by recompile(). The _cffi_type_context_s structure comes from global data in the C extension module. In "compatibility" mode, an FFI instance is created explicitly by the user, and its _cffi_type_context_s is initially empty. You need to call ffi.cdef() to add more information to it. */ #define FFI_COMPLEXITY_OUTPUT 1200 /* xxx should grow as needed */ #define FFIObject_Check(op) PyObject_TypeCheck(op, &FFI_Type) #define LibObject_Check(ob) ((Py_TYPE(ob) == &Lib_Type)) struct FFIObject_s { PyObject_HEAD PyObject *gc_wrefs, *gc_wrefs_freelist; PyObject *init_once_cache; struct _cffi_parse_info_s info; char ctx_is_static, ctx_is_nonempty; builder_c_t types_builder; }; static FFIObject *ffi_internal_new(PyTypeObject *ffitype, const struct _cffi_type_context_s *static_ctx) { static _cffi_opcode_t internal_output[FFI_COMPLEXITY_OUTPUT]; FFIObject *ffi; if (static_ctx != NULL) { ffi = (FFIObject *)PyObject_GC_New(FFIObject, ffitype); /* we don't call PyObject_GC_Track() here: from _cffi_init_module() it is not needed, because in this case the ffi object is immortal */ } else { ffi = (FFIObject *)ffitype->tp_alloc(ffitype, 0); } if (ffi == NULL) return NULL; if (init_builder_c(&ffi->types_builder, static_ctx) < 0) { Py_DECREF(ffi); return NULL; } ffi->gc_wrefs = NULL; ffi->gc_wrefs_freelist = NULL; ffi->init_once_cache = NULL; ffi->info.ctx = &ffi->types_builder.ctx; ffi->info.output = internal_output; ffi->info.output_size = FFI_COMPLEXITY_OUTPUT; ffi->ctx_is_static = (static_ctx != NULL); ffi->ctx_is_nonempty = (static_ctx != NULL); return ffi; } static void ffi_dealloc(FFIObject *ffi) { PyObject_GC_UnTrack(ffi); Py_XDECREF(ffi->gc_wrefs); Py_XDECREF(ffi->gc_wrefs_freelist); Py_XDECREF(ffi->init_once_cache); free_builder_c(&ffi->types_builder, ffi->ctx_is_static); Py_TYPE(ffi)->tp_free((PyObject *)ffi); } static int ffi_traverse(FFIObject *ffi, visitproc visit, void *arg) { Py_VISIT(ffi->types_builder.types_dict); Py_VISIT(ffi->types_builder.included_ffis); Py_VISIT(ffi->types_builder.included_libs); Py_VISIT(ffi->gc_wrefs); return 0; } static PyObject *ffiobj_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { /* user-facing initialization code, for explicit FFI() calls */ return (PyObject *)ffi_internal_new(type, NULL); } /* forward, declared in cdlopen.c because it's mostly useful for this case */ static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds); static PyObject *ffi_fetch_int_constant(FFIObject *ffi, char *name, int recursion) { int index; index = search_in_globals(&ffi->types_builder.ctx, name, strlen(name)); if (index >= 0) { const struct _cffi_global_s *g; g = &ffi->types_builder.ctx.globals[index]; switch (_CFFI_GETOP(g->type_op)) { case _CFFI_OP_CONSTANT_INT: case _CFFI_OP_ENUM: return realize_global_int(&ffi->types_builder, index); default: PyErr_Format(FFIError, "function, global variable or non-integer constant " "'%.200s' must be fetched from its original 'lib' " "object", name); return NULL; } } if (ffi->types_builder.included_ffis != NULL) { Py_ssize_t i; PyObject *included_ffis = ffi->types_builder.included_ffis; if (recursion > 100) { PyErr_SetString(PyExc_RuntimeError, "recursion overflow in ffi.include() delegations"); return NULL; } for (i = 0; i < PyTuple_GET_SIZE(included_ffis); i++) { FFIObject *ffi1; PyObject *x; ffi1 = (FFIObject *)PyTuple_GET_ITEM(included_ffis, i); x = ffi_fetch_int_constant(ffi1, name, recursion + 1); if (x != NULL || PyErr_Occurred()) return x; } } return NULL; /* no exception set, means "not found" */ } #define ACCEPT_STRING 1 #define ACCEPT_CTYPE 2 #define ACCEPT_CDATA 4 #define ACCEPT_ALL (ACCEPT_STRING | ACCEPT_CTYPE | ACCEPT_CDATA) #define CONSIDER_FN_AS_FNPTR 8 static CTypeDescrObject *_ffi_bad_type(FFIObject *ffi, char *input_text) { size_t length = strlen(input_text); char *extra; if (length > 500) { extra = ""; } else { char *p; size_t i, num_spaces = ffi->info.error_location; extra = alloca(length + num_spaces + 4); p = extra; *p++ = '\n'; for (i = 0; i < length; i++) { if (' ' <= input_text[i] && input_text[i] < 0x7f) *p++ = input_text[i]; else if (input_text[i] == '\t' || input_text[i] == '\n') *p++ = ' '; else *p++ = '?'; } *p++ = '\n'; memset(p, ' ', num_spaces); p += num_spaces; *p++ = '^'; *p++ = 0; } PyErr_Format(FFIError, "%s%s", ffi->info.error_message, extra); return NULL; } static CTypeDescrObject *_ffi_type(FFIObject *ffi, PyObject *arg, int accept) { /* Returns the CTypeDescrObject from the user-supplied 'arg'. Does not return a new reference! */ if ((accept & ACCEPT_STRING) && PyText_Check(arg)) { PyObject *types_dict = ffi->types_builder.types_dict; PyObject *x = PyDict_GetItem(types_dict, arg); if (x == NULL) { char *input_text = PyText_AS_UTF8(arg); int err, index = parse_c_type(&ffi->info, input_text); if (index < 0) return _ffi_bad_type(ffi, input_text); x = realize_c_type_or_func(&ffi->types_builder, ffi->info.output, index); if (x == NULL) return NULL; /* Cache under the name given by 'arg', in addition to the fact that the same ct is probably already cached under its standardized name. In a few cases, it is not, e.g. if it is a primitive; for the purpose of this function, the important point is the following line, which makes sure that in any case the next _ffi_type() with the same 'arg' will succeed early, in PyDict_GetItem() above. */ err = PyDict_SetItem(types_dict, arg, x); Py_DECREF(x); /* we know it was written in types_dict (unless out of mem), so there is at least that ref left */ if (err < 0) return NULL; } if (CTypeDescr_Check(x)) return (CTypeDescrObject *)x; else if (accept & CONSIDER_FN_AS_FNPTR) return unwrap_fn_as_fnptr(x); else return unexpected_fn_type(x); } else if ((accept & ACCEPT_CTYPE) && CTypeDescr_Check(arg)) { return (CTypeDescrObject *)arg; } else if ((accept & ACCEPT_CDATA) && CData_Check(arg)) { return ((CDataObject *)arg)->c_type; } #if PY_MAJOR_VERSION < 3 else if (PyUnicode_Check(arg)) { CTypeDescrObject *result; arg = PyUnicode_AsASCIIString(arg); if (arg == NULL) return NULL; result = _ffi_type(ffi, arg, accept); Py_DECREF(arg); return result; } #endif else { const char *m1 = (accept & ACCEPT_STRING) ? "string" : ""; const char *m2 = (accept & ACCEPT_CTYPE) ? "ctype object" : ""; const char *m3 = (accept & ACCEPT_CDATA) ? "cdata object" : ""; const char *s12 = (*m1 && (*m2 || *m3)) ? " or " : ""; const char *s23 = (*m2 && *m3) ? " or " : ""; PyErr_Format(PyExc_TypeError, "expected a %s%s%s%s%s, got '%.200s'", m1, s12, m2, s23, m3, Py_TYPE(arg)->tp_name); return NULL; } } PyDoc_STRVAR(ffi_sizeof_doc, "Return the size in bytes of the argument.\n" "It can be a string naming a C type, or a 'cdata' instance."); static PyObject *ffi_sizeof(FFIObject *self, PyObject *arg) { Py_ssize_t size; CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL); if (ct == NULL) return NULL; size = ct->ct_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; } if (size < 0) { PyErr_Format(FFIError, "don't know the size of ctype '%s'", ct->ct_name); return NULL; } return PyInt_FromSsize_t(size); } PyDoc_STRVAR(ffi_alignof_doc, "Return the natural alignment size in bytes of the argument.\n" "It can be a string naming a C type, or a 'cdata' instance."); static PyObject *ffi_alignof(FFIObject *self, PyObject *arg) { int align; CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL); if (ct == NULL) return NULL; align = get_alignment(ct); if (align < 0) return NULL; return PyInt_FromLong(align); } PyDoc_STRVAR(ffi_typeof_doc, "Parse the C type given as a string and return the\n" "corresponding object.\n" "It can also be used on 'cdata' instance to get its C type."); static PyObject *_cpyextfunc_type_index(PyObject *x); /* forward */ static PyObject *ffi_typeof(FFIObject *self, PyObject *arg) { PyObject *x = (PyObject *)_ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CDATA); if (x != NULL) { Py_INCREF(x); } else { x = _cpyextfunc_type_index(arg); } return x; } PyDoc_STRVAR(ffi_new_doc, "Allocate an instance according to the specified C type and return a\n" "pointer to it. The specified C type must be either a pointer or an\n" "array: ``new('X *')`` allocates an X and returns a pointer to it,\n" "whereas ``new('X[n]')`` allocates an array of n X'es and returns an\n" "array referencing it (which works mostly like a pointer, like in C).\n" "You can also use ``new('X[]', n)`` to allocate an array of a\n" "non-constant length n.\n" "\n" "The memory is initialized following the rules of declaring a global\n" "variable in C: by default it is zero-initialized, but an explicit\n" "initializer can be given which can be used to fill all or part of the\n" "memory.\n" "\n" "When the returned object goes out of scope, the memory is\n" "freed. In other words the returned object has ownership of\n" "the value of type 'cdecl' that it points to. This means that the raw\n" "data can be used as long as this object is kept alive, but must not be\n" "used for a longer time. Be careful about that when copying the\n" "pointer to the memory somewhere else, e.g. into another structure."); static PyObject *_ffi_new(FFIObject *self, PyObject *args, PyObject *kwds, const cffi_allocator_t *allocator) { CTypeDescrObject *ct; PyObject *arg, *init = Py_None; static char *keywords[] = {"cdecl", "init", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:new", keywords, &arg, &init)) return NULL; ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE); if (ct == NULL) return NULL; return direct_newp(ct, init, allocator); } static PyObject *ffi_new(FFIObject *self, PyObject *args, PyObject *kwds) { return _ffi_new(self, args, kwds, &default_allocator); } static PyObject *_ffi_new_with_allocator(PyObject *allocator, PyObject *args, PyObject *kwds) { cffi_allocator_t alloc1; PyObject *my_alloc, *my_free; my_alloc = PyTuple_GET_ITEM(allocator, 1); my_free = PyTuple_GET_ITEM(allocator, 2); alloc1.ca_alloc = (my_alloc == Py_None ? NULL : my_alloc); alloc1.ca_free = (my_free == Py_None ? NULL : my_free); alloc1.ca_dont_clear = (PyTuple_GET_ITEM(allocator, 3) == Py_False); return _ffi_new((FFIObject *)PyTuple_GET_ITEM(allocator, 0), args, kwds, &alloc1); } PyDoc_STRVAR(ffi_new_allocator_doc, "Return a new allocator, i.e. a function that behaves like ffi.new()\n" "but uses the provided low-level 'alloc' and 'free' functions.\n" "\n" "'alloc' is called with the size as argument. If it returns NULL, a\n" "MemoryError is raised. 'free' is called with the result of 'alloc'\n" "as argument. Both can be either Python functions or directly C\n" "functions. If 'free' is None, then no free function is called.\n" "If both 'alloc' and 'free' are None, the default is used.\n" "\n" "If 'should_clear_after_alloc' is set to False, then the memory\n" "returned by 'alloc' is assumed to be already cleared (or you are\n" "fine with garbage); otherwise CFFI will clear it."); static PyObject *ffi_new_allocator(FFIObject *self, PyObject *args, PyObject *kwds) { PyObject *allocator, *result; PyObject *my_alloc = Py_None, *my_free = Py_None; int should_clear_after_alloc = 1; static char *keywords[] = {"alloc", "free", "should_clear_after_alloc", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi:new_allocator", keywords, &my_alloc, &my_free, &should_clear_after_alloc)) return NULL; if (my_alloc == Py_None && my_free != Py_None) { PyErr_SetString(PyExc_TypeError, "cannot pass 'free' without 'alloc'"); return NULL; } allocator = PyTuple_Pack(4, (PyObject *)self, my_alloc, my_free, PyBool_FromLong(should_clear_after_alloc)); if (allocator == NULL) return NULL; { static PyMethodDef md = {"allocator", (PyCFunction)_ffi_new_with_allocator, METH_VARARGS | METH_KEYWORDS}; result = PyCFunction_New(&md, allocator); } Py_DECREF(allocator); return result; } PyDoc_STRVAR(ffi_cast_doc, "Similar to a C cast: returns an instance of the named C\n" "type initialized with the given 'source'. The source is\n" "casted between integers or pointers of any type."); static PyObject *ffi_cast(FFIObject *self, PyObject *args) { CTypeDescrObject *ct; PyObject *ob, *arg; if (!PyArg_ParseTuple(args, "OO:cast", &arg, &ob)) return NULL; ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE); if (ct == NULL) return NULL; return do_cast(ct, ob); } PyDoc_STRVAR(ffi_string_doc, "Return a Python string (or unicode string) from the 'cdata'. If\n" "'cdata' is a pointer or array of characters or bytes, returns the\n" "null-terminated string. The returned string extends until the first\n" "null character, or at most 'maxlen' characters. If 'cdata' is an\n" "array then 'maxlen' defaults to its length.\n" "\n" "If 'cdata' is a pointer or array of wchar_t, returns a unicode string\n" "following the same rules.\n" "\n" "If 'cdata' is a single character or byte or a wchar_t, returns it as a\n" "string or unicode string.\n" "\n" "If 'cdata' is an enum, returns the value of the enumerator as a\n" "string, or 'NUMBER' if the value is out of range."); #define ffi_string b_string /* ffi_string() => b_string() from _cffi_backend.c */ PyDoc_STRVAR(ffi_buffer_doc, "Return a read-write buffer object that references the raw C data\n" "pointed to by the given 'cdata'. The 'cdata' must be a pointer or an\n" "array. Can be passed to functions expecting a buffer, or directly\n" "manipulated with:\n" "\n" " buf[:] get a copy of it in a regular string, or\n" " buf[idx] as a single character\n" " buf[:] = ...\n" " buf[idx] = ... change the content"); #define ffi_buffer b_buffer /* ffi_buffer() => b_buffer() from _cffi_backend.c */ PyDoc_STRVAR(ffi_offsetof_doc, "Return the offset of the named field inside the given structure or\n" "array, which must be given as a C type name. You can give several\n" "field names in case of nested structures. You can also give numeric\n" "values which correspond to array items, in case of an array type."); static PyObject *ffi_offsetof(FFIObject *self, PyObject *args) { PyObject *arg; CTypeDescrObject *ct; Py_ssize_t i, offset; if (PyTuple_Size(args) < 2) { PyErr_SetString(PyExc_TypeError, "offsetof() expects at least 2 arguments"); return NULL; } arg = PyTuple_GET_ITEM(args, 0); ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE); if (ct == NULL) return NULL; offset = 0; for (i = 1; i < PyTuple_GET_SIZE(args); i++) { Py_ssize_t ofs1; ct = direct_typeoffsetof(ct, PyTuple_GET_ITEM(args, i), i > 1, &ofs1); if (ct == NULL) return NULL; offset += ofs1; } return PyInt_FromSsize_t(offset); } PyDoc_STRVAR(ffi_addressof_doc, "Limited equivalent to the '&' operator in C:\n" "\n" "1. ffi.addressof() returns a cdata that is a\n" "pointer to this struct or union.\n" "\n" "2. ffi.addressof(, field-or-index...) returns the address of a\n" "field or array item inside the given structure or array, recursively\n" "in case of nested structures or arrays.\n" "\n" "3. ffi.addressof(, \"name\") returns the address of the named\n" "function or global variable."); static PyObject *address_of_global_var(PyObject *args); /* forward */ static PyObject *ffi_addressof(FFIObject *self, PyObject *args) { PyObject *arg, *z, *result; CTypeDescrObject *ct; Py_ssize_t i, offset = 0; int accepted_flags; if (PyTuple_Size(args) < 1) { PyErr_SetString(PyExc_TypeError, "addressof() expects at least 1 argument"); return NULL; } arg = PyTuple_GET_ITEM(args, 0); if (LibObject_Check(arg)) { /* case 3 in the docstring */ return address_of_global_var(args); } ct = _ffi_type(self, arg, ACCEPT_CDATA); if (ct == NULL) return NULL; if (PyTuple_GET_SIZE(args) == 1) { /* case 1 in the docstring */ accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY; if ((ct->ct_flags & accepted_flags) == 0) { PyErr_SetString(PyExc_TypeError, "expected a cdata struct/union/array object"); return NULL; } } else { /* case 2 in the docstring */ accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER; if ((ct->ct_flags & accepted_flags) == 0) { PyErr_SetString(PyExc_TypeError, "expected a cdata struct/union/array/pointer object"); return NULL; } for (i = 1; i < PyTuple_GET_SIZE(args); i++) { Py_ssize_t ofs1; ct = direct_typeoffsetof(ct, PyTuple_GET_ITEM(args, i), i > 1, &ofs1); if (ct == NULL) return NULL; offset += ofs1; } } z = new_pointer_type(ct); if (z == NULL) return NULL; result = new_simple_cdata(((CDataObject *)arg)->c_data + offset, (CTypeDescrObject *)z); Py_DECREF(z); return result; } static PyObject *_combine_type_name_l(CTypeDescrObject *ct, size_t extra_text_len) { size_t base_name_len; PyObject *result; char *p; base_name_len = strlen(ct->ct_name); result = PyBytes_FromStringAndSize(NULL, base_name_len + extra_text_len); if (result == NULL) return NULL; p = PyBytes_AS_STRING(result); memcpy(p, ct->ct_name, ct->ct_name_position); p += ct->ct_name_position; p += extra_text_len; memcpy(p, ct->ct_name + ct->ct_name_position, base_name_len - ct->ct_name_position); return result; } PyDoc_STRVAR(ffi_getctype_doc, "Return a string giving the C type 'cdecl', which may be itself a\n" "string or a object. If 'replace_with' is given, it gives\n" "extra text to append (or insert for more complicated C types), like a\n" "variable name, or '*' to get actually the C type 'pointer-to-cdecl'."); static PyObject *ffi_getctype(FFIObject *self, PyObject *args, PyObject *kwds) { PyObject *c_decl, *res; char *p, *replace_with = ""; int add_paren, add_space; CTypeDescrObject *ct; size_t replace_with_len; static char *keywords[] = {"cdecl", "replace_with", NULL}; #if PY_MAJOR_VERSION >= 3 PyObject *u; #endif if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s:getctype", keywords, &c_decl, &replace_with)) return NULL; ct = _ffi_type(self, c_decl, ACCEPT_STRING|ACCEPT_CTYPE); if (ct == NULL) return NULL; while (replace_with[0] != 0 && isspace(replace_with[0])) replace_with++; replace_with_len = strlen(replace_with); while (replace_with_len > 0 && isspace(replace_with[replace_with_len - 1])) replace_with_len--; add_paren = (replace_with[0] == '*' && ((ct->ct_flags & CT_ARRAY) != 0)); add_space = (!add_paren && replace_with_len > 0 && replace_with[0] != '[' && replace_with[0] != '('); res = _combine_type_name_l(ct, replace_with_len + add_space + 2*add_paren); if (res == NULL) return NULL; p = PyBytes_AS_STRING(res) + ct->ct_name_position; if (add_paren) *p++ = '('; if (add_space) *p++ = ' '; memcpy(p, replace_with, replace_with_len); if (add_paren) p[replace_with_len] = ')'; #if PY_MAJOR_VERSION >= 3 /* bytes -> unicode string */ u = PyUnicode_DecodeLatin1(PyBytes_AS_STRING(res), PyBytes_GET_SIZE(res), NULL); Py_DECREF(res); res = u; #endif return res; } PyDoc_STRVAR(ffi_new_handle_doc, "Return a non-NULL cdata of type 'void *' that contains an opaque\n" "reference to the argument, which can be any Python object. To cast it\n" "back to the original object, use from_handle(). You must keep alive\n" "the cdata object returned by new_handle()!"); static PyObject *ffi_new_handle(FFIObject *self, PyObject *arg) { /* g_ct_voidp is equal to */ return newp_handle(g_ct_voidp, arg); } PyDoc_STRVAR(ffi_from_handle_doc, "Cast a 'void *' back to a Python object. Must be used *only* on the\n" "pointers returned by new_handle(), and *only* as long as the exact\n" "cdata object returned by new_handle() is still alive (somewhere else\n" "in the program). Failure to follow these rules will crash."); #define ffi_from_handle b_from_handle /* ffi_from_handle => b_from_handle from _cffi_backend.c */ PyDoc_STRVAR(ffi_from_buffer_doc, "Return a that points to the data of the given Python\n" "object, which must support the buffer interface. Note that this is\n" "not meant to be used on the built-in types str, unicode, or bytearray\n" "(you can build 'char[]' arrays explicitly) but only on objects\n" "containing large quantities of raw data in some other format, like\n" "'array.array' or numpy arrays."); static PyObject *ffi_from_buffer(PyObject *self, PyObject *arg) { return direct_from_buffer(g_ct_chararray, arg); } PyDoc_STRVAR(ffi_gc_doc, "Return a new cdata object that points to the same data.\n" "Later, when this new cdata object is garbage-collected,\n" "'destructor(old_cdata_object)' will be called."); #define ffi_gc b_gcp /* ffi_gc() => b_gcp() from _cffi_backend.c */ PyDoc_STRVAR(ffi_def_extern_doc, "A decorator. Attaches the decorated Python function to the C code\n" "generated for the 'extern \"Python\"' function of the same name.\n" "Calling the C function will then invoke the Python function.\n" "\n" "Optional arguments: 'name' is the name of the C function, if\n" "different from the Python function; and 'error' and 'onerror'\n" "handle what occurs if the Python function raises an exception\n" "(see the docs for details)."); /* forward; see call_python.c */ static PyObject *_ffi_def_extern_decorator(PyObject *, PyObject *); static PyObject *ffi_def_extern(FFIObject *self, PyObject *args, PyObject *kwds) { static PyMethodDef md = {"def_extern_decorator", (PyCFunction)_ffi_def_extern_decorator, METH_O}; PyObject *name = Py_None, *error = Py_None; PyObject *res, *onerror = Py_None; static char *keywords[] = {"name", "error", "onerror", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", keywords, &name, &error, &onerror)) return NULL; args = Py_BuildValue("(OOOO)", (PyObject *)self, name, error, onerror); if (args == NULL) return NULL; res = PyCFunction_New(&md, args); Py_DECREF(args); return res; } PyDoc_STRVAR(ffi_callback_doc, "Return a callback object or a decorator making such a callback object.\n" "'cdecl' must name a C function pointer type. The callback invokes the\n" "specified 'python_callable' (which may be provided either directly or\n" "via a decorator). Important: the callback object must be manually\n" "kept alive for as long as the callback may be invoked from the C code."); static PyObject *_ffi_callback_decorator(PyObject *outer_args, PyObject *fn) { PyObject *res, *old; old = PyTuple_GET_ITEM(outer_args, 1); PyTuple_SET_ITEM(outer_args, 1, fn); res = b_callback(NULL, outer_args); PyTuple_SET_ITEM(outer_args, 1, old); return res; } static PyObject *ffi_callback(FFIObject *self, PyObject *args, PyObject *kwds) { PyObject *c_decl, *python_callable = Py_None, *error = Py_None; PyObject *res, *onerror = Py_None; static char *keywords[] = {"cdecl", "python_callable", "error", "onerror", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", keywords, &c_decl, &python_callable, &error, &onerror)) return NULL; c_decl = (PyObject *)_ffi_type(self, c_decl, ACCEPT_STRING | ACCEPT_CTYPE | CONSIDER_FN_AS_FNPTR); if (c_decl == NULL) return NULL; args = Py_BuildValue("(OOOO)", c_decl, python_callable, error, onerror); if (args == NULL) return NULL; if (python_callable != Py_None) { res = b_callback(NULL, args); } else { static PyMethodDef md = {"callback_decorator", (PyCFunction)_ffi_callback_decorator, METH_O}; res = PyCFunction_New(&md, args); } Py_DECREF(args); return res; } #ifdef MS_WIN32 PyDoc_STRVAR(ffi_getwinerror_doc, "Return either the GetLastError() or the error number given by the\n" "optional 'code' argument, as a tuple '(code, message)'."); #define ffi_getwinerror b_getwinerror /* ffi_getwinerror() => b_getwinerror() from misc_win32.h */ #endif PyDoc_STRVAR(ffi_errno_doc, "the value of 'errno' from/to the C calls"); static PyObject *ffi_get_errno(PyObject *self, void *closure) { /* xxx maybe think about how to make the saved errno local to an ffi instance */ return b_get_errno(NULL, NULL); } static int ffi_set_errno(PyObject *self, PyObject *newval, void *closure) { PyObject *x = b_set_errno(NULL, newval); if (x == NULL) return -1; Py_DECREF(x); return 0; } PyDoc_STRVAR(ffi_dlopen_doc, "Load and return a dynamic library identified by 'name'. The standard\n" "C library can be loaded by passing None.\n" "\n" "Note that functions and types declared with 'ffi.cdef()' are not\n" "linked to a particular library, just like C headers. In the library\n" "we only look for the actual (untyped) symbols at the time of their\n" "first access."); PyDoc_STRVAR(ffi_dlclose_doc, "Close a library obtained with ffi.dlopen(). After this call, access to\n" "functions or variables from the library will fail (possibly with a\n" "segmentation fault)."); static PyObject *ffi_dlopen(PyObject *self, PyObject *args); /* forward */ static PyObject *ffi_dlclose(PyObject *self, PyObject *args); /* forward */ PyDoc_STRVAR(ffi_int_const_doc, "Get the value of an integer constant.\n" "\n" "'ffi.integer_const(\"xxx\")' is equivalent to 'lib.xxx' if xxx names an\n" "integer constant. The point of this function is limited to use cases\n" "where you have an 'ffi' object but not any associated 'lib' object."); static PyObject *ffi_int_const(FFIObject *self, PyObject *args, PyObject *kwds) { char *name; PyObject *x; static char *keywords[] = {"name", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", keywords, &name)) return NULL; x = ffi_fetch_int_constant(self, name, 0); if (x == NULL && !PyErr_Occurred()) { PyErr_Format(PyExc_AttributeError, "integer constant '%.200s' not found", name); } return x; } PyDoc_STRVAR(ffi_memmove_doc, "ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.\n" "\n" "Like the C function memmove(), the memory areas may overlap;\n" "apart from that it behaves like the C function memcpy().\n" "\n" "'src' can be any cdata ptr or array, or any Python buffer object.\n" "'dest' can be any cdata ptr or array, or a writable Python buffer\n" "object. The size to copy, 'n', is always measured in bytes.\n" "\n" "Unlike other methods, this one supports all Python buffer including\n" "byte strings and bytearrays---but it still does not support\n" "non-contiguous buffers."); #define ffi_memmove b_memmove /* ffi_memmove() => b_memmove() from _cffi_backend.c */ #ifdef WITH_THREAD # include "pythread.h" #else typedef void *PyThread_type_lock; # define PyThread_allocate_lock() ((void *)-1) # define PyThread_free_lock(lock) ((void)(lock)) # define PyThread_acquire_lock(lock, _) ((void)(lock)) # define PyThread_release_lock(lock) ((void)(lock)) #endif PyDoc_STRVAR(ffi_init_once_doc, "init_once(function, tag): run function() once. More precisely,\n" "'function()' is called the first time we see a given 'tag'.\n" "\n" "The return value of function() is remembered and returned by the current\n" "and all future init_once() with the same tag. If init_once() is called\n" "from multiple threads in parallel, all calls block until the execution\n" "of function() is done. If function() raises an exception, it is\n" "propagated and nothing is cached."); #if PY_MAJOR_VERSION < 3 /* PyCapsule_New is redefined to be PyCObject_FromVoidPtr in _cffi_backend, which gives 2.6 compatibility; but the destructor signature is different */ static void _free_init_once_lock(void *lock) { PyThread_free_lock((PyThread_type_lock)lock); } #else static void _free_init_once_lock(PyObject *capsule) { PyThread_type_lock lock; lock = PyCapsule_GetPointer(capsule, "cffi_init_once_lock"); if (lock != NULL) PyThread_free_lock(lock); } #endif static PyObject *ffi_init_once(FFIObject *self, PyObject *args, PyObject *kwds) { static char *keywords[] = {"func", "tag", NULL}; PyObject *cache, *func, *tag, *tup, *res, *x, *lockobj; PyThread_type_lock lock; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", keywords, &func, &tag)) return NULL; /* a lot of fun with reference counting and error checking in this function */ /* atomically get or create a new dict (no GIL release) */ cache = self->init_once_cache; if (cache == NULL) { cache = PyDict_New(); if (cache == NULL) return NULL; self->init_once_cache = cache; } /* get the tuple from cache[tag], or make a new one: (False, lock) */ tup = PyDict_GetItem(cache, tag); if (tup == NULL) { lock = PyThread_allocate_lock(); if (lock == NULL) return NULL; x = PyCapsule_New(lock, "cffi_init_once_lock", _free_init_once_lock); if (x == NULL) { PyThread_free_lock(lock); return NULL; } tup = PyTuple_Pack(2, Py_False, x); Py_DECREF(x); if (tup == NULL) return NULL; x = tup; /* Possible corner case if 'tag' is an object overriding __eq__ in pure Python: the GIL may be released when we are running it. We really need to call dict.setdefault(). */ tup = PyObject_CallMethod(cache, "setdefault", "OO", tag, x); Py_DECREF(x); if (tup == NULL) return NULL; Py_DECREF(tup); /* there is still a ref inside the dict */ } res = PyTuple_GET_ITEM(tup, 1); Py_INCREF(res); if (PyTuple_GET_ITEM(tup, 0) == Py_True) { /* tup == (True, result): return the result. */ return res; } /* tup == (False, lock) */ lockobj = res; lock = (PyThread_type_lock)PyCapsule_GetPointer(lockobj, "cffi_init_once_lock"); if (lock == NULL) { Py_DECREF(lockobj); return NULL; } Py_BEGIN_ALLOW_THREADS PyThread_acquire_lock(lock, WAIT_LOCK); Py_END_ALLOW_THREADS x = PyDict_GetItem(cache, tag); if (x != NULL && PyTuple_GET_ITEM(x, 0) == Py_True) { /* the real result was put in the dict while we were waiting for PyThread_acquire_lock() above */ res = PyTuple_GET_ITEM(x, 1); Py_INCREF(res); } else { res = PyObject_CallFunction(func, ""); if (res != NULL) { tup = PyTuple_Pack(2, Py_True, res); if (tup == NULL || PyDict_SetItem(cache, tag, tup) < 0) { Py_XDECREF(tup); Py_DECREF(res); res = NULL; } } } PyThread_release_lock(lock); Py_DECREF(lockobj); return res; } #define METH_VKW (METH_VARARGS | METH_KEYWORDS) static PyMethodDef ffi_methods[] = { {"addressof", (PyCFunction)ffi_addressof, METH_VARARGS, ffi_addressof_doc}, {"alignof", (PyCFunction)ffi_alignof, METH_O, ffi_alignof_doc}, {"buffer", (PyCFunction)ffi_buffer, METH_VKW, ffi_buffer_doc}, {"def_extern", (PyCFunction)ffi_def_extern, METH_VKW, ffi_def_extern_doc}, {"callback", (PyCFunction)ffi_callback, METH_VKW, ffi_callback_doc}, {"cast", (PyCFunction)ffi_cast, METH_VARARGS, ffi_cast_doc}, {"dlclose", (PyCFunction)ffi_dlclose, METH_VARARGS, ffi_dlclose_doc}, {"dlopen", (PyCFunction)ffi_dlopen, METH_VARARGS, ffi_dlopen_doc}, {"from_buffer",(PyCFunction)ffi_from_buffer,METH_O, ffi_from_buffer_doc}, {"from_handle",(PyCFunction)ffi_from_handle,METH_O, ffi_from_handle_doc}, {"gc", (PyCFunction)ffi_gc, METH_VKW, ffi_gc_doc}, {"getctype", (PyCFunction)ffi_getctype, METH_VKW, ffi_getctype_doc}, #ifdef MS_WIN32 {"getwinerror",(PyCFunction)ffi_getwinerror,METH_VKW, ffi_getwinerror_doc}, #endif {"init_once", (PyCFunction)ffi_init_once, METH_VKW, ffi_init_once_doc}, {"integer_const",(PyCFunction)ffi_int_const,METH_VKW, ffi_int_const_doc}, {"memmove", (PyCFunction)ffi_memmove, METH_VKW, ffi_memmove_doc}, {"new", (PyCFunction)ffi_new, METH_VKW, ffi_new_doc}, {"new_allocator",(PyCFunction)ffi_new_allocator,METH_VKW,ffi_new_allocator_doc}, {"new_handle", (PyCFunction)ffi_new_handle, METH_O, ffi_new_handle_doc}, {"offsetof", (PyCFunction)ffi_offsetof, METH_VARARGS, ffi_offsetof_doc}, {"sizeof", (PyCFunction)ffi_sizeof, METH_O, ffi_sizeof_doc}, {"string", (PyCFunction)ffi_string, METH_VKW, ffi_string_doc}, {"typeof", (PyCFunction)ffi_typeof, METH_O, ffi_typeof_doc}, {NULL} }; static PyGetSetDef ffi_getsets[] = { {"errno", ffi_get_errno, ffi_set_errno, ffi_errno_doc}, {NULL} }; static PyTypeObject FFI_Type = { PyVarObject_HEAD_INIT(NULL, 0) "CompiledFFI", sizeof(FFIObject), 0, (destructor)ffi_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 | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ 0, /* tp_doc */ (traverseproc)ffi_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ ffi_methods, /* tp_methods */ 0, /* tp_members */ ffi_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ ffiobj_init, /* tp_init */ 0, /* tp_alloc */ ffiobj_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; static PyObject * _fetch_external_struct_or_union(const struct _cffi_struct_union_s *s, PyObject *included_ffis, int recursion) { Py_ssize_t i; if (included_ffis == NULL) return NULL; if (recursion > 100) { PyErr_SetString(PyExc_RuntimeError, "recursion overflow in ffi.include() delegations"); return NULL; } for (i = 0; i < PyTuple_GET_SIZE(included_ffis); i++) { FFIObject *ffi1; const struct _cffi_struct_union_s *s1; int sindex; PyObject *x; ffi1 = (FFIObject *)PyTuple_GET_ITEM(included_ffis, i); sindex = search_in_struct_unions(&ffi1->types_builder.ctx, s->name, strlen(s->name)); if (sindex < 0) /* not found at all */ continue; s1 = &ffi1->types_builder.ctx.struct_unions[sindex]; if ((s1->flags & (_CFFI_F_EXTERNAL | _CFFI_F_UNION)) == (s->flags & _CFFI_F_UNION)) { /* s1 is not external, and the same kind (struct or union) as s */ return _realize_c_struct_or_union(&ffi1->types_builder, sindex); } /* not found, look more recursively */ x = _fetch_external_struct_or_union( s, ffi1->types_builder.included_ffis, recursion + 1); if (x != NULL || PyErr_Occurred()) return x; /* either found, or got an error */ } return NULL; /* not found at all, leave without an error */ } cffi-1.5.2/c/cffi1_module.c0000664000175000017500000001542712657646311015623 0ustar arigoarigo00000000000000 #include "parse_c_type.c" #include "realize_c_type.c" #define CFFI_VERSION_MIN 0x2601 #define CFFI_VERSION_MAX 0x27FF typedef struct FFIObject_s FFIObject; typedef struct LibObject_s LibObject; static PyTypeObject FFI_Type; /* forward */ static PyTypeObject Lib_Type; /* forward */ #include "ffi_obj.c" #include "cglob.c" #include "lib_obj.c" #include "cdlopen.c" #include "commontypes.c" #include "call_python.c" static int init_ffi_lib(PyObject *m) { PyObject *x; int i, res; static char init_done = 0; if (PyType_Ready(&FFI_Type) < 0) return -1; if (PyType_Ready(&Lib_Type) < 0) return -1; if (!init_done) { if (init_global_types_dict(FFI_Type.tp_dict) < 0) return -1; FFIError = PyErr_NewException("ffi.error", NULL, NULL); if (FFIError == NULL) return -1; if (PyDict_SetItemString(FFI_Type.tp_dict, "error", FFIError) < 0) return -1; if (PyDict_SetItemString(FFI_Type.tp_dict, "CType", (PyObject *)&CTypeDescr_Type) < 0) return -1; if (PyDict_SetItemString(FFI_Type.tp_dict, "CData", (PyObject *)&CData_Type) < 0) return -1; for (i = 0; all_dlopen_flags[i].name != NULL; i++) { x = PyInt_FromLong(all_dlopen_flags[i].value); if (x == NULL) return -1; res = PyDict_SetItemString(FFI_Type.tp_dict, all_dlopen_flags[i].name, x); Py_DECREF(x); if (res < 0) return -1; } init_done = 1; } x = (PyObject *)&FFI_Type; Py_INCREF(x); if (PyModule_AddObject(m, "FFI", x) < 0) return -1; x = (PyObject *)&Lib_Type; Py_INCREF(x); if (PyModule_AddObject(m, "Lib", x) < 0) return -1; return 0; } static int make_included_tuples(char *module_name, const char *const *ctx_includes, PyObject **included_ffis, PyObject **included_libs) { Py_ssize_t num = 0; const char *const *p_include; if (ctx_includes == NULL) return 0; for (p_include = ctx_includes; *p_include; p_include++) { num++; } *included_ffis = PyTuple_New(num); *included_libs = PyTuple_New(num); if (*included_ffis == NULL || *included_libs == NULL) goto error; num = 0; for (p_include = ctx_includes; *p_include; p_include++) { PyObject *included_ffi, *included_lib; PyObject *m = PyImport_ImportModule(*p_include); if (m == NULL) goto import_error; included_ffi = PyObject_GetAttrString(m, "ffi"); PyTuple_SET_ITEM(*included_ffis, num, included_ffi); included_lib = (included_ffi == NULL) ? NULL : PyObject_GetAttrString(m, "lib"); PyTuple_SET_ITEM(*included_libs, num, included_lib); Py_DECREF(m); if (included_lib == NULL) goto import_error; if (!FFIObject_Check(included_ffi) || !LibObject_Check(included_lib)) goto import_error; num++; } return 0; import_error: PyErr_Format(PyExc_ImportError, "while loading %.200s: failed to import ffi, lib from %.200s", module_name, *p_include); error: Py_XDECREF(*included_ffis); *included_ffis = NULL; Py_XDECREF(*included_libs); *included_libs = NULL; return -1; } static PyObject *_my_Py_InitModule(char *module_name) { #if PY_MAJOR_VERSION >= 3 struct PyModuleDef *module_def, local_module_def = { PyModuleDef_HEAD_INIT, module_name, NULL, -1, NULL, NULL, NULL, NULL, NULL }; /* note: the 'module_def' is allocated dynamically and leaks, but anyway the C extension module can never be unloaded */ module_def = PyMem_Malloc(sizeof(struct PyModuleDef)); if (module_def == NULL) return PyErr_NoMemory(); *module_def = local_module_def; return PyModule_Create(module_def); #else return Py_InitModule(module_name, NULL); #endif } static PyObject *b_init_cffi_1_0_external_module(PyObject *self, PyObject *arg) { PyObject *m, *modules_dict; FFIObject *ffi; LibObject *lib; Py_ssize_t version, num_exports; char *module_name, *exports, *module_name_with_lib; void **raw; const struct _cffi_type_context_s *ctx; raw = (void **)PyLong_AsVoidPtr(arg); if (raw == NULL) return NULL; module_name = (char *)raw[0]; version = (Py_ssize_t)raw[1]; exports = (char *)raw[2]; ctx = (const struct _cffi_type_context_s *)raw[3]; if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) { if (!PyErr_Occurred()) PyErr_Format(PyExc_ImportError, "cffi extension module '%s' uses an unknown version tag %p. " "This module might need a more recent version of cffi " "than the one currently installed, which is %s", module_name, (void *)version, CFFI_VERSION); return NULL; } /* initialize the exports array */ num_exports = 25; if (ctx->flags & 1) /* set to mean that 'extern "Python"' is used */ num_exports = 26; memcpy(exports, (char *)cffi_exports, num_exports * sizeof(void *)); /* make the module object */ m = _my_Py_InitModule(module_name); if (m == NULL) return NULL; /* build the FFI and Lib object inside this new module */ ffi = ffi_internal_new(&FFI_Type, ctx); Py_XINCREF(ffi); /* make the ffi object really immortal */ if (ffi == NULL || PyModule_AddObject(m, "ffi", (PyObject *)ffi) < 0) return NULL; lib = lib_internal_new(ffi, module_name, NULL); if (lib == NULL || PyModule_AddObject(m, "lib", (PyObject *)lib) < 0) return NULL; if (make_included_tuples(module_name, ctx->includes, &ffi->types_builder.included_ffis, &lib->l_types_builder->included_libs) < 0) return NULL; /* add manually 'module_name.lib' in sys.modules: see test_import_from_lib */ modules_dict = PySys_GetObject("modules"); if (!modules_dict) return NULL; module_name_with_lib = alloca(strlen(module_name) + 5); strcpy(module_name_with_lib, module_name); strcat(module_name_with_lib, ".lib"); if (PyDict_SetItemString(modules_dict, module_name_with_lib, (PyObject *)lib) < 0) return NULL; #if PY_MAJOR_VERSION >= 3 /* add manually 'module_name' in sys.modules: it seems that Py_InitModule() is not enough to do that */ if (PyDict_SetItemString(modules_dict, module_name, m) < 0) return NULL; #endif return m; } cffi-1.5.2/doc/0000775000175000017500000000000012657646372013443 5ustar arigoarigo00000000000000cffi-1.5.2/doc/source/0000775000175000017500000000000012657646372014743 5ustar arigoarigo00000000000000cffi-1.5.2/doc/source/cdef.rst0000664000175000017500000010742112657646311016374 0ustar arigoarigo00000000000000====================================== Preparing and Distributing modules ====================================== .. contents:: There are three or four different ways to use CFFI in a project. In order of complexity: * The **"in-line", "ABI mode"**: .. code-block:: python import cffi ffi = cffi.FFI() ffi.cdef("C-like declarations") lib = ffi.dlopen("libpath") # use ffi and lib here .. _out-of-line-abi: * The **"out-of-line",** but still **"ABI mode",** useful to organize the code and reduce the import time: .. code-block:: python # in a separate file "package/foo_build.py" import cffi ffi = cffi.FFI() ffi.set_source("package._foo", None) ffi.cdef("C-like declarations") if __name__ == "__main__": ffi.compile() Running ``python foo_build.py`` produces a file ``_foo.py``, which can then be imported in the main program: .. code-block:: python from package._foo import ffi lib = ffi.dlopen("libpath") # use ffi and lib here .. _out-of-line-api: * The **"out-of-line", "API mode"** gives you the most flexibility to access a C library at the level of C, instead of at the binary level: .. code-block:: python # in a separate file "package/foo_build.py" import cffi ffi = cffi.FFI() ffi.set_source("package._foo", "real C code") # <= ffi.cdef("C-like declarations with '...'") if __name__ == "__main__": ffi.compile(verbose=True) Running ``python foo_build.py`` produces a file ``_foo.c`` and invokes the C compiler to turn it into a file ``_foo.so`` (or ``_foo.pyd`` or ``_foo.dylib``). It is a C extension module which can be imported in the main program: .. code-block:: python from package._foo import ffi, lib # no ffi.dlopen() # use ffi and lib here .. _distutils-setuptools: * Finally, you can (but don't have to) use CFFI's **Distutils** or **Setuptools integration** when writing a ``setup.py``. For Distutils (only in out-of-line API mode): .. code-block:: python # setup.py (requires CFFI to be installed first) from distutils.core import setup import foo_build # possibly with sys.path tricks to find it setup( ..., ext_modules=[foo_build.ffi.distutils_extension()], ) For Setuptools (out-of-line, but works in ABI or API mode; recommended): .. code-block:: python # setup.py (with automatic dependency tracking) from setuptools import setup setup( ..., setup_requires=["cffi>=1.0.0"], cffi_modules=["package/foo_build.py:ffi"], install_requires=["cffi>=1.0.0"], ) Note that CFFI actually contains two different ``FFI`` classes. The page `Using the ffi/lib objects`_ describes the common functionality. It is what you get in the ``from package._foo import ffi`` lines above. On the other hand, the extended ``FFI`` class is the one you get from ``import cffi; ffi = cffi.FFI()``. It has the same functionality (for in-line use), but also the extra methods described below (to prepare the FFI). .. _`Using the ffi/lib objects`: using.html The reason for this split of functionality is that a regular program using CFFI out-of-line does not need to import the ``cffi`` pure Python package at all. (Internally it still needs ``_cffi_backend``, a C extension module that comes with CFFI; this is why CFFI is also listed in ``install_requires=..`` above. In the future this might be split into a different PyPI package that only installs ``_cffi_backend``.) Note that a few small differences do exist: notably, ``from _foo import ffi`` returns an object of a type written in C, which does not let you add random attributes to it (nor does it have all the underscore-prefixed internal attributes of the Python version). Similarly, the ``lib`` objects returned by the C version are read-only, apart from writes to global variables. Also, ``lib.__dict__`` does not work before version 1.2 or if ``lib`` happens to declare a name called ``__dict__`` (use instead ``dir(lib)``). The same is true for ``lib.__class__`` before version 1.4. .. _cdef: ffi.cdef(): declaring types and functions ----------------------------------------- **ffi.cdef(source)**: parses the given C source. It registers all the functions, types, constants 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.set_source()``). .. _`all types listed above`: 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) * _Bool and bool (equivalent). If not directly supported by the C compiler, this is declared with the size of ``unsigned char``. * 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``. * all `common Windows types`_ are defined if you run on Windows (``DWORD``, ``LPARAM``, etc.). *Changed in version 0.9:* the types ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE PTCHAR`` are no longer automatically defined; see `ffi.set_unicode()`_. * *New in version 0.9.3:* the other standard integer types from stdint.h, like ``intmax_t``, as long as they map to integers of 1, 2, 4 or 8 bytes. Larger integers are not supported. .. _`common Windows types`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx The declarations can also contain "``...``" at various places; these are placeholders that will be completed by the compiler. More information about it below in `Letting the C compiler fill the gaps`_. Note that all standard type names listed above are 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.) Multiple calls to ``ffi.cdef()`` are possible. Beware that it can be slow to call ``ffi.cdef()`` a lot of times, a consideration that is important mainly in in-line mode. The ``ffi.cdef()`` call takes an optional argument ``packed``: if True, then all structs declared within this cdef are "packed". (If you need both packed and non-packed structs, use several cdefs in sequence.) 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. Also, this has no effect on structs declared with ``"...;"``---more about it later in `Letting the C compiler fill the gaps`_.) Note that you can use the type-qualifiers ``const`` and ``restrict`` (but not ``__restrict`` or ``__restrict__``) in the ``cdef()``, but this has no effect on the cdata objects that you get at run-time (they are never ``const``). The effect is limited to knowing if a global variable is meant to be a constant or not. Also, *new in version 1.3:* when using ``set_source()`` or ``verify()``, these two qualifiers are copied from the cdef to the generated C code; this fixes warnings by the C compiler. Note a trick if you copy-paste code from sources in which there are extra macros (for example, the Windows documentation uses SAL annotations like ``_In_`` or ``_Out_``). These hints must be removed in the string given to cdef(), but it can be done programmatically like this:: ffi.cdef(re.sub(r"\b(_In_|_Inout_|_Out_|_Outptr_)(opt_)?\b", " ", """ DWORD WINAPI GetModuleFileName( _In_opt_ HMODULE hModule, _Out_ LPTSTR lpFilename, _In_ DWORD nSize ); """)) .. _`ffi.set_unicode()`: **ffi.set_unicode(enabled_flag)**: Windows: if ``enabled_flag`` is True, enable the ``UNICODE`` and ``_UNICODE`` defines in C, and declare the types ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE PTCHAR`` to be (pointers to) ``wchar_t``. If ``enabled_flag`` is False, declare these types to be (pointers to) plain 8-bit characters. (These types are not predeclared at all if you don't call ``set_unicode()``.) *New in version 0.9.* The reason behind this method is that a lot of standard functions have two versions, like ``MessageBoxA()`` and ``MessageBoxW()``. The official interface is ``MessageBox()`` with arguments like ``LPTCSTR``. Depending on whether ``UNICODE`` is defined or not, the standard header renames the generic function name to one of the two specialized versions, and declares the correct (unicode or not) types. Usually, the right thing to do is to call this method with True. Be aware (particularly on Python 2) that, afterwards, you need to pass unicode strings as arguments instead of byte strings. (Before cffi version 0.9, ``TCHAR`` and friends where hard-coded as unicode, but ``UNICODE`` was, inconsistently, not defined by default.) .. _loading-libraries: ffi.dlopen(): loading libraries in ABI mode ------------------------------------------- ``ffi.dlopen(libpath, [flags])``: this function opens a shared library and returns a module-like library object. Use this when you are fine with the limitations of ABI-level access to the system. In case of doubt, read again `ABI versus API`_ in the overview. .. _`ABI versus API`: overview.html#abi-versus-api You can use the library object to call the functions previously declared by ``ffi.cdef()``, to read constants, 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). Let me state it again: this gives ABI-level access to the library, so you need to have all types declared manually exactly as they were while the library was made. No checking is done. Mismatches can cause random crashes. Note that only functions and global variables live in library objects; the 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. (Alternatively, the out-of-line FFIs have a method ``ffi.dlclose(lib)``.) .. _dlopen-note: Note: the old version of ``ffi.dlopen()`` from the in-line ABI mode tries to use ``ctypes.util.find_library()`` if it cannot directly find the library. The newer out-of-line ``ffi.dlopen()`` no longer does it automatically; it simply passes the argument it receives to the underlying ``dlopen()`` or ``LoadLibrary()`` function. If needed, it is up to you to use ``ctypes.util.find_library()`` or any other way to look for the library's filename. This also means that ``ffi.dlopen(None)`` no longer work on Windows; try instead ``ffi.dlopen(ctypes.util.find_library('c'))``. ffi.set_source(): preparing out-of-line modules ----------------------------------------------- **ffi.set_source(module_name, c_header_source, [\*\*keywords...])**: prepare the ffi for producing out-of-line an external module called ``module_name``. *New in version 1.0.* ``ffi.set_source()`` by itself does not write any file, but merely records its arguments for later. It can therefore be called before or after ``ffi.cdef()``. In **ABI mode,** you call ``ffi.set_source(module_name, None)``. The argument is the name (or dotted name inside a package) of the Python module to generate. In this mode, no C compiler is called. In **API mode,** the ``c_header_source`` argument is a string that will be pasted into the .c file generated. This piece of C code typically contains some ``#include``, but may also contain more, like definitions for custom "wrapper" C functions. The goal is that the .c file can be generated like this:: // C file "module_name.c" #include ...c_header_source... ...magic code... where the "magic code" is automatically generated from the ``cdef()``. For example, if the ``cdef()`` contains ``int foo(int x);`` then the magic code will contain logic to call the function ``foo()`` with an integer argument, itself wrapped inside some CPython or PyPy-specific code. The keywords arguments to ``set_source()`` control how the C compiler will be called. They are passed directly to distutils_ or setuptools_ and include at least ``sources``, ``include_dirs``, ``define_macros``, ``undef_macros``, ``libraries``, ``library_dirs``, ``extra_objects``, ``extra_compile_args`` and ``extra_link_args``. 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 (the file ``module_name.c`` shown above is always generated and automatically added as the first argument to ``sources``). See the distutils documentations 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 .. _setuptools: https://pythonhosted.org/setuptools/setuptools.html An extra keyword argument processed internally is ``source_extension``, defaulting to ``".c"``. The file generated will be actually called ``module_name + source_extension``. Example for C++ (but note that there are still a few known issues of C-versus-C++ compatibility): .. code-block:: python ffi.set_source("mymodule", ''' extern "C" { int somefunc(int somearg) { return real_cpp_func(somearg); } } ''', source_extension='.cpp') Letting the C compiler fill the gaps ------------------------------------ If you are using a C compiler ("API mode"), then: * 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 *``. * similarly, most other things declared in the ``cdef()`` are checked, to the best we implemented so far; mistakes give compilation warnings or errors. Moreover, you can use "``...``" (literally, dot-dot-dot) in the ``cdef()`` at various places, in order to ask the C compiler to fill in the details. These places are: * structure declarations: any ``struct { }`` that ends with "``...;``" as the last "field" 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 an error if it is not correct. * *New in version 1.1:* integer types: the syntax "``typedef int... foo_t;``" declares the type ``foo_t`` as an integer type whose exact size and signedness is not specified. The compiler will figure it out. (Note that this requires ``set_source()``; it does not work with ``verify()``.) The ``int...`` can be replaced with ``long...`` or ``unsigned long long...`` or any other primitive integer type, with no effect. The type will always map to one of ``(u)int(8,16,32,64)_t`` in Python, but in the generated C code, only ``foo_t`` is used. * *New in version 1.3:* floating-point types: "``typedef float... foo_t;``" (or equivalently "``typedef double... foo_t;``") declares ``foo_t`` as a-float-or-a-double; the compiler will figure out which it is. Note that if the actual C type is even larger (``long double`` on some platforms), then compilation will fail. The problem is that the Python "float" type cannot be used to store the extra precision. (Use the non-dot-dot-dot syntax ``typedef long double foo_t;`` as usual, which returns values that are not Python floats at all but cdata "long double" objects.) * 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 struct-like types only.* In some cases you need to say that ``foo_t`` is not opaque, but just a struct where you don't know any field; 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. This is slightly different from "``int n[];``", because the latter means that the length is not known even to the C compiler, and thus no attempt is made to complete it. *New in version 1.1:* support for multidimensional arrays: "``int n[...][...];``". *New in version 1.2:* "``int m[][...];``", i.e. ``...`` can be used in the innermost dimensions without being also used in the outermost dimension. In the example given, the length of the ``m`` array is assumed not to be known to the C compiler, but the length of every item (like the sub-array ``m[0]``) is always known the C compiler. In other words, only the outermost dimension can be specified as ``[]``, both in C and in CFFI, but any dimension can be given as ``[...]`` in CFFI. * 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 constants and macros: you can write in the ``cdef`` the line "``#define FOO ...``", with any macro name FOO but with ``...`` as a value. Provided the macro is defined to be an integer value, this value will be available via an attribute of the library object. 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 C 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. If a type is named, and an integer type, then use ``typedef int... the_type_name;``. 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 misdeclared ``long a[5];``). CFFI considers `all types listed above`_ as primitive (so ``long long a[5];`` and ``int64_t a[5]`` are different declarations). ffi.compile() etc.: compiling out-of-line modules ------------------------------------------------- You can use one of the following functions to actually generate the .py or .c file prepared with ``ffi.set_source()`` and ``ffi.cdef()``. Note that these function won't overwrite a .py/.c file with exactly the same content, to preserve the mtime. In some cases where you need the mtime to be updated anyway, delete the file before calling the functions. **ffi.compile(tmpdir='.', verbose=False):** explicitly generate the .py or .c file, and (if .c) compile it. The output file is (or are) put in the directory given by ``tmpdir``. In the examples given here, we use ``if __name__ == "__main__": ffi.compile()`` in the build scripts---if they are directly executed, this makes them rebuild the .py/.c file in the current directory. (Note: if a package is specified in the call to ``set_source()``, then a corresponding subdirectory of the ``tmpdir`` is used.) *New in version 1.4:* ``verbose`` argument. If True, it prints the usual distutils output, including the command lines that call the compiler. (This parameter might be changed to True by default in a future release.) **ffi.emit_python_code(filename):** generate the given .py file (same as ``ffi.compile()`` for ABI mode, with an explicitly-named file to write). If you choose, you can include this .py file pre-packaged in your own distributions: it is identical for any Python version (2 or 3). **ffi.emit_c_code(filename):** generate the given .c file (for API mode) without compiling it. Can be used if you have some other method to compile it, e.g. if you want to integrate with some larger build system that will compile this file for you. You can also distribute the .c file: unless the build script you used depends on the OS or platform, the .c file itself is generic (it would be exactly the same if produced on a different OS, with a different version of CPython, or with PyPy; it is done with generating the appropriate ``#ifdef``). **ffi.distutils_extension(tmpdir='build', verbose=True):** for distutils-based ``setup.py`` files. Calling this creates the .c file if needed in the given ``tmpdir``, and returns a ``distutils.core.Extension`` instance. For Setuptools, you use instead the line ``cffi_modules=["path/to/foo_build.py:ffi"]`` in ``setup.py``. This line asks Setuptools to import and use a helper provided by CFFI, which in turn executes the file ``path/to/foo_build.py`` (as with ``execfile()``) and looks up its global variable called ``ffi``. You can also say ``cffi_modules=["path/to/foo_build.py:maker"]``, where ``maker`` names a global function; it is called with no argument and is supposed to return a ``FFI`` object. ffi.include(): combining multiple CFFI interfaces ------------------------------------------------- **ffi.include(other_ffi)**: includes the typedefs, structs, unions, enums and constants defined in another FFI instance. This is meant for large projects where one CFFI-based interface depends on some types declared in a different CFFI-based interface. *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. (You can write several ``cdef()`` calls over the same ``ffi`` from several Python files, if one file would be too large.) For out-of-line modules, the ``ffi.include(other_ffi)`` line should occur in the build script, and the ``other_ffi`` argument should be another FFI that comes from another build script. When the two build scripts are turned into generated files, say ``_ffi.so`` and ``_other_ffi.so``, then importing ``_ffi.so`` will internally cause ``_other_ffi.so`` to be imported. At that point, the real declarations from ``_other_ffi.so`` are combined with the real declarations from ``_ffi.so``. The usage of ``ffi.include()`` is the cdef-level equivalent of a ``#include`` in C, where a part of the program might include types and functions defined in another part for its own usage. You can see on the ``ffi`` object (and associated ``lib`` objects on the *including* side) the types and constants declared on the included side. In API mode, you can also see the functions and global variables directly. In ABI mode, these must be accessed via the original ``other_lib`` object returned by the ``dlopen()`` method on ``other_ffi``. ffi.cdef() limitations ---------------------- All of the ANSI C *declarations* should be supported in ``cdef()``, and some of C99. (This excludes any ``#include`` or ``#ifdef``.) 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. * Function pointers with non-default calling conventions (e.g. on Windows, "stdcall"). Note that declarations like ``int field[];`` in structures are interpreted as variable-length structures. Declarations like ``int field[...];`` on the other hand are arrays whose length is going to be completed by the compiler. You can use ``int field[];`` for array fields that are not, in fact, variable-length; it works too, but in this case, 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. *New in version 1.2:* Thread-local variables (``__thread``) can be accessed, as well as variables defined as dynamic macros (``#define myvar (*fetchme())``). Before version 1.2, you need to write getter/setter functions. 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. ffi.verify(): in-line API-mode ------------------------------ **ffi.verify()** is supported for backward compatibility, but is deprecated. ``ffi.verify(c_header_source, tmpdir=.., ext_package=.., modulename=.., flags=.., **kwargs)`` makes and compiles a C file from the ``ffi.cdef()``, like ``ffi.set_source()`` in API mode, and then immediately loads and returns the dynamic library object. Some non-trivial logic is used to decide if the dynamic library must be recompiled or not; see below for ways to control it. The ``c_header_source`` and the extra keyword arguments have the same meaning as in ``ffi.set_source()``. One remaining use case for ``ffi.verify()`` would be the following hack to find explicitly the size of any type, in bytes, and have it available in Python immediately (e.g. because it is needed in order to write the rest of the build script): .. code-block:: python ffi = cffi.FFI() ffi.cdef("const int mysize;") lib = ffi.verify("const int mysize = sizeof(THE_TYPE);") print lib.mysize Extra arguments to ``ffi.verify()``: * ``tmpdir`` controls where the C files are created and compiled. Unless the ``CFFI_TMPDIR`` environment variable is set, the default 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.) * ``ext_package`` controls in which package the compiled extension module should be looked from. This is only useful after distributing ffi.verify()-based modules. * 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. * 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. * ``source_extension`` has the same meaning as in ``ffi.set_source()``. * The optional ``flags`` argument has been added in version 0.9; see ``man dlopen`` (ignored on Windows). It defaults to ``ffi.RTLD_NOW``. (With ``ffi.set_source()``, you would use ``sys.setdlopenflags()``.) * The optional ``relative_to`` argument is useful if you need to list local files passed to the C compiler:: ext = ffi.verify(..., sources=['foo.c'], relative_to=__file__) The line above is roughly the same as:: ext = ffi.verify(..., sources=['/path/to/this/file/foo.c']) except that the default name of the produced library is built from the CRC checkum of the argument ``sources``, as well as most other arguments you give to ``ffi.verify()`` -- but not ``relative_to``. So if you used the second line, it would stop finding the already-compiled library after your project is installed, because the ``'/path/to/this/file'`` suddenly changed. The first line does not have this problem. Note that 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 manually remove the whole ``__pycache__`` directory. An alternative cache directory can be given as the ``tmpdir`` argument to ``verify()``, via the environment variable ``CFFI_TMPDIR``, or by calling ``cffi.verifier.set_tmpdir(path)`` prior to calling ``verify``. Upgrading from CFFI 0.9 to CFFI 1.0 ----------------------------------- CFFI 1.0 is backward-compatible, but it is still a good idea to consider moving to the out-of-line approach new in 1.0. Here are the steps. **ABI mode** if your CFFI project uses ``ffi.dlopen()``: .. code-block:: python import cffi ffi = cffi.FFI() ffi.cdef("stuff") lib = ffi.dlopen("libpath") and *if* the "stuff" part is big enough that import time is a concern, then rewrite it as described in `the out-of-line but still ABI mode`__ above. Optionally, see also the `setuptools integration`__ paragraph. .. __: out-of-line-abi_ .. __: distutils-setuptools_ **API mode** if your CFFI project uses ``ffi.verify()``: .. code-block:: python import cffi ffi = cffi.FFI() ffi.cdef("stuff") lib = ffi.verify("real C code") then you should really rewrite it as described in `the out-of-line, API mode`__ above. It avoids a number of issues that have caused ``ffi.verify()`` to grow a number of extra arguments over time. Then see the `distutils or setuptools`__ paragraph. Also, remember to remove the ``ext_package=".."`` from your ``setup.py``, which was sometimes needed with ``verify()`` but is just creating confusion with ``set_source()``. .. __: out-of-line-api_ .. __: distutils-setuptools_ The following example should work both with old (pre-1.0) and new versions of CFFI---supporting both is important to run on PyPy, because CFFI 1.0 does not work in PyPy < 2.6: .. code-block:: python # in a separate file "package/foo_build.py" import cffi ffi = cffi.FFI() C_HEADER_SRC = ''' #include "somelib.h" ''' C_KEYWORDS = dict(libraries=['somelib']) if hasattr(ffi, 'set_source'): ffi.set_source("package._foo", C_HEADER_SRC, **C_KEYWORDS) ffi.cdef(''' int foo(int); ''') if __name__ == "__main__": ffi.compile() And in the main program: .. code-block:: python try: from package._foo import ffi, lib except ImportError: from package.foo_build import ffi, C_HEADER_SRC, C_KEYWORDS lib = ffi.verify(C_HEADER_SRC, **C_KEYWORDS) (FWIW, this latest trick can be used more generally to allow the import to "work" even if the ``_foo`` module was not generated.) Writing a ``setup.py`` script that works both with CFFI 0.9 and 1.0 requires explicitly checking the version of CFFI that we can have---it is hard-coded as a built-in module in PyPy: .. code-block:: python if '_cffi_backend' in sys.builtin_module_names: # PyPy import _cffi_backend requires_cffi = "cffi==" + _cffi_backend.__version__ else: requires_cffi = "cffi>=1.0.0" Then we use the ``requires_cffi`` variable to give different arguments to ``setup()`` as needed, e.g.: .. code-block:: python if requires_cffi.startswith("cffi==0."): # backward compatibility: we have "cffi==0.*" from package.foo_build import ffi extra_args = dict( ext_modules=[ffi.verifier.get_extension()], ext_packages="...", # if needed ) else: extra_args = dict( setup_requires=[requires_cffi], cffi_modules=['package/foo_build.py:ffi'], ) setup( name=..., ..., install_requires=[requires_cffi], **extra_args ) cffi-1.5.2/doc/source/using.rst0000664000175000017500000016211512657646311016621 0ustar arigoarigo00000000000000================================ Using the ffi/lib objects ================================ .. contents:: Keep this page under your pillow. .. _working: 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. 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("int *") >>> ffi.new("int[10]") >>> ffi.new("char *") # allocates only one char---not a C string! >>> ffi.new("char[]", "foobar") # this allocates a C string, ending in \0 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: .. code-block:: python global_weakkeydict = weakref.WeakKeyDictionary() def make_foo(): 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. return s1 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. We have ``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)``. For example, reading a NULL pointer returns a ````, 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()`_. 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: .. code-block:: python # void somefunction(int *); 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 (adding surrogates 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. The C99 variable-sized structures are supported too, as long as the initializer says how long the array should be: .. code-block:: python # typedef struct { int x; int y[]; } foo_t; p = ffi.new("foo_t *", [5, [6, 7, 8]]) # length 3 p = ffi.new("foo_t *", [5, 3]) # length 3 with 0 in the array p = ffi.new("foo_t *", {'y': 3}) # length 3 with 0 everywhere Finally, note that any Python object used as initializer can also be used directly without ``ffi.new()`` in assignments to array items or struct fields. In fact, ``p = ffi.new("T*", initializer)`` is equivalent to ``p = ffi.new("T*"); p[0] = initializer``. Examples: .. code-block:: python # if 'p' is a p[2] = [10, 20] # writes to p[2][0] and p[2][1] # if 'p' is a , and foo_t has fields x, y and z p[0] = {'x': 10, 'z': 20} # writes to p.x and p.z; p.y unmodified # if, on the other hand, foo_t has a field 'char a[5]': p.a = "abc" # writes 'a', 'b', 'c' and '\0'; p.a[4] unmodified In function calls, when passing arguments, these rules can be used too; see `Function calls`_. 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) 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: .. code-block:: python # int foo(short a, int b); n = lib.foo(2, 3) # returns a normal integer lib.foo(40000, 3) # raises OverflowError 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!): .. code-block:: python # size_t strlen(const char *); assert lib.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 even ``int[5]``; the 5 is ignored). So you can pass an ``int *`` as a list of integers: .. code-block:: python # void do_something_with_array(int *array); lib.do_something_with_array([1, 2, 3, 4, 5]) See `Reference: conversions`_ for a similar way to pass ``struct foo_s *`` arguments---but in general, it is clearer to simply pass ``ffi.new('struct foo_s *', initializer)``. CFFI supports passing and returning structs to functions and callbacks. Example: .. code-block:: python # struct foo_s { int a, b; }; # struct foo_s function_returning_a_struct(void); myfoo = lib.function_returning_a_struct() # `myfoo`: 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 "``...;``" in the ``cdef()``; 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 C header code passed to ``ffi.set_source()``, and have this C function call the real one. Aside from these limitations, functions and callbacks can receive and return structs. For performance, API-level functions are not returned as ```` objects, but as a different type (on CPython, ````). This means you cannot e.g. pass them to some other C function expecting a function pointer argument. Only ``ffi.typeof()`` works on them. To get a cdata containing a regular function pointer, use ``ffi.addressof(lib, "name")`` (new in version 1.1). Before version 1.1 (or with the deprecated ``ffi.verify()``), if you really need a cdata pointer to the function, use the following workaround: .. code-block:: python 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:: lib.printf("hello, %d\n", 42) # doesn't work! 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()``: .. code-block:: python lib.printf("hello, %d\n", ffi.cast("int", 42)) lib.printf("hello, %ld\n", ffi.cast("long", 42)) lib.printf("hello, %f\n", ffi.cast("double", 42)) But of course: .. code-block:: python lib.printf("hello, %s\n", ffi.new("char[]", "world")) Note that if you are using ``dlopen()``, the function declaration in the ``cdef()`` must match the original one in C exactly, as usual --- in particular, if this function is variadic in C, then its ``cdef()`` declaration must also be variadic. You cannot declare it in the ``cdef()`` with fixed arguments instead, even if you plan to only call it with these argument types. The reason is that some architectures have a different calling convention depending on whether the function signature is fixed or not. (On x86-64, the difference can sometimes be seen in PyPy's JIT-generated code if some arguments are ``double``.) Note that the function signature ``int foo();`` is interpreted by CFFI as equivalent to ``int foo(void);``. This differs from the C standard, in which ``int foo();`` is really like ``int foo(...);`` and can be called with any arguments. (This feature of C is a pre-C89 relic: the arguments cannot be accessed at all in the body of ``foo()`` without relying on compiler-specific extensions. Nowadays virtually all code with ``int foo();`` really means ``int foo(void);``.) .. _extern-python: .. _`extern "Python"`: Extern "Python" (new-style callbacks) ------------------------------------- When the C code needs a pointer to a function which invokes back a Python function of your choice, here is how you do it in the out-of-line API mode. The next section about Callbacks_ describes the ABI-mode solution. This is *new in version 1.4.* Use old-style Callbacks_ if backward compatibility is an issue. (The original callbacks are slower to invoke and have the same issue as libffi's callbacks; notably, see the warning__. The new style described in the present section does not use libffi's callbacks at all.) .. __: Callbacks_ In the builder script, declare in the cdef a function prefixed with ``extern "Python"``:: ffi.cdef(""" extern "Python" int my_callback(int, int); void library_function(int(*callback)(int, int)); """) ffi.set_source("_my_example", """ #include """) The function ``my_callback()`` is then implemented in Python inside your application's code:: from _my_example import ffi, lib @ffi.def_extern() def my_callback(x, y): return 42 You obtain a ```` pointer-to-function object by getting ``lib.my_callback``. This ```` can be passed to C code and then works like a callback: when the C code calls this function pointer, the Python function ``my_callback`` is called. (You need to pass ``lib.my_callback`` to C code, and not ``my_callback``: the latter is just the Python function above, which cannot be passed to C.) CFFI implements this by defining ``my_callback`` as a static C function, written after the ``set_source()`` code. The ```` then points to this function. What this function does is invoke the Python function object that is, at runtime, attached with ``@ffi.def_extern()``. The ``@ffi.def_extern()`` decorator should be applied to **global functions,** one for each ``extern "Python"`` function of the same name. To support some corner cases, it is possible to redefine the attached Python function by calling ``@ffi.def_extern()`` again for the same name---but this is not recommended! Better attach a single global Python function for this name, and write it more flexibly in the first place. This is because each ``extern "Python"`` function turns into only one C function. Calling ``@ffi.def_extern()`` again changes this function's C logic to call the new Python function; the old Python function is not callable any more. The C function pointer you get from ``lib.my_function`` is always this C function's address, i.e. it remains the same. Extern "Python" and ``void *`` arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As described just before, you cannot use ``extern "Python"`` to make a variable number of C function pointers. However, achieving that result is not possible in pure C code either. For this reason, it is usual for C to define callbacks with a ``void *data`` argument. You can use ``ffi.new_handle()`` and ``ffi.from_handle()`` to pass a Python object through this ``void *`` argument. For example, if the C type of the callbacks is:: typedef void (*event_cb_t)(event_t *evt, void *userdata); and you register events by calling this function:: void event_cb_register(event_cb_t cb, void *userdata); Then you would write this in the build script:: ffi.cdef(""" typedef ... event_t; typedef void (*event_cb_t)(event_t *evt, void *userdata); void event_cb_register(event_cb_t cb, void *userdata); extern "Python" void my_event_callback(event_t *, void *); """) ffi.set_source("_demo_cffi", """ #include """) and in your main application you register events like this:: from _demo_cffi import ffi, lib class Widget(object): def __init__(self): userdata = ffi.new_handle(self) self._userdata = userdata # must keep this alive! lib.event_cb_register(lib.my_event_callback, userdata) def process_event(self, evt): ... @ffi.def_extern() def my_event_callback(evt, userdata): widget = ffi.from_handle(userdata) widget.process_event(evt) Some other libraries don't have an explicit ``void *`` argument, but let you attach the ``void *`` to an existing structure. For example, the library might say that ``widget->userdata`` is a generic field reserved for the application. If the event's signature is now this:: typedef void (*event_cb_t)(widget_t *w, event_t *evt); Then you can use the ``void *`` field in the low-level ``widget_t *`` like this:: from _demo_cffi import ffi, lib class Widget(object): def __init__(self): ll_widget = lib.new_widget(500, 500) self.ll_widget = ll_widget # userdata = ffi.new_handle(self) self._userdata = userdata # must still keep this alive! ll_widget.userdata = userdata # this makes a copy of the "void *" lib.event_cb_register(ll_widget, lib.my_event_callback) def process_event(self, evt): ... @ffi.def_extern() def my_event_callback(ll_widget, evt): widget = ffi.from_handle(ll_widget.userdata) widget.process_event(evt) Extern "Python" accessed from C directly ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In case you want to access some ``extern "Python"`` function directly from the C code written in ``set_source()``, you need to write a forward static declaration. The real implementation of this function is added by CFFI *after* the C code---this is needed because the declaration might use types defined by ``set_source()`` (e.g. ``event_t`` above, from the ``#include``), so it cannot be generated before. :: ffi.set_source("_demo_cffi", """ #include static void my_event_callback(widget_t *, event_t *); /* here you can write C code which uses '&my_event_callback' */ """) This can also be used to write custom C code which calls Python directly. Here is an example (inefficient in this case, but might be useful if the logic in ``my_algo()`` is much more complex):: ffi.cdef(""" extern "Python" int f(int); int my_algo(int); """) ffi.set_source("_example_cffi", """ static int f(int); /* the forward declaration */ static int my_algo(int n) { int i, sum = 0; for (i = 0; i < n; i++) sum += f(i); /* call f() here */ return sum; } """) Extern "Python": reference ~~~~~~~~~~~~~~~~~~~~~~~~~~ ``extern "Python"`` must appear in the cdef(). Like the C++ ``extern "C"`` syntax, it can also be used with braces around a group of functions:: extern "Python" { int foo(int); int bar(int); } The ``extern "Python"`` functions cannot be variadic for now. This may be implemented in the future. (`This demo`__ shows how to do it anyway, but it is a bit lengthy.) .. __: https://bitbucket.org/cffi/cffi/src/default/demo/extern_python_varargs.py Each corresponding Python callback function is defined with the ``@ffi.def_extern()`` decorator. Be careful when writing this function: if it raises an exception, or tries to return an object of the wrong type, then the exception cannot be propagated. Instead, the exception is printed to stderr and the C-level callback is made to return a default value. This can be controlled with ``error`` and ``onerror``, described below. .. _def-extern: The ``@ffi.def_extern()`` decorator takes these optional arguments: * ``name``: the name of the function as written in the cdef. By default it is taken from the name of the Python function you decorate. .. _error_onerror: * ``error``: the returned value in case the Python function raises an exception. It is 0 or null by default. The exception is still printed to stderr, so this should be used only as a last-resort solution. * ``onerror``: if you want to be sure to catch all exceptions, use ``@ffi.def_extern(onerror=my_handler)``. If an exception occurs and ``onerror`` is specified, then ``onerror(exception, exc_value, traceback)`` is called. This is useful in some situations where you cannot simply write ``try: except:`` in the main callback function, because it might not catch exceptions raised by signal handlers: if a signal occurs while in C, the Python signal handler is called as soon as possible, which is after entering the callback function but *before* executing even the ``try:``. If the signal handler raises, we are not in the ``try: except:`` yet. If ``onerror`` is called and returns normally, then it is assumed that it handled the exception on its own and nothing is printed to stderr. If ``onerror`` raises, then both tracebacks are printed. Finally, ``onerror`` can itself provide the result value of the callback in C, but doesn't have to: if it simply returns None---or if ``onerror`` itself fails---then the value of ``error`` will be used, if any. Note the following hack: in ``onerror``, you can access the original callback arguments as follows. First check if ``traceback`` is not None (it is None e.g. if the whole function ran successfully but there was an error converting the value returned: this occurs after the call). If ``traceback`` is not None, then ``traceback.tb_frame`` is the frame of the outermost function, i.e. directly the frame of the function decorated with ``@ffi.def_extern()``. So you can get the value of ``argname`` in that frame by reading ``traceback.tb_frame.f_locals['argname']``. .. _Callbacks: Callbacks (old style) --------------------- Here is how to make a new ```` object that contains a pointer to a function, where that function invokes back a Python function of your choice:: >>> @ffi.callback("int(int, int)") >>> def myfunc(x, y): ... return x + y ... >>> myfunc > Note that ``"int(*)(int, int)"`` is a C *function pointer* type, whereas ``"int(int, int)"`` is a C *function* type. Either can be specified to ffi.callback() and the result is the same. .. warning:: Callbacks are provided for the ABI mode or for backward compatibility. If you are using the out-of-line API mode, it is recommended to use the `extern "Python"`_ mechanism instead of callbacks: it gives faster and cleaner code. It also avoids a SELinux issue whereby the setting of ``deny_execmem`` must be left to ``off`` in order to use callbacks. (A fix in cffi was attempted---see the ``ffi_closure_alloc`` branch---but was not merged because it creates potential memory corruption with ``fork()``. For more information, `see here.`__) .. __: https://bugzilla.redhat.com/show_bug.cgi?id=1249685 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. The easiest way to do that is to always use ``@ffi.callback()`` at module-level only, and to pass "context" information around with `ffi.new_handle()`_, if possible. Example: .. code-block:: python # a good way to use this decorator is once at global level @ffi.callback("int(int, void *)") def my_global_callback(x, handle): return ffi.from_handle(handle).some_method(x) class Foo(object): def __init__(self): handle = ffi.new_handle(self) self._handle = handle # must be kept alive lib.register_stuff_with_callback_and_voidp_arg(my_global_callback, handle) def some_method(self, x): ... (See also the section about `extern "Python"`_ above, where the same general style is used.) 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: .. code-block:: python # file "example_build.py" import cffi ffi = cffi.FFI() ffi.cdef(""" int (*python_callback)(int how_many, int *values); void *const c_callback; /* pass this const ptr to C routines */ """) lib = ffi.set_source("_example", """ #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`` 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. **ffi.cast("C type", value)**: similar to a C cast: returns an instance of the named C type initialized with the given value. The value is casted between integers or pointers of any type. **ffi.error**: the Python exception raised in various cases. (Don't confuse it with ``ffi.errno``.) **ffi.errno**: the value of ``errno`` received from the most recent C call in this thread, and passed to the following C call. (This is a read-write property.) **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()``. (Note that it is also possible to declare and call the ``GetLastError()`` function as usual.) **ffi.string(cdata, [maxlen])**: 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. 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. Here are a few examples of where buffer() would be useful: - use ``file.write()`` and ``file.readinto()`` with such a buffer (for files opened in binary mode) - use ``ffi.buffer(mystruct[0])[:] = socket.recv(len(buffer))`` to read into a struct over a socket, rewriting the contents of mystruct[0] Remember that like in C, you can use ``array + index`` to get the pointer to the index'th item of an array. 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 Python 2's ``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. 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. Python 2/3 compatibility note: you should avoid using ``str(buf)``, because it gives inconsistent results between Python 2 and Python 3. (This is similar to how ``str()`` gives inconsistent results on regular byte strings). Use ``buf[:]`` instead. **ffi.from_buffer(python_buffer)**: return a ```` that points to the data of the given Python object, which must support the buffer interface. This is the opposite of ``ffi.buffer()``. It gives a reference to the existing data, not a copy; for this reason, and for PyPy compatibility, it does not work with the built-in types str or unicode or bytearray (or buffers/memoryviews on them). It is meant to be used on objects containing large quantities of raw data, like ``array.array`` or numpy arrays. It supports both the old buffer API (in Python 2.x) and the new memoryview API. Note that if you pass a read-only buffer object, you still get a regular ````; it is your responsibility not to write there if the original buffer doesn't expect you to. The original object is kept alive (and, in case of memoryview, locked) as long as the cdata object returned by ``ffi.from_buffer()`` is alive. *New in version 0.9.* .. _memmove: **ffi.memmove(dest, src, n)**: copy ``n`` bytes from memory area ``src`` to memory area ``dest``. See examples below. Inspired by the C functions ``memcpy()`` and ``memmove()``---like the latter, the areas can overlap. Each of ``dest`` and ``src`` can be either a cdata pointer or a Python object supporting the buffer/memoryview interface. In the case of ``dest``, the buffer/memoryview must be writable. Unlike ``ffi.from_buffer()``, there are no restrictions on the type of buffer. *New in version 1.3.* Examples: * ``ffi.memmove(myptr, b"hello", 5)`` copies the 5 bytes of ``b"hello"`` to the area that ``myptr`` points to. * ``ba = bytearray(100); ffi.memmove(ba, myptr, 100)`` copies 100 bytes from ``myptr`` into the bytearray ``ba``. * ``ffi.memmove(myptr + 1, myptr, 100)`` shifts 100 bytes from the memory at ``myptr`` to the memory at ``myptr + 1``. **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.: .. code-block:: python def myfunction(ptr): assert ffi.typeof(ptr) is ffi.typeof("foo_t*") ... Note also that the mapping from strings like ``"foo_t*"`` to the ```` objects is stored in some internal dictionary. This guarantees that there is only one ```` object, so you can use the ``is`` operator to compare it. The downside is that the dictionary entries are immortal for now. In the future, we may add transparent reclamation of old, unused entries. In the meantime, note that using strings like ``"int[%d]" % length`` to name a type will create many immortal cached entries if called with many different lengths. **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.NULL**: a constant NULL of type ````. **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 natural alignment size in bytes of the argument. Corresponds to the ``__alignof__`` operator in GCC. **ffi.offsetof("C struct or array type", \*fields_or_indexes)**: return the offset within the struct of the given field. Corresponds to ``offsetof()`` in C. *New in version 0.9:* You can give several field names in case of nested structures. You can also give numeric values which correspond to array items, in case of a pointer or array type. For example, ``ffi.offsetof("int[5]", 2)`` is equal to the size of two integers, as is ``ffi.offsetof("int *", 2)``. **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. 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, allocating and freeing the C data at known points in time, and using it in a ``with`` statement. .. _ffi-new_handle: .. _`ffi.new_handle()`: **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 retrieve 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!* (In case you are wondering, this ``void *`` is not the ``PyObject *`` pointer. This wouldn't make sense on PyPy anyway.) The ``ffi.new_handle()/from_handle()`` functions *conceptually* work like this: * ``new_handle()`` returns cdata objects that contains references to the Python objects; we call them collectively the "handle" cdata objects. The ``void *`` value in these handle cdata objects are random but unique. * ``from_handle(p)`` searches all live "handle" cdata objects for the one that has the same value ``p`` as its ``void *`` value. It then returns the Python object referenced by that handle cdata object. If none is found, you get "undefined behavior" (i.e. crashes). The "handle" 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 handle cdata object *itself* is not alive any more, then the association ``void * -> python_object`` is dead and ``from_handle()`` will crash. *New in version 1.4:* two calls to ``new_handle(x)`` are guaranteed to return cdata objects with different ``void *`` values, even with the same ``x``. This is a useful feature that avoids issues with unexpected duplicates in the following trick: if you need to keep alive the "handle" until explicitly asked to free it, but don't have a natural Python-side place to attach it to, then the easiest is to ``add()`` it to a global set. It can later be removed from the set by ``global_set.discard(p)``, with ``p`` any cdata object whose ``void *`` value compares equal. .. _`ffi.addressof()`: **ffi.addressof(cdata, \*fields_or_indexes)**: limited equivalent to the '&' operator in C: 1. ``ffi.addressof()`` returns a cdata that is a pointer to this struct or union. 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()``. 2. ``ffi.addressof(, field-or-index...)`` returns the address of a field or array item inside the given structure or array. In case of nested structures or arrays, you can give more than one field or index to look recursively. Note that ``ffi.addressof(array, index)`` can also be expressed as ``array + index``: this is true both in CFFI and in C, where ``&array[index]`` is just ``array + index``. 3. ``ffi.addressof(, "name")`` returns the address of the named function or global variable from the given library object. *New in version 1.1:* for functions, it returns a regular cdata object containing a pointer to the function. Note that the case 1. cannot be used to take the address of a primitive or pointer, but only a struct or union. It would be difficult to implement 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, for a pointer, use ``ffi.new("foo_t *[1]")``. **ffi.dlopen(libpath, [flags])**: opens and returns a "handle" to a dynamic library, as a ```` object. See `Preparing and Distributing modules`_. **ffi.dlclose(lib)**: explicitly closes a ```` object returned by ``ffi.dlopen()``. **ffi.RLTD_...**: constants: flags for ``ffi.dlopen()``. .. _`alternative allocators`: **ffi.new_allocator(alloc=None, free=None, should_clear_after_alloc=True)**: returns a new allocator. An "allocator" is a callable that behaves like ``ffi.new()`` but uses the provided low-level ``alloc`` and ``free`` functions. *New in version 1.2.* ``alloc()`` is invoked with the size as sole argument. If it returns NULL, a MemoryError is raised. Later, if ``free`` is not None, it will be called with the result of ``alloc()`` as argument. Both can be either Python function or directly C functions. If only ``free`` is None, then no free function is called. If both ``alloc`` and ``free`` are None, the default alloc/free combination is used. (In other words, the call ``ffi.new(*args)`` is equivalent to ``ffi.new_allocator()(*args)``.) If ``should_clear_after_alloc`` is set to False, then the memory returned by ``alloc()`` is assumed to be already cleared (or you are fine with garbage); otherwise CFFI will clear it. .. _initonce: **ffi.init_once(function, tag)**: run ``function()`` once. The ``tag`` should be a primitive object, like a string, that identifies the function: ``function()`` is only called the first time we see the ``tag``. The return value of ``function()`` is remembered and returned by the current and all future ``init_once()`` with the same tag. If ``init_once()`` is called from multiple threads in parallel, all calls block until the execution of ``function()`` is done. If ``function()`` raises an exception, it is propagated and nothing is cached (i.e. ``function()`` will be called again, in case we catch the exception and try ``init_once()`` again). *New in version 1.4.* Example:: from _xyz_cffi import ffi, lib def initlib(): lib.init_my_library() def make_new_foo(): ffi.init_once(initlib, "init") return lib.make_foo() ``init_once()`` is optimized to run very quickly if ``function()`` has already been called. (On PyPy, the cost is zero---the JIT usually removes everything in the machine code it produces.) *Note:* one motivation__ for ``init_once()`` is the CPython notion of "subinterpreters" in the embedded case. If you are using the out-of-line API mode, ``function()`` is called only once even in the presence of multiple subinterpreters, and its return value is shared among all subinterpreters. The goal is to mimic the way traditional CPython C extension modules have their init code executed only once in total even if there are subinterpreters. In the example above, the C function ``init_my_library()`` is called once in total, not once per subinterpreter. For this reason, avoid Python-level side-effects in ``function()`` (as they will only be applied in the first subinterpreter to run); instead, return a value, as in the following example:: def init_get_max(): return lib.initialize_once_and_get_some_maximum_number() def process(i): if i > ffi.init_once(init_get_max, "max"): raise IndexError("index too large!") ... .. __: https://bitbucket.org/cffi/cffi/issues/233/ .. _`Preparing and Distributing modules`: cdef.html#loading-libraries 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 | +---------------+------------------------+------------------+----------------+ `(*)` ``item *`` is ``item[]`` in function arguments: In a function declaration, as per the C standard, a ``item *`` argument is identical to a ``item[]`` argument (and ``ffi.cdef()`` doesn't record the difference). So when you call such a function, 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 point_s *`` argument might be passed as ``[[x, y]]`` or ``[{'x': 5, 'y': 10}]``. 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. (PyPy might in the future do the same, but it is harder because strings are not naturally zero-terminated in PyPy.) `(**)` C function calls are done with the GIL released. Note that we assume that the called functions are *not* using the Python API from Python.h. For example, we don't check afterwards if they set a Python exception. You may work around it, but mixing CFFI with ``Python.h`` is not recommended. (If you do that, on PyPy and on some platforms like Windows, you may need to explicitly link to ``libpypy-c.dll`` to access the CPython C API compatibility layer; indeed, CFFI-generated modules on PyPy don't link to ``libpypy-c.dll`` on their own. But really, don't do that in the first place.) `(***)` ``long double`` support: We keep ``long double`` values inside a cdata object to avoid loosing precision. Normal Python floating-point numbers only contain enough precision for a ``double``. If you really want to convert such an object to a regular Python float (i.e. a C ``double``), call ``float()``. If you need to do arithmetic on such numbers without any precision loss, you need instead to define and use a family of C functions like ``long double add(long double a, long double b);``. `(****)` Slicing with ``x[start:stop]``: Slicing is allowed, as long as you specify explicitly both ``start`` and ``stop`` (and don't give any ``step``). It gives a 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). 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. you can say ``chararray[10:15] = "hello"``, but the assigned string must be of exactly the correct length; no implicit null character is added.) `(*****)` Enums are handled like ints: Like C, enum types are mostly int types (unsigned or signed, int or long; note that GCC's first choice is unsigned). Reading an enum field of a structure, for example, returns you an integer. 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))``. cffi-1.5.2/doc/source/index.rst0000664000175000017500000000574412657646311016607 0ustar arigoarigo00000000000000================================ CFFI documentation ================================ C Foreign Function Interface for Python. The goal is to provide a convenient and reliable way to call compiled C code from Python using interface declarations written in C. * Goals_ * `Comments and bugs`_ .. toctree:: :maxdepth: 2 whatsnew installation overview using cdef embedding Goals ----- The interface is based on `LuaJIT's FFI`_, and follows a few principles: * The goal is to call C code from Python without learning a 3rd language: existing alternatives require users to learn domain specific language (Cython_, SWIG_) or API (ctypes_). The CFFI design requires users to know only C and Python, minimizing the extra bits of API that need to be learned. * Keep all the Python-related logic in Python so that you don't need to write much C code (unlike `CPython native C extensions`_). * The preferred way is to work at the level of the API (Application Programming Interface): the C compiler is called from the declarations you write to validate and link to the C language constructs. Alternatively, it is also possible to work at the ABI level (Application Binary Interface), the way ctypes_ work. However, on non-Windows platforms, C libraries typically have a specified C API but not an ABI (e.g. they may document a "struct" as having at least these fields, but maybe more). * 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). * 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://wiki.scipy.org/Weave .. _`manually wrap`: overview.html#abi-versus-api Get started by reading `the overview`__. .. __: overview.html 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 cffi-1.5.2/doc/source/overview.rst0000664000175000017500000003474012657646311017344 0ustar arigoarigo00000000000000======================================================= Overview ======================================================= .. contents:: CFFI can be used in one of four modes: "ABI" versus "API" level, each with "in-line" or "out-of-line" preparation (or compilation). The **ABI mode** accesses libraries at the binary level, whereas the **API mode** accesses them with a C compiler. This is described in detail below__. .. __: `abi-versus-api`_ In the **in-line mode,** everything is set up every time you import your Python code. In the **out-of-line mode,** you have a separate step of preparation (and possibly C compilation) that produces a module which your main program can then import. (The examples below assume that you have `installed CFFI`__.) .. __: installation.html Simple example (ABI level, in-line) ----------------------------------- .. 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. 17 # this is the return value >>> 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)``. *This example does not call any C compiler.* .. _out-of-line-abi-level: Out-of-line example (ABI level, out-of-line) -------------------------------------------- In a real program, you would not include the ``ffi.cdef()`` in your main program's modules. Instead, you can rewrite it as follows. It massively reduces the import times, because it is slow to parse a large C header. It also allows you to do more detailed checkings during build-time without worrying about performance (e.g. calling ``cdef()`` many times with small pieces of declarations, based on the version of libraries detected on the system). *This example does not call any C compiler.* .. code-block:: python # file "simple_example_build.py" # Note: this particular example fails before version 1.0.2 # because it combines variadic function and ABI level. from cffi import FFI ffi = FFI() ffi.set_source("_simple_example", None) ffi.cdef(""" int printf(const char *format, ...); """) if __name__ == "__main__": ffi.compile() Running it once produces ``_simple_example.py``. Your main program only imports this generated module, not ``simple_example_build.py`` any more: .. code-block:: python from _simple_example import ffi lib = ffi.dlopen(None) # Unix: open the standard C library #import ctypes.util # or, try this on Windows: #lib = ffi.dlopen(ctypes.util.find_library("c")) lib.printf(b"hi there, number %d\n", ffi.cast("int", 2)) Note that this ``ffi.dlopen()``, unlike the one from in-line mode, does not invoke any additional magic to locate the library: it must be a path name (with or without a directory), as required by the C ``dlopen()`` or ``LoadLibrary()`` functions. This means that ``ffi.dlopen("libfoo.so")`` is ok, but ``ffi.dlopen("foo")`` is not. In the latter case, you could replace it with ``ffi.dlopen(ctypes.util.find_library("foo"))``. Also, None is only recognized on Unix to open the standard C library. For distribution purposes, remember that there is a new ``_simple_example.py`` file generated. You can either include it statically within your project's source files, or, with Setuptools, you can say in the ``setup.py``: .. code-block:: python from setuptools import setup setup( ... setup_requires=["cffi>=1.0.0"], cffi_modules=["simple_example_build.py:ffi"], install_requires=["cffi>=1.0.0"], ) .. _out-of-line-api-level: .. _real-example: Real example (API level, out-of-line) ------------------------------------- .. code-block:: python # file "example_build.py" from cffi import FFI ffi = FFI() ffi.set_source("_example", """ // passed to the real C compiler #include #include """, libraries=[]) # or a list of libraries to link with # (more arguments like setup.py's Extension class: # include_dirs=[..], extra_objects=[..], and so on) ffi.cdef(""" // some declarations from the man page struct passwd { char *pw_name; ...; // literally dot-dot-dot }; struct passwd *getpwuid(int uid); """) if __name__ == "__main__": ffi.compile() You need to run the ``example_build.py`` script once to generate "source code" into the file ``_example.c`` and compile this to a regular C extension module. (CFFI selects either Python or C for the module to generate based on whether the second argument to ``set_source()`` is ``None`` or not.) *You need a C compiler for this single step. It produces a file called e.g. _example.so or _example.pyd. If needed, it can be distributed in precompiled form like any other extension module.* Then, in your main program, you use: .. code-block:: python from _example import ffi, lib p = lib.getpwuid(0) assert ffi.string(p.pw_name) == b'root' Note that this works independently of the exact C layout of ``struct passwd`` (it is "API level", as opposed to "ABI level"). It requires a C compiler in order to run ``example_build.py``, but it is much more portable than trying to get the details of the fields of ``struct passwd`` exactly right. Similarly, we declared ``getpwuid()`` as taking an ``int`` argument. On some platforms this might be slightly incorrect---but it does not matter. To integrate it inside a ``setup.py`` distribution with Setuptools: .. code-block:: python from setuptools import setup setup( ... setup_requires=["cffi>=1.0.0"], cffi_modules=["example_build.py:ffi"], install_requires=["cffi>=1.0.0"], ) Struct/Array Example (minimal, in-line) --------------------------------------- .. 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 *This example does not call any C compiler.* This example also admits an out-of-line equivalent. It is similar to `Out-of-line example (ABI level, out-of-line)`_ above, but without any call to ``ffi.dlopen()``. In the main program, you write ``from _simple_example import ffi`` and then the same content as the in-line example above starting from the line ``image = ffi.new("pixel_t[]", 800*600)``. .. _performance: Purely for performance (API level, out-of-line) ----------------------------------------------- A variant of the `section above`__ where the goal is not to call an existing C library, but to compile and call some C function written directly in the build script: .. __: real-example_ .. code-block:: python # file "example_build.py" from cffi import FFI ffi = FFI() ffi.cdef("int foo(int *, int *, int);") ffi.set_source("_example", """ static int foo(int *buffer_in, int *buffer_out, int x) { /* some algorithm that is seriously faster in C than in Python */ } """) if __name__ == "__main__": ffi.compile() .. code-block:: python # file "example.py" from _example import ffi, lib buffer_in = ffi.new("int[]", 1000) # initialize buffer_in here... # easier to do all buffer allocations in Python and pass them to C, # even for output-only arguments buffer_out = ffi.new("int[]", 1000) result = lib.foo(buffer_in, buffer_out, 1000) *You need a C compiler to run example_build.py, once. It produces a file called e.g. _example.so or _example.pyd. If needed, it can be distributed in precompiled form like any other extension module.* .. _embedding: Embedding --------- *New in version 1.5.* CFFI can be used for embedding__: creating a standard dynamically-linked library (``.dll`` under Windows, ``.so`` elsewhere) which can be used from a C application. .. code-block:: python import cffi ffi = cffi.FFI() ffi.embedding_api(""" int do_stuff(int, int); """) ffi.set_source("my_plugin", "") ffi.embedding_init_code(""" from my_plugin import ffi @ffi.def_extern() def do_stuff(x, y): print("adding %d and %d" % (x, y)) return x + y """) ffi.compile(target="plugin-1.5.*", verbose=True) This simple example creates ``plugin-1.5.dll`` or ``plugin-1.5.so`` as a DLL with a single exported function, ``do_stuff()``. You execute the script above once, with the interpreter you want to have internally used; it can be CPython 2.x or 3.x or PyPy. This DLL can then be used "as usual" from an application; the application doesn't need to know that it is talking with a library made with Python and CFFI. At runtime, when the application calls ``int do_stuff(int, int)``, the Python interpreter is automatically initialized and ``def do_stuff(x, y):`` gets called. `See the details in the documentation about embedding.`__ .. __: embedding.html .. __: embedding.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, constants** and **global variables.** What you pass to the ``cdef()`` must not contain more than that; in particular, ``#ifdef`` or ``#include`` directives are not supported. 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". In the ABI examples, the ``dlopen()`` calls load libraries manually. At the binary level, a program is split into multiple namespaces---a global one (on some platforms), plus one namespace per library. So ``dlopen()`` returns a ```` object, and this object has got as attributes all function, constant and variable symbols that are coming from this library and that have been declared in the ``cdef()``. If you have several interdependent libraries to load, you would call ``cdef()`` only once but ``dlopen()`` several times. By opposition, the API mode works more closely like a C program: the C linker (static or dynamic) is responsible for finding any symbol used. You name the libraries in the ``libraries`` keyword argument to ``set_source()``, but never need to say which symbol comes from which library. Other common arguments to ``set_source()`` include ``library_dirs`` and ``include_dirs``; all these arguments are passed to the standard distutils/setuptools. 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 ``lib.*()`` function calls should be obvious: it's like C. .. _abi-versus-api: ABI versus API -------------- Accessing the C library at the binary level ("ABI") is fraught with problems, particularly on non-Windows platforms. You are not meant to access fields by guessing where they are in the structures. *The C libraries are typically meant to be used with a C compiler.* The "real example" above shows how to do that: this example uses ``set_source(..., "C source...")`` and never ``dlopen()``. When using this approach, we have the advantage that we can use literally "``...``" at various places in the ``cdef()``, and the missing information will be completed with the help of the C compiler. Actually, a single C source file is produced, which contains first the "C source" part unmodified, followed by some "magic" C code and declarations derived from the ``cdef()``. When this C file is compiled, the resulting C extension module will contain all the information we need---or the C compiler will give warnings or errors, as usual e.g. if we misdeclare some function's signature. Note that the "C source" part from ``set_source()`` can contain arbitrary C code. You can use this to declare some more helper functions written in C. To export these helpers to Python, put their signature in the ``cdef()`` too. (You can use the ``static`` C keyword in the "C source" part, as in ``static int myhelper(int x) { return x * 42; }``, because these helpers are only referenced from the "magic" C code that is generated afterwards in the same C file.) This can be used for example to wrap "crazy" macros into more standard C functions. The extra layer of C can be useful for other reasons too, like calling functions that expect some complicated argument structures that you prefer to build in C rather than in Python. (On the other hand, if all you need is to call "function-like" macros, then you can directly declare them in the ``cdef()`` as if they were functions.) The generated piece of C code should be the same independently on the platform on which you run it (or the Python version), so in simple cases you can directly distribute the pre-generated C code and treat it as a regular C extension module. The special Setuptools lines in the `example above`__ are meant for the more complicated cases where we need to regenerate the C sources as well---e.g. because the Python script that regenerates this file will itself look around the system to know what it should include or not. .. __: real-example_ Note that the "API level + in-line" mode combination is deprecated. It used to be done with ``lib = ffi.verify("C header")``. The out-of-line variant with ``set_source("modname", "C header")`` is preferred. cffi-1.5.2/doc/source/installation.rst0000664000175000017500000001265112657646311020174 0ustar arigoarigo00000000000000======================================================= Installation and Status ======================================================= Quick installation for CPython (cffi is distributed with PyPy): * ``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 Windows 32 and 64. (It relies occasionally on libffi, so it depends on libffi being bug-free; this may not be fully the case on some of the more exotic platforms.) CFFI supports CPython 2.6, 2.7, 3.x (tested with 3.2 to 3.4); and is distributed with PyPy (CFFI 1.0 is distributed with and requires PyPy 2.6). The core speed of CFFI is better than ctypes, with import times being either lower if you use the post-1.0 features, or much higher if you don't. The wrapper Python code you typically need to write around the raw CFFI interface slows things down on CPython, but not unreasonably so. On PyPy, this wrapper code has a minimal impact thanks to the JIT compiler. This makes CFFI the recommended way to interface with C libraries on PyPy. Requirements: * CPython 2.6 or 2.7 or 3.x, or PyPy (PyPy 2.0 for the earliest versions of CFFI; or PyPy 2.6 for CFFI 1.0). * in some cases you need to be able to compile C extension modules; refer to the appropriate docs for your OS. This includes installing CFFI from sources; or developing code based on ``ffi.set_source()`` or ``ffi.verify()``; or installing such 3rd-party modules from sources. * on CPython, on non-Windows platforms, you also need to install ``libffi-dev`` in order to compile CFFI itself. * pycparser >= 2.06: https://github.com/eliben/pycparser (automatically tracked by ``pip install cffi``). * `py.test`_ is needed to run the tests of CFFI itself. .. _`py.test`: http://pypi.python.org/pypi/pytest Download and Installation: * http://pypi.python.org/packages/source/c/cffi/cffi-1.5.2.tar.gz - MD5: ... - SHA: ... * Or grab the most current version 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`_.) * running the tests: ``py.test c/ testing/`` (if you didn't install cffi yet, you need first ``python setup_base.py build_ext -f -i``) .. _`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 might be sketchy on details; for now the ultimate reference is given by the tests, notably `testing/cffi1/test_verify1.py`_ and `testing/cffi0/backend_tests.py`_. .. _`demo`: https://bitbucket.org/cffi/cffi/src/default/demo .. _`testing/cffi1/test_verify1.py`: https://bitbucket.org/cffi/cffi/src/default/testing/cffi1/test_verify1.py .. _`testing/cffi0/backend_tests.py`: https://bitbucket.org/cffi/cffi/src/default/testing/cffi0/backend_tests.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 PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig 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 (regular 32-bit) ++++++++++++++++++++++++ Win32 works and is tested at least each official release. The recommended C compiler compatible with Python 2.7 is this one: http://www.microsoft.com/en-us/download/details.aspx?id=44266 There is a known problem with distutils on Python 2.7, as explained in https://bugs.python.org/issue23246, and the same problem applies whenever you want to run compile() to build a dll with this specific compiler suite download. ``import setuptools`` might help, but YMMV For Python 3.4 and beyond: https://www.visualstudio.com/en-us/downloads/visual-studio-2015-ctp-vs Windows 64 ++++++++++ Win64 received very basic testing and we applied a few essential fixes in cffi 0.7. The comment above applies for Python 2.7 on Windows 64 as well. 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 cffi-1.5.2/doc/source/conf.py0000664000175000017500000001427512657646311016244 0ustar arigoarigo00000000000000# -*- 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-2015, 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 = '1.5' # The full version, including alpha/beta/rc tags. release = '1.5.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-1.5.2/doc/source/embedding.rst0000664000175000017500000003434612657646311017416 0ustar arigoarigo00000000000000================================ Using CFFI for embedding ================================ .. contents:: You can use CFFI to generate a ``.so/.dll/.dylib`` which exports the API of your choice to any C application that wants to link with this ``.so/.dll/.dylib``. The general idea is as follows: * You write and execute a Python script, which produces a ``.so/.dll/.dylib`` file with the API of your choice. The script also gives some Python code to be "frozen" inside the ``.so``. * At runtime, the C application loads this ``.so/.dll/.dylib`` without having to know that it was produced by Python and CFFI. * The first time a C function is called, Python is initialized and the frozen Python code is executed. * The frozen Python code attaches Python functions that implement the C functions of your API, which are then used for all subsequent C function calls. One of the goals of this approach is to be entirely independent from the CPython C API: no ``Py_Initialize()`` nor ``PyRun_SimpleString()`` nor even ``PyObject``. It works identically on CPython and PyPy. .. note:: PyPy release 4.0.1 contains CFFI 1.4 only. This is entirely *new in version 1.5.* Usage ----- .. __: overview.html#embedding See the `paragraph in the overview page`__ for a quick introduction. In this section, we explain every step in more details. We will use here this slightly expanded example: .. code-block:: c /* file plugin.h */ typedef struct { int x, y; } point_t; extern int do_stuff(point_t *); .. code-block:: python # file plugin_build.py import cffi ffi = cffi.FFI() with open('plugin.h') as f: ffi.embedding_api(f.read()) ffi.set_source("my_plugin", ''' #include "plugin.h" ''') ffi.embedding_init_code(""" from my_plugin import ffi @ffi.def_extern() def do_stuff(p): print("adding %d and %d" % (p.x, p.y)) return p.x + p.y """) ffi.compile(target="plugin-1.5.*", verbose=True) Running the code above produces a *DLL*, i,e, a dynamically-loadable library. It is a file with the extension ``.dll`` on Windows, ``.dylib`` on Mac OS/X, or ``.so`` on other platforms. As usual, it is produced by generating some intermediate ``.c`` code and then calling the regular platform-specific C compiler. Here are some details about the methods used above: * **ffi.embedding_api(source):** parses the given C source, which declares functions that you want to be exported by the DLL. It can also declare types, constants and global variables that are part of the C-level API of your DLL. The functions that are found in ``source`` will be automatically defined in the ``.c`` file: they will contain code that initializes the Python interpreter the first time any of them is called, followed by code to call the attached Python function (with ``@ffi.def_extern()``, see next point). The global variables, on the other hand, are not automatically produced. You have to write their definition explicitly in ``ffi.set_source()``, as regular C code (see the point after next). * **ffi.embedding_init_code(python_code):** this gives initialization-time Python source code. This code is copied ("frozen") inside the DLL. At runtime, the code is executed when the DLL is first initialized, just after Python itself is initialized. This newly initialized Python interpreter has got an extra "built-in" module that can be loaded magically without accessing any files, with a line like "``from my_plugin import ffi, lib``". The name ``my_plugin`` comes from the first argument to ``ffi.set_source()``. This module represents "the caller's C world" from the point of view of Python. The initialization-time Python code can import other modules or packages as usual. You may have typical Python issues like needing to set up ``sys.path`` somehow manually first. For every function declared within ``ffi.embedding_api()``, the initialization-time Python code or one of the modules it imports should use the decorator ``@ffi.def_extern()`` to attach a corresponding Python function to it. If the initialization-time Python code fails with an exception, then you get a traceback printed to stderr, along with more information to help you identify problems like wrong ``sys.path``. If some function remains unattached at the time where the C code tries to call it, an error message is also printed to stderr and the function returns zero/null. Note that the CFFI module never calls ``exit()``, but CPython itself contains code that calls ``exit()``, for example if importing ``site`` fails. This may be worked around in the future. * **ffi.set_source(c_module_name, c_code):** set the name of the module from Python's point of view. It also gives more C code which will be included in the generated C code. In trivial examples it can be an empty string. It is where you would ``#include`` some other files, define global variables, and so on. The macro ``CFFI_DLLEXPORT`` is available to this C code: it expands to the platform-specific way of saying "the following declaration should be exported from the DLL". For example, you would put "``extern int my_glob;``" in ``ffi.embedding_api()`` and "``CFFI_DLLEXPORT int my_glob = 42;``" in ``ffi.set_source()``. Currently, any *type* declared in ``ffi.embedding_api()`` must also be present in the ``c_code``. This is automatic if this code contains a line like ``#include "plugin.h"`` in the example above. * **ffi.compile([target=...] [, verbose=True]):** make the C code and compile it. By default, it produces a file called ``c_module_name.dll``, ``c_module_name.dylib`` or ``c_module_name.so``, but the default can be changed with the optional ``target`` keyword argument. You can use ``target="foo.*"`` with a literal ``*`` to ask for a file called ``foo.dll`` on Windows, ``foo.dylib`` on OS/X and ``foo.so`` elsewhere. One reason for specifying an alternate ``target`` is to include characters not usually allowed in Python module names, like "``plugin-1.5.*``". For more complicated cases, you can call instead ``ffi.emit_c_code("foo.c")`` and compile the resulting ``foo.c`` file using other means. CFFI's compilation logic is based on the standard library ``distutils`` package, which is really developed and tested for the purpose of making CPython extension modules, not other DLLs. More reading ------------ If you're reading this page about embedding and you are not familiar with CFFI already, here are a few pointers to what you could read next: * For the ``@ffi.def_extern()`` functions, integer C types are passed simply as Python integers; and simple pointers-to-struct and basic arrays are all straightforward enough. However, sooner or later you will need to read about this topic in more details here__. * ``@ffi.def_extern()``: see `documentation here,`__ notably on what happens if the Python function raises an exception. * To create Python objects attached to C data, one common solution is to use ``ffi.new_handle()``. See documentation here__. * In embedding mode, the major direction is C code that calls Python functions. This is the opposite of the regular extending mode of CFFI, in which the major direction is Python code calling C. That's why the page `Using the ffi/lib objects`_ talks first about the latter, and why the direction "C code that calls Python" is generally referred to as "callbacks" in that page. If you also need to have your Python code call C code, read more about `Embedding and Extending`_ below. * ``ffi.embedding_api(source)``: follows the same syntax as ``ffi.cdef()``, `documented here.`__ You can use the "``...``" syntax as well, although in practice it may be less useful than it is for ``cdef()``. On the other hand, it is expected that often the C sources that you need to give to ``ffi.embedding_api()`` would be exactly the same as the content of some ``.h`` file that you want to give to users of your DLL. That's why the example above does this:: with open('foo.h') as f: ffi.embedding_api(f.read()) Note that a drawback of this approach is that ``ffi.embedding_api()`` doesn't support ``#ifdef`` directives. You may have to use a more convoluted expression like:: with open('foo.h') as f: lines = [line for line in f if not line.startswith('#')] ffi.embedding_api(''.join(lines)) As in the example above, you can also use the same ``foo.h`` from ``ffi.set_source()``:: ffi.set_source('module_name', '#include "foo.h"') .. __: using.html#working .. __: using.html#def-extern .. __: using.html#ffi-new_handle .. __: cdef.html#cdef .. _`Using the ffi/lib objects`: using.html Troubleshooting --------------- The error message cffi extension module 'c_module_name' has unknown version 0x2701 means that the running Python interpreter located a CFFI version older than 1.5. CFFI 1.5 or newer must be installed in the running Python. Using multiple CFFI-made DLLs ----------------------------- Multiple CFFI-made DLLs can be used by the same process. Note that all CFFI-made DLLs in a process share a single Python interpreter. The effect is the same as the one you get by trying to build a large Python application by assembling a lot of unrelated packages. Some of these might be libraries that monkey-patch some functions from the standard library, for example, which might be unexpected from other parts. Multithreading -------------- Multithreading should work transparently, based on Python's standard Global Interpreter Lock. If two threads both try to call a C function when Python is not yet initialized, then locking occurs. One thread proceeds with initialization and blocks the other thread. The other thread will be allowed to continue only when the execution of the initialization-time Python code is done. If the two threads call two *different* CFFI-made DLLs, the Python initialization itself will still be serialized, but the two pieces of initialization-time Python code will not. The idea is that there is a priori no reason for one DLL to wait for initialization of the other DLL to be complete. After initialization, Python's standard Global Interpreter Lock kicks in. The end result is that when one CPU progresses on executing Python code, no other CPU can progress on executing more Python code from another thread of the same process. At regular intervals, the lock switches to a different thread, so that no single thread should appear to block indefinitely. Testing ------- For testing purposes, a CFFI-made DLL can be imported in a running Python interpreter instead of being loaded like a C shared library. You might have some issues with the file name: for example, on Windows, Python expects the file to be called ``c_module_name.pyd``, but the CFFI-made DLL is called ``target.dll`` instead. The base name ``target`` is the one specified in ``ffi.compile()``, and on Windows the extension is ``.dll`` instead of ``.pyd``. You have to rename or copy the file, or on POSIX use a symlink. The module then works like a regular CFFI extension module. It is imported with "``from c_module_name import ffi, lib``" and exposes on the ``lib`` object all C functions. You can test it by calling these C functions. The initialization-time Python code frozen inside the DLL is executed the first time such a call is done. Embedding and Extending ----------------------- The embedding mode is not incompatible with the non-embedding mode of CFFI. You can use *both* ``ffi.embedding_api()`` and ``ffi.cdef()`` in the same build script. You put in the former the declarations you want to be exported by the DLL; you put in the latter only the C functions and types that you want to share between C and Python, but not export from the DLL. As an example of that, consider the case where you would like to have a DLL-exported C function written in C directly, maybe to handle some cases before calling Python functions. To do that, you must *not* put the function's signature in ``ffi.embedding_api()``. (Note that this requires more hacks if you use ``ffi.embedding_api(f.read())``.) You must only write the custom function definition in ``ffi.set_source()``, and prefix it with the macro CFFI_DLLEXPORT: .. code-block:: c CFFI_DLLEXPORT int myfunc(int a, int b) { /* implementation here */ } This function can, if it wants, invoke Python functions using the general mechanism of "callbacks"---called this way because it is a call from C to Python, although in this case it is not calling anything back: .. code-block:: python ffi.cdef(""" extern "Python" int mycb(int); """) ffi.set_source("my_plugin", """ static int mycb(int); /* the callback: forward declaration, to make it accessible from the C code that follows */ CFFI_DLLEXPORT int myfunc(int a, int b) { int product = a * b; /* some custom C code */ return mycb(product); } """) and then the Python initialization code needs to contain the lines: .. code-block:: python @ffi.def_extern() def mycb(x): print "hi, I'm called with x =", x return x * 10 This ``@ffi.def_extern`` is attaching a Python function to the C callback ``mycb()``, which in this case is not exported from the DLL. Nevertheless, the automatic initialization of Python occurs when ``mycb()`` is called, if it happens to be the first function called from C. More precisely, it does not happen when ``myfunc()`` is called: this is just a C function, with no extra code magically inserted around it. It only happens when ``myfunc()`` calls ``mycb()``. As the above explanation hints, this is how ``ffi.embedding_api()`` actually implements function calls that directly invoke Python code; here, we have merely decomposed it explicitly, in order to add some custom C code in the middle. In case you need to force, from C code, Python to be initialized before the first ``@ffi.def_extern()`` is called, you can do so by calling the C function ``cffi_start_python()`` with no argument. It returns an integer, 0 or -1, to tell if the initialization succeeded or not. Currently there is no way to prevent a failing initialization from also dumping a traceback and more information to stderr. cffi-1.5.2/doc/source/whatsnew.rst0000664000175000017500000002427212657646311017335 0ustar arigoarigo00000000000000====================== What's New ====================== v1.5.2 ====== * Fix 1.5.1 for Python 2.6. v1.5.1 ====== * A few installation-time tweaks (thanks Stefano!) * Issue #245: Win32: ``__stdcall`` was never generated for ``extern "Python"`` functions * Issue #246: trying to be more robust against CPython's fragile interpreter shutdown logic v1.5.0 ====== * Support for `using CFFI for embedding`__. .. __: embedding.html v1.4.2 ====== Nothing changed from v1.4.1. v1.4.1 ====== * Fix the compilation failure of cffi on CPython 3.5.0. (3.5.1 works; some detail changed that makes some underscore-starting macros disappear from view of extension modules, and I worked around it, thinking it changed in all 3.5 versions---but no: it was only in 3.5.1.) v1.4.0 ====== * A `better way to do callbacks`__ has been added (faster and more portable, and usually cleaner). It is a mechanism for the out-of-line API mode that replaces the dynamic creation of callback objects (i.e. C functions that invoke Python) with the static declaration in ``cdef()`` of which callbacks are needed. This is more C-like, in that you have to structure your code around the idea that you get a fixed number of function pointers, instead of creating them on-the-fly. * ``ffi.compile()`` now takes an optional ``verbose`` argument. When ``True``, distutils prints the calls to the compiler. * ``ffi.compile()`` used to fail if given ``sources`` with a path that includes ``".."``. Fixed. * ``ffi.init_once()`` added. See docs__. * ``dir(lib)`` now works on libs returned by ``ffi.dlopen()`` too. * Cleaned up and modernized the content of the ``demo`` subdirectory in the sources (thanks matti!). * ``ffi.new_handle()`` is now guaranteed to return unique ``void *`` values, even if called twice on the same object. Previously, in that case, CPython would return two ``cdata`` objects with the same ``void *`` value. This change is useful to add and remove handles from a global dict (or set) without worrying about duplicates. It already used to work like that on PyPy. *This change can break code that used to work on CPython by relying on the object to be kept alive by other means than keeping the result of ffi.new_handle() alive.* (The corresponding `warning in the docs`__ of ``ffi.new_handle()`` has been here since v0.8!) .. __: using.html#extern-python .. __: using.html#initonce .. __: using.html#ffi-new-handle v1.3.1 ====== * The optional typedefs (``bool``, ``FILE`` and all Windows types) were not always available from out-of-line FFI objects. * Opaque enums are phased out from the cdefs: they now give a warning, instead of (possibly wrongly) being assumed equal to ``unsigned int``. Please report if you get a reasonable use case for them. * Some parsing details, notably ``volatile`` is passed along like ``const`` and ``restrict``. Also, older versions of pycparser mis-parse some pointer-to-pointer types like ``char * const *``: the "const" ends up at the wrong place. Added a workaround. v1.3.0 ====== * Added `ffi.memmove()`_. * Pull request #64: out-of-line API mode: we can now declare floating-point types with ``typedef float... foo_t;``. This only works if ``foo_t`` is a float or a double, not ``long double``. * Issue #217: fix possible unaligned pointer manipulation, which crashes on some architectures (64-bit, non-x86). * Issues #64 and #126: when using ``set_source()`` or ``verify()``, the ``const`` and ``restrict`` keywords are copied from the cdef to the generated C code; this fixes warnings by the C compiler. It also fixes corner cases like ``typedef const int T; T a;`` which would previously not consider ``a`` as a constant. (The cdata objects themselves are never ``const``.) * Win32: support for ``__stdcall``. For callbacks and function pointers; regular C functions still don't need to have their `calling convention`_ declared. * Windows: CPython 2.7 distutils doesn't work with Microsoft's official Visual Studio for Python, and I'm told this is `not a bug`__. For ffi.compile(), we `removed a workaround`__ that was inside cffi but which had unwanted side-effects. Try saying ``import setuptools`` first, which patches distutils... .. _`ffi.memmove()`: using.html#memmove .. __: https://bugs.python.org/issue23246 .. __: https://bitbucket.org/cffi/cffi/pull-requests/65/remove-_hack_at_distutils-which-imports/diff .. _`calling convention`: using.html#windows-calling-conventions v1.2.1 ====== Nothing changed from v1.2.0. v1.2.0 ====== * Out-of-line mode: ``int a[][...];`` can be used to declare a structure field or global variable which is, simultaneously, of total length unknown to the C compiler (the ``a[]`` part) and each element is itself an array of N integers, where the value of N *is* known to the C compiler (the ``int`` and ``[...]`` parts around it). Similarly, ``int a[5][...];`` is supported (but probably less useful: remember that in C it means ``int (a[5])[...];``). * PyPy: the ``lib.some_function`` objects were missing the attributes ``__name__``, ``__module__`` and ``__doc__`` that are expected e.g. by some decorators-management functions from ``functools``. * Out-of-line API mode: you can now do ``from _example.lib import x`` to import the name ``x`` from ``_example.lib``, even though the ``lib`` object is not a standard module object. (Also works in ``from _example.lib import *``, but this is even more of a hack and will fail if ``lib`` happens to declare a name called ``__all__``. Note that ``*`` excludes the global variables; only the functions and constants make sense to import like this.) * ``lib.__dict__`` works again and gives you a copy of the dict---assuming that ``lib`` has got no symbol called precisely ``__dict__``. (In general, it is safer to use ``dir(lib)``.) * Out-of-line API mode: global variables are now fetched on demand at every access. It fixes issue #212 (Windows DLL variables), and also allows variables that are defined as dynamic macros (like ``errno``) or ``__thread`` -local variables. (This change might also tighten the C compiler's check on the variables' type.) * Issue #209: dereferencing NULL pointers now raises RuntimeError instead of segfaulting. Meant as a debugging aid. The check is only for NULL: if you dereference random or dead pointers you might still get segfaults. * Issue #152: callbacks__: added an argument ``ffi.callback(..., onerror=...)``. If the main callback function raises an exception and ``onerror`` is provided, then ``onerror(exception, exc_value, traceback)`` is called. This is similar to writing a ``try: except:`` in the main callback function, but in some cases (e.g. a signal) an exception can occur at the very start of the callback function---before it had time to enter the ``try: except:`` block. * Issue #115: added ``ffi.new_allocator()``, which officializes support for `alternative allocators`__. .. __: using.html#callbacks .. __: using.html#alternative-allocators v1.1.2 ====== * ``ffi.gc()``: fixed a race condition in multithreaded programs introduced in 1.1.1 v1.1.1 ====== * Out-of-line mode: ``ffi.string()``, ``ffi.buffer()`` and ``ffi.getwinerror()`` didn't accept their arguments as keyword arguments, unlike their in-line mode equivalent. (It worked in PyPy.) * Out-of-line ABI mode: documented a restriction__ of ``ffi.dlopen()`` when compared to the in-line mode. * ``ffi.gc()``: when called several times with equal pointers, it was accidentally registering only the last destructor, or even none at all depending on details. (It was correctly registering all of them only in PyPy, and only with the out-of-line FFIs.) .. __: cdef.html#dlopen-note v1.1.0 ====== * Out-of-line API mode: we can now declare integer types with ``typedef int... foo_t;``. The exact size and signedness of ``foo_t`` is figured out by the compiler. * Out-of-line API mode: we can now declare multidimensional arrays (as fields or as globals) with ``int n[...][...]``. Before, only the outermost dimension would support the ``...`` syntax. * Out-of-line ABI mode: we now support any constant declaration, instead of only integers whose value is given in the cdef. Such "new" constants, i.e. either non-integers or without a value given in the cdef, must correspond to actual symbols in the lib. At runtime they are looked up the first time we access them. This is useful if the library defines ``extern const sometype somename;``. * ``ffi.addressof(lib, "func_name")`` now returns a regular cdata object of type "pointer to function". You can use it on any function from a library in API mode (in ABI mode, all functions are already regular cdata objects). To support this, you need to recompile your cffi modules. * Issue #198: in API mode, if you declare constants of a ``struct`` type, what you saw from lib.CONSTANT was corrupted. * Issue #196: ``ffi.set_source("package._ffi", None)`` would incorrectly generate the Python source to ``package._ffi.py`` instead of ``package/_ffi.py``. Also fixed: in some cases, if the C file was in ``build/foo.c``, the .o file would be put in ``build/build/foo.o``. v1.0.3 ====== * Same as 1.0.2, apart from doc and test fixes on some platforms. v1.0.2 ====== * Variadic C functions (ending in a "..." argument) were not supported in the out-of-line ABI mode. This was a bug---there was even a (non-working) example__ doing exactly that! .. __: overview.html#out-of-line-abi-level v1.0.1 ====== * ``ffi.set_source()`` crashed if passed a ``sources=[..]`` argument. Fixed by chrippa on pull request #60. * Issue #193: if we use a struct between the first cdef() where it is declared and another cdef() where its fields are defined, then this definition was ignored. * Enums were buggy if you used too many "..." in their definition. v1.0.0 ====== * The main news item is out-of-line module generation: * `for ABI level`_, with ``ffi.dlopen()`` * `for API level`_, which used to be with ``ffi.verify()``, now deprecated * (this page will list what is new from all versions from 1.0.0 forward.) .. _`for ABI level`: overview.html#out-of-line-abi-level .. _`for API level`: overview.html#out-of-line-api-level cffi-1.5.2/doc/Makefile0000664000175000017500000000606612657646311015104 0ustar arigoarigo00000000000000# 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-1.5.2/doc/misc/0000775000175000017500000000000012657646372014376 5ustar arigoarigo00000000000000cffi-1.5.2/doc/misc/design.rst0000664000175000017500000000533312657646311016376 0ustar arigoarigo00000000000000================ 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-1.5.2/doc/misc/grant-cffi-1.0.rst0000664000175000017500000001250512657646311017440 0ustar arigoarigo00000000000000 =========================== Grant Proposal for CFFI 1.0 =========================== *Accepted by the PSF board on April 4, 2015* This Grant Proposal is to give a boost towards "CFFI 1.0". Two main issues with the current CFFI need to be solved: the difficulties of installation, and the potentially large time taken at import. 1. The difficulties of installation can be seen from outside by looking at various workarounds and 3rd-party documentation that have grown into existence. For example, the `setup.py` of projects like cryptography, PyNaCl and bcrypt deploys workarounds that are explicitly documented in https://caremad.io/2014/11/distributing-a-cffi-project/. 2. The time taken at import is excessive in some cases. For example, importing `pygame-cffi` on a Raspberry Pi ARM board takes on the order of 10 to 20 seconds (and this is the "fast" case where the compiler doesn't need to be invoked any more). Technical Overview ------------------ "CFFI" is an existing Python project which complements the ctypes, SWIG and Cython approaches to ease writing C Extension Modules for Python. It has several advantages over the previous approaches, which are presented at the start of the documentation at http://cffi.readthedocs.org/en/latest/ . It has been very successful so far: http://pypi-ranking.info/alltime records almost 7 million downloads (for comparison, the #1 of all packages has almost 36 million downloads). CFFI works on any Python >= 2.6, including 3.x, as well as on PyPy. One problem is that while getting started with CFFI is very easy, the installation process of a package that uses CFFI has got its rough edges. CFFI (at least in its "verify()" mode) is based on calling the C compiler to get information about the exact C types, structures, argument types to functions, and so on. The C compiler is invoked transparently at run-time, and the results cached. A correctly-installed package using CFFI should cache the results at installation time, but it can be difficult to ensure that no more run-time compiler invocation is needed; doing so requires following some extra guidelines or understanding some internal details. (The problem is particularly acute on Windows where a typical user might not have a proper C compiler installed.) To fix this, we have in mind adding a different CFFI mode (replacing "verify()"), while keeping the access to the underlying C library unmodified. In this mode, the code containing the cdef() and verify() invocations would be moved to a separate Python source file. Running that Python file would produce a dynamically-linked library. There would be no caching logic involved; you would need to run it explicitly during development whenever you made changes to it, to re-generate and re-compile the dynamically-linked library. When distributed, the same file would be run (once) during installation. This can be fully automated in setuptools-based setup.py files; alternatively, it can be done in distutils-based setup.py files by requiring prior manual installation of CFFI itself. A major difference with the existing verify() approach would be that the ``.so/.dll/.dylib`` file would not be immediately loaded into the process; you would load it only from the installed program at run-time, and get the ``ffi`` and ``lib`` objects in this way (these are the two objects that you use so far to access a C library with verify()). Additionally, this would solve another issue: every import of a large CFFI-using package takes a while so far. This is caused by CFFI needing to parse again the C source code given in the cdef() (adding a run-time dependency to the ``pycparser`` and ``ply`` packages). CFFI also computes a CRC to know if it can reuse its cache. In the proposed change, all the cdef() code would be pre-parsed and stored in the dynamically-linked library, and no CRC would be needed. This would massively reduce the import times. Grant objective --------------- The objective is to give a boost towards "CFFI 1.0", which needs to have the functionalities described above in order to solve the two main issues with the current CFFI: the difficulties of installation, and the time taken at import. Included in the objective: the internal refactorings of CFFI that are needed to get it done cleanly. The goal is to avoid simply adding another layer on top of the old unchanged CFFI. This work may happen eventually in any case, but support from the PSF would help make it happen sooner rather than later. Grant size ---------- 2'500 US$ for supporting the development time. This would cover 2.5 weeks of full-time work at the part-time cost of 25 US$ per hour. The estimated work time until the CFFI 1.0 release is a bit larger than that (I estimate it at roughly 4 weeks), but 2.5 weeks should cover all the basics. An extended grant size of 4'000 US$ would be appreciated but not required ``:-)`` Grant beneficiaries ------------------- Armin Rigo, main author of CFFI, committing 2.5 weeks of full-time work. Grant follow-up --------------- I will report on the success of the grant on the CFFI mailing list and on the blog I usually post to (the PyPy blog) and mention the PSF as providing the grant. The PSF will receive an email pointing to these postings once they are out. Moreover a full CFFI 1.0 release should follow (likely starting with beta versions); the PSF will receive another email pointing to it. cffi-1.5.2/doc/misc/parse_c_type.rst0000664000175000017500000000431512657646311017601 0ustar arigoarigo00000000000000================================================== CPython C extension module produced by recompile() ================================================== Global variable:: _cffi_opcode_t _cffi_types[]; Every _cffi_types entry is initially an odd integer. At runtime, it is fixed to be a `CTypeDescrObject *` when the odd integer is interpreted and turned into a real object. The generated C functions are listed in _cffi_globals, a sorted array of entries which get turned lazily into real . Each entry in this array has an index in the _cffi_types array, which describe the function type (OP_FUNCTION opcode, see below). We turn the odd integers describing argument and return types into real CTypeDescrObjects at the point where the entry is turned into a real builtin function object. The odd integers are "opcodes" that contain a type info in the lowest byte. The remaining high bytes of the integer is an "arg" that depends on the type info: OP_PRIMITIVE the arg tells which primitive type it is (an index in some list) OP_POINTER the arg is the index of the item type in the _cffi_types array. OP_ARRAY the arg is the index of the item type in the _cffi_types array. followed by another opcode that contains (uintptr_t)length_of_array. OP_OPEN_ARRAY for syntax like "int[]". same as OP_ARRAY but without the length OP_STRUCT_UNION the arg is the index of the struct/union in _cffi_structs_unions OP_ENUM the arg is the index of the enum in _cffi_enums OP_TYPENAME the arg is the index of the typename in _cffi_typenames OP_FUNCTION the arg is the index of the result type in _cffi_types. followed by other opcodes for the arguments. terminated by OP_FUNCTION_END. OP_FUNCTION_END the arg's lowest bit is set if there is a "..." argument. OP_NOOP simple indirection: the arg is the index to look further in There are other opcodes, used not inside _cffi_types but in other individual ``type_op`` fields. Most importantly, these are used on _cffi_globals entries: OP_CPYTHON_BLTN_* declare a function OP_CONSTANT declare a non-integral constant OP_CONSTANT_INT declare an int constant OP_GLOBAL_VAR declare a global var cffi-1.5.2/doc/make.bat0000664000175000017500000000577712657646311015061 0ustar arigoarigo00000000000000@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-1.5.2/AUTHORS0000664000175000017500000000025312657646311013737 0ustar arigoarigo00000000000000This package has been mostly done by Armin Rigo with help from Maciej FijaƂkowski. The idea is heavily based (although not directly copied) from LuaJIT ffi by Mike Pall.