././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1637678 cffi-1.16.0/0000755000175100001770000000000000000000000013314 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/AUTHORS0000644000175100001770000000032000000000000014357 0ustar00runnerdocker00000000000000This 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. Other contributors: Google Inc. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/LICENSE0000644000175100001770000000241600000000000014324 0ustar00runnerdocker00000000000000 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/MANIFEST.in0000644000175100001770000000045000000000000015051 0ustar00runnerdocker00000000000000recursive-include src/cffi *.py *.h recursive-include src/c *.c *.h *.asm *.py win64.obj ffi.lib 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1637678 cffi-1.16.0/PKG-INFO0000644000175100001770000000271000000000000014411 0ustar00runnerdocker00000000000000Metadata-Version: 2.1 Name: cffi Version: 1.16.0 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 Project-URL: Documentation, http://cffi.readthedocs.org/ Project-URL: Source Code, https://github.com/python-cffi/cffi Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues Project-URL: Changelog, https://cffi.readthedocs.io/en/latest/whatsnew.html Project-URL: Downloads, https://github.com/python-cffi/cffi/releases Project-URL: Contact, https://groups.google.com/forum/#!forum/python-cffi Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: License :: OSI Approved :: MIT License Requires-Python: >=3.8 License-File: LICENSE Requires-Dist: pycparser CFFI ==== Foreign Function Interface for Python calling C code. Please see the `Documentation `_. Contact ------- `Mailing list `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/README.md0000644000175100001770000000126300000000000014575 0ustar00runnerdocker00000000000000CFFI ==== Foreign Function Interface for Python calling C code. Please see the [Documentation](http://cffi.readthedocs.org/) or uncompiled in the doc/ subdirectory. Download -------- [Download page](https://github.com/python-cffi/cffi/releases) Source Code ----------- Source code is publicly available on [GitHub](https://github.com/python-cffi/cffi). Contact ------- [Mailing list](https://groups.google.com/forum/#!forum/python-cffi) Testing/development tips ------------------------ To run tests under CPython, run the following in the source root directory:: pip install pytest pip install -e . # editable install of CFFI for local development pytest c/ testing/ ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1317677 cffi-1.16.0/demo/0000755000175100001770000000000000000000000014240 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/_curses.py0000644000175100001770000007366400000000000016275 0ustar00runnerdocker00000000000000"""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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/_curses_build.py0000644000175100001770000002124400000000000017437 0ustar00runnerdocker00000000000000import 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/_curses_setup.py0000644000175100001770000000046100000000000017476 0ustar00runnerdocker00000000000000from 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, ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/api.py0000644000175100001770000000315300000000000015365 0ustar00runnerdocker00000000000000import 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/bsdopendirtype.py0000644000175100001770000000233000000000000017643 0ustar00runnerdocker00000000000000from _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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/bsdopendirtype_build.py0000644000175100001770000000107700000000000021031 0ustar00runnerdocker00000000000000from cffi import FFI ffibuilder = FFI() ffibuilder.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; """) ffibuilder.set_source("_bsdopendirtype", """ #include #include """) if __name__ == '__main__': ffibuilder.compile(verbose=True) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/bsdopendirtype_setup.py0000644000175100001770000000050600000000000021066 0ustar00runnerdocker00000000000000from setuptools import setup setup( name="example", version="0.1", py_modules=["bsdopendirtype"], setup_requires=["cffi>=1.0.dev0"], cffi_modules=[ "bsdopendirtype_build.py:ffibuilder", ], install_requires=["cffi>=1.0.dev0"], # should maybe be "cffi-backend" only? zip_safe=False, ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/btrfs-snap.py0000644000175100001770000000213700000000000016674 0ustar00runnerdocker00000000000000""" 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/cffi-cocoa.py0000644000175100001770000000643200000000000016610 0ustar00runnerdocker00000000000000# 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')) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/embedding.py0000644000175100001770000000061600000000000016533 0ustar00runnerdocker00000000000000import cffi ffibuilder = cffi.FFI() ffibuilder.embedding_api(""" int add(int, int); """) ffibuilder.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 """) ffibuilder.set_source("_embedding_cffi", "") ffibuilder.compile(verbose=True) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/embedding_test.c0000644000175100001770000000172700000000000017370 0ustar00runnerdocker00000000000000/* There are two options: =====1===== Link this program with _embedding_test.so. E.g. with gcc: gcc -o embedding_test embedding_test.c _embedding_cffi*.so You must then run the executable with the right command (LD_LIBRARY_PATH on Linux), otherwise it won't find the _embedding_cffi*.so: LD_LIBRARY_PATH=. ./embedding_test There are platform-specific options to gcc to avoid needing that, too. Linux: gcc -o embedding_test embedding_test.c _embedding_cffi*.so \ -Wl,-rpath=\$ORIGIN/ =====2===== Compile and link the _embedding_test.c source code together with this example (e.g. with PyPy): gcc -o embedding_test embedding_test.c _embedding_cffi.c \ -I/opt/pypy/include -pthread -lpypy-c */ #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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/extern_python.py0000644000175100001770000000071400000000000017522 0ustar00runnerdocker00000000000000import 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/extern_python_varargs.py0000644000175100001770000000247200000000000021252 0ustar00runnerdocker00000000000000import 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/fastcsv.py0000644000175100001770000002024000000000000016261 0ustar00runnerdocker00000000000000import 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/gmp.py0000644000175100001770000000175400000000000015404 0ustar00runnerdocker00000000000000import 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/gmp_build.py0000644000175100001770000000130300000000000016551 0ustar00runnerdocker00000000000000import 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 # ffibuilder = cffi.FFI() ffibuilder.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); """) ffibuilder.set_source('_gmp_cffi', "#include ", libraries=['gmp', 'm']) if __name__ == '__main__': ffibuilder.compile(verbose=True) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/manual.c0000644000175100001770000000670100000000000015665 0ustar00runnerdocker00000000000000#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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/manual2.py0000644000175100001770000000222700000000000016154 0ustar00runnerdocker00000000000000import _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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/pwuid.py0000644000175100001770000000024600000000000015744 0ustar00runnerdocker00000000000000import 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/pwuid_build.py0000644000175100001770000000054600000000000017126 0ustar00runnerdocker00000000000000from 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/py.cleanup0000755000175100001770000000153600000000000016251 0ustar00runnerdocker00000000000000#! /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,) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/pyobj.py0000644000175100001770000000650700000000000015745 0ustar00runnerdocker00000000000000 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 #include #include """) if __name__ == '__main__': ffi.compile() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/readdir2_setup.py0000644000175100001770000000031100000000000017521 0ustar00runnerdocker00000000000000from distutils.core import setup import readdir2_build setup( name="readdir2", version="0.1", py_modules=["readdir2"], ext_modules=[readdir2_build.ffi.distutils_extension('build')], ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/readdir_build.py0000644000175100001770000000157200000000000017410 0ustar00runnerdocker00000000000000import 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/readdir_ctypes.py0000644000175100001770000000337500000000000017623 0ustar00runnerdocker00000000000000# 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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/readdir_setup.py0000644000175100001770000000037100000000000017445 0ustar00runnerdocker00000000000000from 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, ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/recopendirtype.py0000644000175100001770000000253200000000000017650 0ustar00runnerdocker00000000000000from _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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/recopendirtype_build.py0000644000175100001770000000061100000000000021023 0ustar00runnerdocker00000000000000from 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/setup_manual.py0000644000175100001770000000030100000000000017301 0ustar00runnerdocker00000000000000from distutils.core import setup from distutils.extension import Extension setup(name='manual', ext_modules=[Extension(name='manual', sources=['manual.c'])]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/winclipboard.py0000644000175100001770000000227200000000000017272 0ustar00runnerdocker00000000000000__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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/winclipboard_build.py0000644000175100001770000000151400000000000020447 0ustar00runnerdocker00000000000000from 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/xclient.py0000644000175100001770000000137200000000000016263 0ustar00runnerdocker00000000000000import 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/demo/xclient_build.py0000644000175100001770000000137100000000000017441 0ustar00runnerdocker00000000000000from 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(verbose=True) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1317677 cffi-1.16.0/doc/0000755000175100001770000000000000000000000014061 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/Makefile0000644000175100001770000000606600000000000015531 0ustar00runnerdocker00000000000000# 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." ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/make.bat0000644000175100001770000000577700000000000015506 0ustar00runnerdocker00000000000000@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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1317677 cffi-1.16.0/doc/misc/0000755000175100001770000000000000000000000015014 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/misc/design.rst0000644000175100001770000000533300000000000017023 0ustar00runnerdocker00000000000000================ 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[]``). ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/misc/grant-cffi-1.0.rst0000644000175100001770000001250500000000000020065 0ustar00runnerdocker00000000000000 =========================== 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/misc/parse_c_type.rst0000644000175100001770000000431500000000000020226 0ustar00runnerdocker00000000000000================================================== 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1317677 cffi-1.16.0/doc/source/0000755000175100001770000000000000000000000015361 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/source/cdef.rst0000644000175100001770000012551400000000000017024 0ustar00runnerdocker00000000000000====================================== 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 ffibuilder = cffi.FFI() ffibuilder.set_source("package._foo", None) ffibuilder.cdef("C-like declarations") if __name__ == "__main__": ffibuilder.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 and speed 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 ffibuilder = cffi.FFI() ffibuilder.set_source("package._foo", r"""real C code""") # <= ffibuilder.cdef("C-like declarations with '...'") if __name__ == "__main__": ffibuilder.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.ffibuilder.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:ffibuilder"], install_requires=["cffi>=1.0.0"], ) Note again that the ``foo_build.py`` example contains the following lines, which mean that the ``ffibuilder`` is not actually compiled when ``package.foo_build`` is merely imported---it will be compiled independently by the Setuptools logic, using compilation parameters provided by Setuptools: .. code-block:: python if __name__ == "__main__": # not when running with setuptools ffibuilder.compile(verbose=True) * Note that some bundler tools that try to find all modules used by a project, like PyInstaller, will miss ``_cffi_backend`` in the out-of-line mode because your program contains no explicit ``import cffi`` or ``import _cffi_backend``. You need to add ``_cffi_backend`` explicitly (as a "hidden import" in PyInstaller, but it can also be done more generally by adding the line ``import _cffi_backend`` in your main program). 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_or_ffibuilder = cffi.FFI()``. It has the same functionality (for in-line use), but also the extra methods described below (to prepare the FFI). NOTE: We use the name ``ffibuilder`` instead of ``ffi`` in the out-of-line context, when the code is about producing a ``_foo.so`` file; this is an attempt to distinguish it from the different ``ffi`` object that you get by later saying ``from _foo import 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__``, ``lib.__all__`` and ``lib.__name__`` added in successive versions. .. _cdef: ffi/ffibuilder.cdef(): declaring types and functions ---------------------------------------------------- **ffi/ffibuilder.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). *New in version 1.11:* char16_t and char32_t. * _Bool and bool (equivalent). If not directly supported by the C compiler, this is declared with the size of ``unsigned char``. * FILE. `See here.`__ * all `common Windows types`_ are defined if you run on Windows (``DWORD``, ``LPARAM``, etc.). Exception: ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE PTCHAR`` are not automatically defined; see `ffi.set_unicode()`_. * 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. .. __: ref.html#file .. _`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 optionally takes an extra argument: either ``packed`` or ``pack``. If you pass ``packed=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`_. In particular, if your C source uses other attributes like ``__attribute__((aligned(16)))``, there is no way to declare this fact in the ``cdef()``, but you can generally just declare the struct with ``"...;"`` as the last field.) *New in version 1.12:* In ABI mode, you can also pass ``pack=n``, with an integer ``n`` which must be a power of two. Then the alignment of any field is limited to ``n`` if it would otherwise be greater than ``n``. Passing ``pack=1`` is equivalent to passing ``packed=True``. This is meant to emulate ``#pragma pack(n)`` from the MSVC compiler. On Windows, the default is ``pack=8`` (from cffi 1.12 onwards); on other platforms, the default is ``pack=None``. 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 ); """)) Note also that pycparser, the underlying C parser, recognizes preprocessor-like directives in the following format: ``# NUMBER "FILE"``. For example, if you put ``# 42 "foo.h"`` in the middle of the string passed to ``cdef()`` and there is an error two lines later, then it is reported with an error message that starts with ``foo.h:43:`` (the line which is given the number 42 is the line immediately after the directive). *New in version 1.10.1:* CFFI automatically puts the line ``# 1 ""`` just before the string you give to ``cdef()``. .. _`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()``.) 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. .. _loading-libraries: .. _dlopen: 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 (dependency on ABI details, getting crashes instead of C compiler errors/warnings, and higher overhead to call the C functions). 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). Note that ``libpath`` `cannot be None`__ on Windows with Python 3. .. __: http://bugs.python.org/issue23606 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. API-level access, on the other hand, is safer. Speed-wise, API-level access is much faster (it is common to have the opposite misconception about performance). 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'))``. *New in version 1.14:* ``ffi.dlopen(handle)``: instead of a file path, you can give an already-opened library handle, as a cdata of type ``void *``. Such a call converts this handle into a regular FFI object with the functions and global variables declared by ``ffi.cdef()``. Useful if you have special needs (e.g. you need the GNU extension ``dlmopen()``, which you can itself declare and call using a different ``ffi`` object). Note that in this variant, ``dlclose()`` is not called automatically if the FFI object is garbage-collected (but you can still call ``ffi.dlclose()`` explicitly if needed). .. _set_source: ffibuilder.set_source(): preparing out-of-line modules ------------------------------------------------------ **ffibuilder.set_source(module_name, c_header_source, [\*\*keywords...])**: prepare the ffi for producing out-of-line an external module called ``module_name``. ``ffibuilder.set_source()`` by itself does not write any file, but merely records its arguments for later. It can therefore be called before or after ``ffibuilder.cdef()``. In **ABI mode,** you call ``ffibuilder.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. Typically, it is specified as ``r""" ...multiple lines of C code... """`` (the ``r`` prefix allows these lines to contain a literal ``\n``, for example). 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 ffibuilder.set_source("mymodule", r''' extern "C" { int somefunc(int somearg) { return real_cpp_func(somearg); } } ''', source_extension='.cpp') .. _pkgconfig: **ffibuilder.set_source_pkgconfig(module_name, pkgconfig_libs, c_header_source, [\*\*keywords...])**: *New in version 1.12.* This is equivalent to ``set_source()`` but it first calls the system utility ``pkg-config`` with the package names given in the list ``pkgconfig_libs``. It collects the information obtained in this way and adds it to the explicitly-provided ``**keywords`` (if any). This should probably not be used on Windows. If the ``pkg-config`` program is not installed or does not know about the requested library, the call fails with ``cffi.PkgConfigError``. If necessary, you can catch this error and try to call ``set_source()`` directly. (Ideally, you should also do that if the ``ffibuilder`` instance has no method ``set_source_pkgconfig()``, to support older versions of cffi.) 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: ``struct { }`` or ``union { }`` Use "``...;``" as the last "field" to declare a partial structure. This means fields can be left undeclared, declared out of order, or use non-standard alignment. Precisely, the field offsets, total struct size, and total struct alignment aren't deduced by looking at the ``cdef``. Instead they will be corrected by the compiler. Note that you can only access fields that you declared; and you must use the correct type for those you declare, the compiler can't figure it out. 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. ``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. ``typedef float... foo_t;`` *New in version 1.3:* Declares ``foo_t`` as a-float-or-a-double; the compiler will figure out which it is. ``typedef double... foo_t;`` has the same effect. 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.) ``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;``". ``extern int n[...];`` When used as structure fields or in global variables, arrays can have an unspecified length. The length is completed by the C compiler. This is slightly different from "``extern 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. This supports multidimensional arrays: "``extern int n[...][...];``". *New in version 1.2:* "``extern 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. ``enum foo { A, B, C, ... };`` If you don't know the exact order (or values) of the declared constants, then declare them 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. ``#define FOO ...`` For integer constants and macros you can write a line in the ``cdef`` 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---except in the following case: if such a type is explicitly named. For an integer type, use ``typedef int... the_type_name;``, or another type like ``typedef unsigned long... the_type_name;``. Both are equivalent and replaced by the real C type, which must be an integer type. Similarly, for floating-point types, use ``typedef float... the_type_name;`` or equivalently ``typedef double... the_type_name;``. Note that ``long double`` cannot be detected this way. In the case of function arguments or return types, when it is a simple integer/float type, you can simply misdeclare it. 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). It works because the C compiler will do the casting for us. This C-level casting of arguments and return types only works for regular function, and not for function pointer types; currently, it also does not work for variadic functions. For more complex types, you have no choice but be precise. For example, you cannot misdeclare a ``int *`` argument as ``long *``, or a global array ``extern int a[5];`` as ``extern long a[5];``. CFFI considers `all types listed above`_ as primitive (so ``extern long long a[5];`` and ``extern int64_t a[5]`` are different declarations). The reason for that is detailed in `a comment about an issue.`__ .. __: https://foss.heptapod.net/pypy/cffi/-/issues/265#note_50393 ffibuilder.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 ``ffibuilder.set_source()`` and ``ffibuilder.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. *New in version 1.8:* the C code produced by ``emit_c_code()`` or ``compile()`` contains ``#define Py_LIMITED_API``. This means that on CPython >= 3.2, compiling this source produces a binary .so/.dll that should work for any version of CPython >= 3.2 (as opposed to only for the same version of CPython x.y). However, the standard ``distutils`` package will still produce a file called e.g. ``NAME.cpython-35m-x86_64-linux-gnu.so``. You can manually rename it to ``NAME.abi3.so``, or use setuptools version 26 or later. Also, note that compiling with a debug version of Python will not actually define ``Py_LIMITED_API``, as doing so makes ``Python.h`` unhappy. *New in version 1.12:* ``Py_LIMITED_API`` is now defined on Windows too. If you use ``virtualenv``, you need a recent version of it: versions older than 16.0.0 forgot to copy ``python3.dll`` into the virtual environment. In case upgrading ``virtualenv`` is a real problem, you can manually edit the C code to remove the first line ``# define Py_LIMITED_API``. **ffibuilder.compile(tmpdir='.', verbose=False, debug=None):** 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__": ffibuilder.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.) *New in version 1.8.1:* ``debug`` argument. If set to a bool, it controls whether the C code is compiled in debug mode or not. The default None means to use the host Python's ``sys.flags.debug``. Starting with version 1.8.1, if you are running a debug-mode Python, the C code is thus compiled in debug mode by default (note that it is anyway necessary to do so on Windows). **ffibuilder.emit_python_code(filename):** generate the given .py file (same as ``ffibuilder.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). **ffibuilder.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``). **ffibuilder.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:ffibuilder"]`` 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 ``ffibuilder``. 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/ffibuilder.include(): combining multiple CFFI interfaces ------------------------------------------------------------ **ffi/ffibuilder.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 ``ffibuilder.include(other_ffibuilder)`` line should occur in the build script, and the ``other_ffibuilder`` argument should be another FFI instance 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 either in C99, or are GCC or MSVC extensions: * Any ``__attribute__`` or ``#pragma pack(n)`` * Additional types: special-size floating and fixed point types, vector types, and so on. * The C99 types ``float _Complex`` and ``double _Complex`` are supported by cffi since version 1.11, but not libffi: you cannot call C functions with complex arguments or return value, except if they are directly API-mode functions. The type ``long double _Complex`` is not supported at all (declare and use it as if it were an array of two ``long double``, and write wrapper functions in C with set_source()). * ``__restrict__`` or ``__restrict`` are extensions of, respectively, GCC and MSVC. They are not recognized. But ``restrict`` is a C keyword and is accepted (and ignored). 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. Note that if you declare a variable in ``cdef()`` without using ``const``, CFFI assumes it is a read-write variable and generates two pieces of code, one to read it and one to write it. If the variable cannot in fact be written to in C code, for one reason or another, it will not compile. In this case, you can declare it as a constant: for example, instead of ``foo_t *myglob;`` you would use ``foo_t *const myglob;``. Note also that ``const foo_t *myglob;`` is a *variable;* it contains a variable pointer to a constant ``foo_t``. 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 intended 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 ``ffibuilder.set_source()``. * The optional ``flags`` argument (ignored on Windows) defaults to ``ffi.RTLD_NOW``; see ``man dlopen``. (With ``ffibuilder.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 old versions of PyPy (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 = r''' #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_package="...", # 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 ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/source/conf.py0000644000175100001770000001427700000000000016673 0ustar00runnerdocker00000000000000# -*- 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-2018, 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.16' # The full version, including alpha/beta/rc tags. release = '1.16.0' # 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/source/embedding.rst0000644000175100001770000005426100000000000020041 0ustar00runnerdocker00000000000000================================ Using CFFI for embedding ================================ .. contents:: You can use CFFI to generate C code which exports the API of your choice to any C application that wants to link with this C code. This API, which you define yourself, ends up as the API of a ``.so/.dll/.dylib`` library---or you can statically link it within a larger application. Possible use cases: * Exposing a library written in Python directly to C/C++ programs. * Using Python to make a "plug-in" for an existing C/C++ program that is already written to load them. * Using Python to implement part of a larger C/C++ application (with static linking). * Writing a small C/C++ wrapper around Python, hiding the fact that the application is actually written in Python (to make a custom command-line interface; for distribution purposes; or simply to make it a bit harder to reverse-engineer the application). The general idea is as follows: * You write and execute a Python script, which produces a ``.c`` file with the API of your choice (and optionally compile it into a ``.so/.dll/.dylib``). The script also gives some Python code to be "frozen" inside the ``.so``. * At runtime, the C application loads this ``.so/.dll/.dylib`` (or is statically linked with the ``.c`` source) without having to know that it was produced from 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 defines more 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. This is entirely *new in version 1.5.* (PyPy contains CFFI 1.5 since release 5.0.) 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:: c /* file plugin.h, Windows-friendly version */ typedef struct { int x, y; } point_t; /* When including this file from ffibuilder.set_source(), the following macro is defined to '__declspec(dllexport)'. When including this file directly from your C program, we define it to 'extern __declspec(dllimport)' instead. With non-MSVC compilers we simply define it to 'extern'. (The 'extern' is needed for sharing global variables; functions would be fine without it. The macros always include 'extern': you must not repeat it when using the macros later.) */ #ifndef CFFI_DLLEXPORT # if defined(_MSC_VER) # define CFFI_DLLEXPORT extern __declspec(dllimport) # else # define CFFI_DLLEXPORT extern # endif #endif CFFI_DLLEXPORT int do_stuff(point_t *); .. code-block:: python # file plugin_build.py import cffi ffibuilder = cffi.FFI() with open('plugin.h') as f: # read plugin.h and pass it to embedding_api(), manually # removing the '#' directives and the CFFI_DLLEXPORT data = ''.join([line for line in f if not line.startswith('#')]) data = data.replace('CFFI_DLLEXPORT', '') ffibuilder.embedding_api(data) ffibuilder.set_source("my_plugin", r''' #include "plugin.h" ''') ffibuilder.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 """) ffibuilder.compile(target="plugin-1.5.*", verbose=True) # or: ffibuilder.emit_c_code("my_plugin.c") 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. See below__ for some pointers to C-level issues with using the produced library. .. __: `Issues about using the .so`_ Here are some details about the methods used above: * **ffibuilder.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 ``ffibuilder.set_source()``, as regular C code (see the point after next). * **ffibuilder.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 ``ffibuilder.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 ``ffibuilder.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. * **ffibuilder.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 ``ffibuilder.embedding_api()`` and "``CFFI_DLLEXPORT int my_glob = 42;``" in ``ffibuilder.set_source()``. Currently, any *type* declared in ``ffibuilder.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. * **ffibuilder.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 ``ffibuilder.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; it might not always be appropriate for making general DLLs. Also, just getting the C code is what you need if you do not want to make a stand-alone ``.so/.dll/.dylib`` file: this C file can be compiled and statically linked as part of a larger application. 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. * ``ffibuilder.embedding_api(source)``: follows the same syntax as ``ffibuilder.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 ``ffibuilder.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: ffibuilder.embedding_api(f.read()) Note that a drawback of this approach is that ``ffibuilder.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('#')] ffibuilder.embedding_api(''.join(lines)) As in the example above, you can also use the same ``foo.h`` from ``ffibuilder.set_source()``:: ffibuilder.set_source('module_name', r''' #include "foo.h" ''') .. __: using.html#working .. __: using.html#def-extern .. __: ref.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. * On PyPy, the error message debug: pypy_setup_home: directories 'lib-python' and 'lib_pypy' not found in pypy's shared library location or in any parent directory means that the ``libpypy-c.so`` file was found, but the standard library was not found from this location. This occurs at least on some Linux distributions, because they put ``libpypy-c.so`` inside ``/usr/lib/``, instead of the way we recommend, which is: keep that file inside ``/opt/pypy/bin/`` and put a symlink to there from ``/usr/lib/``. The quickest fix is to do that change manually. Issues about using the .so -------------------------- This paragraph describes issues that are not necessarily specific to CFFI. It assumes that you have obtained the ``.so/.dylib/.dll`` file as described above, but that you have troubles using it. (In summary: it is a mess. This is my own experience, slowly built by using Google and by listening to reports from various platforms. Please report any inaccuracies in this paragraph or better ways to do things.) * The file produced by CFFI should follow this naming pattern: ``libmy_plugin.so`` on Linux, ``libmy_plugin.dylib`` on Mac, or ``my_plugin.dll`` on Windows (no ``lib`` prefix on Windows). * First note that this file does not contain the Python interpreter nor the standard library of Python. You still need it to be somewhere. There are ways to compact it to a smaller number of files, but this is outside the scope of CFFI (please report if you used some of these ways successfully so that I can add some links here). * In what we'll call the "main program", the ``.so`` can be either used dynamically (e.g. by calling ``dlopen()`` or ``LoadLibrary()`` inside the main program), or at compile-time (e.g. by compiling it with ``gcc -lmy_plugin``). The former case is always used if you're building a plugin for a program, and the program itself doesn't need to be recompiled. The latter case is for making a CFFI library that is more tightly integrated inside the main program. * In the case of compile-time usage: you can add the gcc option ``-Lsome/path/`` before ``-lmy_plugin`` to describe where the ``libmy_plugin.so`` is. On some platforms, notably Linux, ``gcc`` will complain if it can find ``libmy_plugin.so`` but not ``libpython27.so`` or ``libpypy-c.so``. To fix it, you need to call ``LD_LIBRARY_PATH=/some/path/to/libpypy gcc``. * When actually executing the main program, it needs to find the ``libmy_plugin.so`` but also ``libpython27.so`` or ``libpypy-c.so``. For PyPy, unpack a PyPy distribution and you get a full directory structure with ``libpypy-c.so`` inside a ``bin`` subdirectory, or on Windows ``pypy-c.dll`` inside the top directory; you must not move this file around, but just point to it. One way to point to it is by running the main program with some environment variable: ``LD_LIBRARY_PATH=/some/path/to/libpypy`` on Linux, ``DYLD_LIBRARY_PATH=/some/path/to/libpypy`` on OS/X. * You can avoid the ``LD_LIBRARY_PATH`` issue if you compile ``libmy_plugin.so`` with the path hard-coded inside in the first place. On Linux, this is done by ``gcc -Wl,-rpath=/some/path``. You would put this option in ``ffibuilder.set_source("my_plugin", ..., extra_link_args=['-Wl,-rpath=/some/path/to/libpypy'])``. The path can start with ``$ORIGIN`` to mean "the directory where ``libmy_plugin.so`` is". You can then specify a path relative to that place, like ``extra_link_args=['-Wl,-rpath=$ORIGIN/../venv/bin']``. Use ``ldd libmy_plugin.so`` to look at what path is currently compiled in after the expansion of ``$ORIGIN``.) After this, you don't need ``LD_LIBRARY_PATH`` any more to locate ``libpython27.so`` or ``libpypy-c.so`` at runtime. In theory it should also cover the call to ``gcc`` for the main program. I wasn't able to make ``gcc`` happy without ``LD_LIBRARY_PATH`` on Linux if the rpath starts with ``$ORIGIN``, though. * The same rpath trick might be used to let the main program find ``libmy_plugin.so`` in the first place without ``LD_LIBRARY_PATH``. (This doesn't apply if the main program uses ``dlopen()`` to load it as a dynamic plugin.) You'd make the main program with ``gcc -Wl,-rpath=/path/to/libmyplugin``, possibly with ``$ORIGIN``. The ``$`` in ``$ORIGIN`` causes various shell problems on its own: if using a common shell you need to say ``gcc -Wl,-rpath=\$ORIGIN``. From a Makefile, you need to say something like ``gcc -Wl,-rpath=\$$ORIGIN``. * On some Linux distributions, notably Debian, the ``.so`` files of CPython C extension modules may be compiled without saying that they depend on ``libpythonX.Y.so``. This makes such Python systems unsuitable for embedding if the embedder uses ``dlopen(..., RTLD_LOCAL)``. You get an ``undefined symbol`` error. See `issue #264`__. A workaround is to first call ``dlopen("libpythonX.Y.so", RTLD_LAZY|RTLD_GLOBAL)``, which will force ``libpythonX.Y.so`` to be loaded first. .. __: https://foss.heptapod.net/pypy/cffi/-/issues/264 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 ``ffibuilder.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* ``ffibuilder.embedding_api()`` and ``ffibuilder.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 ``ffibuilder.embedding_api()``. (Note that this requires more hacks if you use ``ffibuilder.embedding_api(f.read())``.) You must only write the custom function definition in ``ffibuilder.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 ffibuilder.cdef(""" extern "Python" int mycb(int); """) ffibuilder.set_source("my_plugin", r""" 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 ``ffibuilder.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. Note that the function ``cffi_start_python()`` is static: it must be called from C source written inside ``ffibuilder.set_source()``. To call it from somewhere else, you need to make a function (with a different non-static name) in the ``ffibuilder.set_source()`` that just calls ``cffi_start_python()``. The reason it is static is to avoid naming conflicts in case you are ultimately trying to link a large C program with more than one cffi embedded module in it. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/source/goals.rst0000644000175100001770000000567600000000000017236 0ustar00runnerdocker00000000000000Goals ----- 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. * There is no C++ support. Sometimes, it is reasonable to write a C wrapper around the C++ code and then call this C API with CFFI. Otherwise, look at other projects. I would recommend cppyy_, which has got some similarities (and also works efficiently on both CPython and PyPy). .. _`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 .. _`cppyy`: http://cppyy.readthedocs.io/en/latest/ .. _`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 ``#cffi`` or ``#pypy`` channels of ``irc.libera.chat``. 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://github.com/python-cffi/cffi/issues .. _`mailing list`: https://groups.google.com/forum/#!forum/python-cffi ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/source/index.rst0000644000175100001770000000060100000000000017217 0ustar00runnerdocker00000000000000================================ CFFI documentation ================================ C Foreign Function Interface for Python. Interact with almost any C code from Python, based on C-like declarations that you can often copy-paste from header files or documentation. .. toctree:: :maxdepth: 2 goals whatsnew installation overview using ref cdef embedding ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/source/installation.rst0000644000175100001770000001211000000000000020607 0ustar00runnerdocker00000000000000======================================================= 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 is tested with CPython 3.8-3.12. 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 3.8+, 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. On non-Windows platforms, this usually means installing the package ``python-dev``. Refer to the appropriate docs for your OS. * 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``). * `pytest`_ is needed to run the tests of CFFI itself. .. _`pytest`: http://pypi.python.org/pypi/pytest Download and Installation: * https://pypi.python.org/pypi/cffi * Or grab the most current version from `GitHub`_: ``git clone https://github.com/python-cffi/cffi`` * running the tests: ``pytest c/ testing/`` (if you didn't install cffi yet, you need first ``python setup_base.py build_ext -f -i``) .. _`GitHub project home`: https://github.com/python-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://github.com/python-cffi/cffi/blob/main/demo .. _`testing/cffi1/test_verify1.py`: https://github.com/python-cffi/cffi/blob/main/testing/cffi1/test_verify1.py .. _`testing/cffi0/backend_tests.py`: https://github.com/python-cffi/cffi/blob/main/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 and Mark Keller for this) 1) Install homebrew: http://brew.sh 2) Run the following commands in a terminal :: brew install pkg-config libffi PKG_CONFIG_PATH=$(brew --prefix libffi)/lib/pkgconfig pip install --no-binary cffi cffi (the ``--no-binary cffi`` might be needed or not.) Alternatively, **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 (32/64-bit) +++++++++++++++++++ Win32 and Win64 work and are 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 More generally, the solution that should always work is to download the sources of CFFI (instead of a prebuilt binary) and make sure that you build it with the same version of Python that will use it. For example, with virtualenv: * ``virtualenv ~/venv`` * ``cd ~/path/to/sources/of/cffi`` * ``~/venv/bin/python setup.py build --force`` # forcing a rebuild to make sure * ``~/venv/bin/python setup.py install`` This will compile and install CFFI in this virtualenv, using the Python from this virtualenv. NetBSD ++++++ You need to make sure you have an up-to-date version of libffi, which fixes some bugs. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/source/overview.rst0000644000175100001770000005444200000000000017772 0ustar00runnerdocker00000000000000======================================================= Overview ======================================================= .. contents:: The first section presents a simple working example of using CFFI to call a C function in a compiled shared object (DLL) from Python. CFFI is flexible and covers several other use cases presented in the second section. The third section shows how to export Python functions to a Python interpreter embedded in a C or C++ application. The last two sections delve deeper in the CFFI library. Make sure you have `cffi installed`__. You can find these and some other complete demos in the demo__ directory of the repository__. .. __: installation.html .. __: https://github.com/python-cffi/cffi/blob/main/demo .. __: https://github.com/python-cffi/cffi .. _out-of-line-api-level: .. _real-example: Main mode of usage ------------------ The main way to use CFFI is as an interface to some already-compiled shared object which is provided by other means. Imagine that you have a system-installed shared object called ``piapprox.dll`` (Windows) or ``libpiapprox.so`` (Linux and others) or ``libpiapprox.dylib`` (OS X), exporting a function ``float pi_approx(int n);`` that computes some approximation of pi given a number of iterations. You want to call this function from Python. Note this method works equally well with a static library ``piapprox.lib`` (Windows) or ``libpiapprox.a``. Create the file ``piapprox_build.py``: .. code-block:: python from cffi import FFI ffibuilder = FFI() # cdef() expects a single string declaring the C types, functions and # globals needed to use the shared object. It must be in valid C syntax. ffibuilder.cdef(""" float pi_approx(int n); """) # set_source() gives the name of the python extension module to # produce, and some C source code as a string. This C code needs # to make the declarated functions, types and globals available, # so it is often just the "#include". ffibuilder.set_source("_pi_cffi", """ #include "pi.h" // the C header of the library """, libraries=['piapprox']) # library name, for the linker if __name__ == "__main__": ffibuilder.compile(verbose=True) Execute this script. If everything is OK, it should produce ``_pi_cffi.c``, and then invoke the compiler on it. The produced ``_pi_cffi.c`` contains a copy of the string given in :ref:`set_source() `, in this example the ``#include "pi.h"``. Afterwards, it contains glue code for all the functions, types and globals declared in the :ref:`cdef() ` above. At runtime, you use the extension module like this: .. code-block:: python from _pi_cffi import ffi, lib print(lib.pi_approx(5000)) That's all! In the rest of this page, we describe some more advanced examples and other CFFI modes. In particular, there is a complete example `if you don't have an already-installed C library to call`_. For more information about the ``cdef()`` and ``set_source()`` methods of the ``FFI`` class, see `Preparing and Distributing modules`__. .. __: cdef.html When your example works, a common alternative to running the build script manually is to have it run as part of a ``setup.py``. Here is an example using the Setuptools distribution: .. code-block:: python from setuptools import setup setup( ... setup_requires=["cffi>=1.0.0"], cffi_modules=["piapprox_build:ffibuilder"], # "filename:global" install_requires=["cffi>=1.0.0"], ) Other CFFI modes ---------------- 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 faster **API mode** accesses them with a C compiler. We explain the difference in more details 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. Simple example (ABI level, in-line) +++++++++++++++++++++++++++++++++++ May look familiar to those who have used ctypes_. .. 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[]", b"world") # equivalent to C code: char arg[] = "world"; >>> C.printf(b"hi there, %s.\n", arg) # call printf hi there, world. 17 # this is the return value >>> Note that ``char *`` arguments expect a ``bytes`` object. If you have a ``str`` (or a ``unicode`` on Python 2) you need to encode it explicitly with ``somestring.encode(myencoding)``. *Python 3 on Windows:* :ref:`ffi.dlopen(None) ` does not work. This problem is messy and not really fixable. The problem does not occur if you try to call a function from a specific DLL that exists on your system: then you use ``ffi.dlopen("path.dll")``. *This example does not call any C compiler. It works in the so-called ABI mode, which means that it will crash if you call some function or access some fields of a structure that was slightly misdeclared in the cdef().* If using a C compiler to install your module is an option, it is highly recommended to use the API mode instead. (It is also faster.) 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, and replaces ctypes_. You could also call :ref:`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 .. _ctypes: http://docs.python.org/library/ctypes.html *This example does not call any C compiler.* This example also admits an out-of-line equivalent. It is similar to the first example `Main mode of usage`_ above, but passing ``None`` as the second argument to :ref:`ffibuilder.set_source() `. Then 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)``. API Mode, calling the C standard library ++++++++++++++++++++++++++++++++++++++++ .. code-block:: python # file "example_build.py" # Note: we instantiate the same 'cffi.FFI' class as in the previous # example, but call the result 'ffibuilder' now instead of 'ffi'; # this is to avoid confusion with the other 'ffi' object you get below from cffi import FFI ffibuilder = FFI() ffibuilder.set_source("_example", r""" // passed to the real C compiler, // contains implementation of things declared in cdef() #include #include // We can also define custom wrappers or other functions // here (this is an example only): static struct passwd *get_pw_for_root(void) { return getpwuid(0); } """, libraries=[]) # or a list of libraries to link with # (more arguments like setup.py's Extension class: # include_dirs=[..], extra_objects=[..], and so on) ffibuilder.cdef(""" // declarations that are shared between Python and C struct passwd { char *pw_name; ...; // literally dot-dot-dot }; struct passwd *getpwuid(int uid); // defined in struct passwd *get_pw_for_root(void); // defined in set_source() """) if __name__ == "__main__": ffibuilder.compile(verbose=True) 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 :ref:`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' p = lib.get_pw_for_root() 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, in the :ref:`cdef() ` we declared ``getpwuid()`` as taking an ``int`` argument; on some platforms this might be slightly incorrect---but it does not matter. Note also that at runtime, the API mode is faster than the ABI mode. 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:ffibuilder"], install_requires=["cffi>=1.0.0"], ) .. _`if you don't have an already-installed C library to call`: API Mode, calling C sources instead of a compiled library +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ If you want to call some library that is not precompiled, but for which you have C sources, then the easiest solution is to make a single extension module that is compiled from both the C sources of this library, and the additional CFFI wrappers. For example, say you start with the files ``pi.c`` and ``pi.h``: .. code-block:: C /* filename: pi.c*/ # include # include /* Returns a very crude approximation of Pi given a int: a number of iteration */ float pi_approx(int n){ double i,x,y,sum=0; for(i=0;i` many times with small pieces of declarations, based on the version of libraries detected on the system). .. code-block:: python # file "simple_example_build.py" from cffi import FFI ffibuilder = FFI() # Note that the actual source is None ffibuilder.set_source("_simple_example", None) ffibuilder.cdef(""" int printf(const char *format, ...); """) if __name__ == "__main__": ffibuilder.compile(verbose=True) 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 :ref:`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:ffibuilder"], install_requires=["cffi>=1.0.0"], ) In summary, this mode is useful when you wish to declare many C structures but do not need fast interaction with a shared object. It is useful for parsing binary files, for instance. In-line, API level ++++++++++++++++++ The "API level + in-line" mode combination exists but is long deprecated. It used to be done with ``lib = ffi.verify("C header")``. The out-of-line variant with :ref:`set_source("modname", "C header") ` is preferred and avoids a number of problems when the project grows in size. .. _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 ffibuilder = cffi.FFI() ffibuilder.embedding_api(""" int do_stuff(int, int); """) ffibuilder.set_source("my_plugin", "") ffibuilder.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 """) ffibuilder.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 :ref:`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 :ref:`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 :ref:`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 :ref:`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. The most immediate drawback of the ABI level is that calling functions needs to go through the very general *libffi* library, which is slow (and not always perfectly tested on non-standard platforms). The API mode instead compiles a CPython C wrapper that directly invokes the target function. It can be massively faster (and works better than libffi ever will). The more fundamental reason to prefer the API mode is that *the C libraries are typically meant to be used with a C compiler.* You are not supposed to do things like guess where fields are in the structures. The "real example" above shows how CFFI uses a C compiler under the hood: this example uses :ref:`set_source(..., "C source...") ` and never :ref:`dlopen() `. When using this approach, we have the advantage that we can use literally "``...``" at various places in the :ref:`cdef() `, and the missing information will be completed with the help of the C compiler. CFFI will turn this into a single C source file, which contains 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 (which depends on the ``_cffi_backend`` module, on CPython). 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_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/source/ref.rst0000644000175100001770000013546100000000000016701 0ustar00runnerdocker00000000000000================================ CFFI Reference ================================ .. contents:: FFI Interface ------------- *This page documents the runtime interface of the two types "FFI" and "CompiledFFI". These two types are very similar to each other. You get a CompiledFFI object if you import an out-of-line module. You get a FFI object from explicitly writing cffi.FFI(). Unlike CompiledFFI, the type FFI has also got additional methods documented on the* `next page`__. .. __: cdef.html ffi.NULL ++++++++ **ffi.NULL**: a constant NULL of type ````. ffi.error +++++++++ **ffi.error**: the Python exception raised in various cases. (Don't confuse it with ``ffi.errno``.) .. _new: ffi.new() +++++++++ **ffi.new(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[10]')`` allocates an array of 10 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. See the `detailed documentation`__ for other valid initializers. .. __: using.html#working 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. Also, this means that a line like ``x = ffi.cast("B *", ffi.new("A *"))`` or ``x = ffi.new("struct s[1]")[0]`` is wrong: the newly allocated object goes out of scope instantly, and so is freed immediately, and ``x`` is garbage. The only case where this is fine comes from a special case for pointers-to-struct and pointers-to-union types: after ``p = ffi.new("struct-or-union *", ..)``, then either ``p`` or ``p[0]`` keeps the memory alive. The returned memory is initially cleared (filled with zeroes), before the optional initializer is applied. For performance, see `ffi.new_allocator()`_ for a way to allocate non-zero-initialized memory. *New in version 1.12:* see also ``ffi.release()``. ffi.cast() ++++++++++ **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-errno: .. _ffi-getwinerror: ffi.errno, ffi.getwinerror() ++++++++++++++++++++++++++++ **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 thread-local 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: .. _ffi-unpack: ffi.string(), ffi.unpack() ++++++++++++++++++++++++++ **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. The 'maxlen' argument limits how far we look for a null character. If 'cdata' is an array then 'maxlen' defaults to its length. See ``ffi.unpack()`` 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. *New in version 1.11:* can also be char16_t or char32_t. - If 'cdata' is a single character or byte or a wchar_t or charN_t, returns it as a byte string or unicode string. (Note that in some situation a single wchar_t or char32_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.unpack(cdata, length)**: unpacks an array of C data of the given length, returning a Python string/unicode/list. The 'cdata' should be a pointer; if it is an array it is first converted to the pointer type. *New in version 1.6.* - If 'cdata' is a pointer to 'char', returns a byte string. It does not stop at the first null. (An equivalent way to do that is ``ffi.buffer(cdata, length)[:]``.) - If 'cdata' is a pointer to 'wchar_t', returns a unicode string. ('length' is measured in number of wchar_t; it is not the size in bytes.) *New in version 1.11:* can also be char16_t or char32_t. - If 'cdata' is a pointer to anything else, returns a list, of the given 'length'. (A slower way to do that is ``[cdata[i] for i in range(length)]``.) .. _ffi-buffer: .. _ffi-from-buffer: ffi.buffer(), ffi.from_buffer() +++++++++++++++++++++++++++++++ **ffi.buffer(cdata, [size])**: return a buffer object that references the raw C data pointed to by the given 'cdata', of 'size' bytes. What Python calls "a buffer", or more precisely "an object supporting the buffer interface", is an object that represents some raw memory and that can be passed around to various built-in or extension functions; these built-in functions read from or write to the raw memory directly, without needing an extra copy. The 'cdata' argument 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. 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) - overwrite the content of a struct: if ``p`` is a cdata pointing to it, use ``ffi.buffer(p)[:] = newcontent``, where ``newcontent`` is a bytes object (``str`` in Python 2). Remember that like in C, you can use ``array + index`` to get the pointer to the index'th item of an array. (In C you might more naturally write ``&array[index]``, but that is equivalent.) The returned object's type is not the builtin ``buffer`` nor ``memoryview`` types, because these types' API changes too much across Python versions. Instead it has the following Python API (a subset of Python 2's ``buffer``) in addition to supporting the buffer interface: - ``buf[:]`` or ``bytes(buf)``: copy data out of the buffer, returning a regular byte string (or ``buf[start:end]`` for a part) - ``buf[:] = newstr``: copy data into the buffer (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. *New in version 1.10:* ``ffi.buffer`` is now the type of the returned buffer objects; ``ffi.buffer()`` actually calls the constructor. **ffi.from_buffer([cdecl,] python_buffer, require_writable=False)**: return an array cdata (by default a ````) that points to the data of the given Python object, which must support the buffer interface. Note that ``ffi.from_buffer()`` turns a generic Python buffer object into a cdata object, whereas ``ffi.buffer()`` does the opposite conversion. Both calls don't actually copy any data. ``ffi.from_buffer()`` is meant to be used on objects containing large quantities of raw data, like bytearrays or ``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. *In particular, never modify byte strings!* 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. A common use case is calling a C function with some ``char *`` that points to the internal buffer of a Python object; for this case you can directly pass ``ffi.from_buffer(python_buffer)`` as argument to the call. *New in version 1.10:* the ``python_buffer`` can be anything supporting the buffer/memoryview interface (except unicode strings). Previously, bytearray objects were supported in version 1.7 onwards (careful, if you resize the bytearray, the ```` object will point to freed memory); and byte strings were supported in version 1.8 onwards. *New in version 1.12:* added the optional *first* argument ``cdecl``, and the keyword argument ``require_writable``: * ``cdecl`` defaults to ``"char[]"``, but a different array or (from version 1.13) pointer type can be specified for the result. A value like ``"int[]"`` will return an array of ints instead of chars, and its length will be set to the number of ints that fit in the buffer (rounded down if the division is not exact). Values like ``"int[42]"`` or ``"int[2][3]"`` will return an array of exactly 42 (resp. 2-by-3) ints, raising a ValueError if the buffer is too small. The difference between specifying ``"int[]"`` and using the older code ``p1 = ffi.from_buffer(x); p2 = ffi.cast("int *", p1)`` is that the older code needs to keep ``p1`` alive as long as ``p2`` is in use, because only ``p1`` keeps the underlying Python object alive and locked. (In addition, ``ffi.from_buffer("int[]", x)`` gives better array bound checking.) *New in version 1.13:* ``cdecl`` can be a pointer type. If it points to a struct or union, you can, as usual, write ``p.field`` instead of ``p[0].field``. You can also access ``p[n]``; note that CFFI does not perform any bounds checking in this case. Note also that ``p[0]`` cannot be used to keep the buffer alive (unlike what occurs with ``ffi.new()``). * if ``require_writable`` is set to True, the function fails if the buffer obtained from ``python_buffer`` is read-only (e.g. if ``python_buffer`` is a byte string). The exact exception is raised by the object itself, and for things like bytes it varies with the Python version, so don't rely on it. (Before version 1.12, the same effect can be achieved with a hack: call ``ffi.memmove(python_buffer, b"", 0)``. This has no effect if the object is writable, but fails if it is read-only.) Please keep in mind that CFFI does not implement the C keyword ``const``: even if you set ``require_writable`` to False explicitly, you still get a regular read-write cdata pointer. *New in version 1.12:* see also ``ffi.release()``. ffi.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. *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``. In versions before 1.10, ``ffi.from_buffer()`` had restrictions on the type of buffer, which made ``ffi.memmove()`` more general. .. _ffi-typeof: .. _ffi-sizeof: .. _ffi-alignof: ffi.typeof(), ffi.sizeof(), ffi.alignof() +++++++++++++++++++++++++++++++++++++++++ **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.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. For ``array = ffi.new("T[]", n)``, then ``ffi.sizeof(array)`` returns ``n * ffi.sizeof("T")``. *New in version 1.9:* Similar rules apply for structures with a variable-sized array at the end. More precisely, if ``p`` was returned by ``ffi.new("struct foo *", ...)``, then ``ffi.sizeof(p[0])`` now returns the total allocated size. In previous versions, it used to just return ``ffi.sizeof(ffi.typeof(p[0]))``, which is the size of the structure ignoring the variable-sized part. (Note that due to alignment, it is possible for ``ffi.sizeof(p[0])`` to return a value smaller than ``ffi.sizeof(ffi.typeof(p[0]))``.) **ffi.alignof("C type")**: return the natural alignment size in bytes of the argument. Corresponds to the ``__alignof__`` operator in GCC. .. _ffi-offsetof: .. _ffi-addressof: ffi.offsetof(), ffi.addressof() +++++++++++++++++++++++++++++++ **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. 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.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. 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-cdata: .. _ffi-ctype: ffi.CData, ffi.CType ++++++++++++++++++++ **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``. *New in version 1.10:* ``ffi.buffer`` is now `a type`__ as well. .. __: #ffi-buffer .. _ffi-gc: ffi.gc() ++++++++ **ffi.gc(cdata, destructor, size=0)**: 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.custom_malloc(42), lib.custom_free)``. Note that like objects returned by ``ffi.new()``, the returned pointer objects have *ownership*, which means the destructor is called as soon as *this* exact returned object is garbage-collected. *New in version 1.12:* see also ``ffi.release()``. **ffi.gc(ptr, None, size=0)**: removes the ownership on a object returned by a regular call to ``ffi.gc``, and no destructor will be called when it is garbage-collected. The object is modified in-place, and the function returns ``None``. *New in version 1.7: ffi.gc(ptr, None)* Note that ``ffi.gc()`` should be avoided for limited resources, or (with cffi below 1.11) for large memory allocations. 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. In cffi 1.12, see also ``ffi.release()``. *New in version 1.11:* the ``size`` argument. If given, this should be an estimate of the size (in bytes) that ``ptr`` keeps alive. This information is passed on to the garbage collector, fixing part of the problem described above. The ``size`` argument is most important on PyPy; on CPython, it is ignored so far, but in the future it could be used to trigger more eagerly the cyclic reference GC, too (see CPython `issue 31105`__). The form ``ffi.gc(ptr, None, size=0)`` can be called with a negative ``size``, to cancel the estimate. It is not mandatory, though: nothing gets out of sync if the size estimates do not match. It only makes the next GC start more or less early. Note that if you have several ``ffi.gc()`` objects, the corresponding destructors will be called in a random order. If you need a particular order, see the discussion in `issue 340`__. .. __: http://bugs.python.org/issue31105 .. __: https://foss.heptapod.net/pypy/cffi/-/issues/340 .. _ffi-new-handle: .. _ffi-from-handle: ffi.new_handle(), ffi.from_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!* See a `typical usage example`_ below. (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. .. _`typical usage example`: Usage example: suppose you have a C library where you must call a ``lib.process_document()`` function which invokes some callback. The ``process_document()`` function receives a pointer to a callback and a ``void *`` argument. The callback is then invoked with the ``void *data`` argument that is equal to the provided value. In this typical case, you can implement it like this (out-of-line API mode):: class MyDocument: ... def process(self): h = ffi.new_handle(self) lib.process_document(lib.my_callback, # the callback h, # 'void *data' args...) # 'h' stays alive until here, which means that the # ffi.from_handle() done in my_callback() during # the call to process_document() is safe def callback(self, arg1, arg2): ... # the actual callback is this one-liner global function: @ffi.def_extern() def my_callback(arg1, arg2, data): return ffi.from_handle(data).callback(arg1, arg2) .. _ffi-dlopen: .. _ffi-dlclose: ffi.dlopen(), ffi.dlclose() +++++++++++++++++++++++++++ **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()``. ffi.new_allocator() +++++++++++++++++++ **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. Example: for performance, if you are using ``ffi.new()`` to allocate large chunks of memory where the initial content can be left uninitialized, you can do:: # at module level new_nonzero = ffi.new_allocator(should_clear_after_alloc=False) # then replace `p = ffi.new("char[]", bigsize)` with: p = new_nonzero("char[]", bigsize) **NOTE:** the following is a general warning that applies particularly (but not only) to PyPy versions 5.6 or older (PyPy > 5.6 attempts to account for the memory returned by ``ffi.new()`` or a custom allocator; and CPython uses reference counting). If you do large allocations, then there is no hard guarantee about when the memory will be freed. You should avoid both ``new()`` and ``new_allocator()()`` if you want to be sure that the memory is promptly released, e.g. before you allocate more of it. An alternative is to declare and call the C ``malloc()`` and ``free()`` functions, or some variant like ``mmap()`` and ``munmap()``. Then you control exactly when the memory is allocated and freed. For example, add these two lines to your existing ``ffibuilder.cdef()``:: void *malloc(size_t size); void free(void *ptr); and then call these two functions manually:: p = lib.malloc(n * ffi.sizeof("int")) try: my_array = ffi.cast("int *", p) ... finally: lib.free(p) In cffi version 1.12 you can indeed use ``ffi.new_allocator()`` but use the ``with`` statement (see ``ffi.release()``) to force the free function to be called at a known point. The above is equivalent to this code:: my_new = ffi.new_allocator(lib.malloc, lib.free) # at global level ... with my_new("int[]", n) as my_array: ... **Warning:** due to a bug, ``p = ffi.new_allocator(..)("struct-or-union *")`` might not follow the rule that either ``p`` or ``p[0]`` keeps the memory alive, which holds for the normal ``ffi.new("struct-or-union *")`` allocator. It may sometimes be the case that if there is only a reference to ``p[0]``, the memory is freed. The cause is that the rule doesn't hold for ``ffi.gc()``, which is sometimes used in the implementation of ``ffi.new_allocator()()``; this might be fixed in a future release. .. _ffi-release: ffi.release() and the context manager +++++++++++++++++++++++++++++++++++++ **ffi.release(cdata)**: release the resources held by a cdata object from ``ffi.new()``, ``ffi.gc()``, ``ffi.from_buffer()`` or ``ffi.new_allocator()()``. The cdata object must not be used afterwards. The normal Python destructor of the cdata object releases the same resources, but this allows the releasing to occur at a known time, as opposed as at an unspecified point in the future. *New in version 1.12.* ``ffi.release(cdata)`` is equivalent to ``cdata.__exit__()``, which means that you can use the ``with`` statement to ensure that the cdata is released at the end of a block (in version 1.12 and above):: with ffi.from_buffer(...) as p: do something with p The effect is more precisely as follows: * on an object returned from ``ffi.gc(destructor)``, ``ffi.release()`` will cause the ``destructor`` to be called immediately. * on an object returned from a custom allocator, the custom free function is called immediately. * on CPython, ``ffi.from_buffer(buf)`` locks the buffer, so ``ffi.release()`` can be used to unlock it at a known time. On PyPy, there is no locking (so far); the effect of ``ffi.release()`` is limited to removing the link, allowing the original buffer object to be garbage-collected even if the cdata object stays alive. * on CPython this method has no effect (so far) on objects returned by ``ffi.new()``, because the memory is allocated inline with the cdata object and cannot be freed independently. It might be fixed in future releases of cffi. * on PyPy, ``ffi.release()`` frees the ``ffi.new()`` memory immediately. It is useful because otherwise the memory is kept alive until the next GC occurs. If you allocate large amounts of memory with ``ffi.new()`` and don't free them with ``ffi.release()``, PyPy (>= 5.7) runs its GC more often to compensate, so the total memory allocated should be kept within bounds anyway; but calling ``ffi.release()`` explicitly should improve performance by reducing the frequency of GC runs. After ``ffi.release(x)``, do not use anything pointed to by ``x`` any longer. As an exception to this rule, you can call ``ffi.release(x)`` several times for the exact same cdata object ``x``; the calls after the first one are ignored. ffi.init_once() +++++++++++++++ **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://foss.heptapod.net/pypy/cffi/-/issues/233 .. _ffi-getctype: .. _ffi-list-types: ffi.getctype(), ffi.list_types() ++++++++++++++++++++++++++++++++ **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.list_types()**: Returns the user type names known to this FFI instance. This returns a tuple containing three lists of names: ``(typedef_names, names_of_structs, names_of_unions)``. *New in version 1.6.* .. _`Preparing and Distributing modules`: cdef.html#loading-libraries 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(), bool() | | and enums | on which int() works | long, depending | `[6]`, | | `[5]` | (but not a float!). | on the type | ``<`` | | | Must be within range. | (ver. 1.10: or a | | | | | bool) | | +---------------+------------------------+------------------+----------------+ | ``char`` | a string of length 1 | a string of | int(), bool(), | | | or another | length 1 | ``<`` | +---------------+------------------------+------------------+----------------+ | ``wchar_t``, | a unicode of length 1 | a unicode of | | | ``char16_t``, | (or maybe 2 if | length 1 | int(), | | ``char32_t`` | surrogates) or | (or maybe 2 if | bool(), ``<`` | | `[8]` | another similar | surrogates) | | +---------------+------------------------+------------------+----------------+ | ``float``, | a float or anything on | a Python float | float(), int(),| | ``double`` | which float() works | | bool(), ``<`` | +---------------+------------------------+------------------+----------------+ |``long double``| another with | a , to | float(), int(),| | | a ``long double``, or | avoid loosing | bool() | | | anything on which | precision `[3]` | | | | float() works | | | +---------------+------------------------+------------------+----------------+ | ``float`` | a complex number | a Python complex | complex(), | | ``_Complex``, | or anything on which | number | bool() | | ``double`` | complex() works | | `[7]` | | ``_Complex`` | | | | +---------------+------------------------+------------------+----------------+ | pointers | another with | a |``[]`` `[4]`, | | | a compatible type (i.e.| |``+``, ``-``, | | | same type | |bool() | | | or ``void*``, or as an | | | | | array instead) `[1]` | | | +---------------+------------------------+ | | | ``void *`` | another with | | | | | 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 `[2]` | +---------------+------------------------+------------------+----------------+ | arrays | a list or tuple of | a |len(), iter(), | | | items | |``[]`` `[4]`, | | | | |``+``, ``-`` | +---------------+------------------------+ +----------------+ | ``char[]``, | same as arrays, or a | | len(), iter(), | | ``un/signed`` | Python byte string | | ``[]``, ``+``, | | ``char[]``, | | | ``-`` | | ``_Bool[]`` | | | | +---------------+------------------------+ +----------------+ |``wchar_t[]``, | same as arrays, or a | | len(), iter(), | |``char16_t[]``,| Python unicode string | | ``[]``, | |``char32_t[]`` | | | ``+``, ``-`` | | | | | | +---------------+------------------------+------------------+----------------+ | 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 | +---------------+------------------------+------------------+----------------+ `[1]` ``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 byte 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}]``. In all these cases (including passing a byte string to a ``char *`` argument), the required C data structure is created just before the call is done, and freed afterwards. As an optimization, CFFI assumes that a function with a ``char *`` argument to which you pass a Python byte string will not actually modify the array of characters passed in, and so it attempts to pass directly a pointer inside the Python byte string object. This still doesn't mean that the ``char *`` argument can be stored by the C function and inspected later. The ``char *`` is only valid for the duration of the call, even if the Python object is kept alive for longer. (On PyPy, this optimization is only available since PyPy 5.4 with CFFI 1.8. It may fail in rare cases and fall back to making a copy anyway, but only for short strings so it shouldn't be noticeable.) If you need to pass a ``char *`` that must be valid for longer than just the call, you need to build it explicitly, either with ``p = ffi.new("char[]", mystring)`` (which makes a copy) or by not using a byte string in the first place but something else like a buffer object, or a bytearray and ``ffi.from_buffer()``; or just use ``ffi.new("char[]", length)`` directly if possible. `[2]` 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.) `[3]` ``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);``. `[4]` 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.) `[5]` 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))``. `[6]` bool() on a primitive cdata: *New in version 1.7.* In previous versions, it only worked on pointers; for primitives it always returned True. *New in version 1.10:* The C type ``_Bool`` or ``bool`` converts to Python booleans now. You get an exception if a C ``_Bool`` happens to contain a value different from 0 and 1 (this case triggers undefined behavior in C; if you really have to interface with a library relying on this, don't use ``_Bool`` in the CFFI side). Also, when converting from a byte string to a ``_Bool[]``, only the bytes ``\x00`` and ``\x01`` are accepted. `[7]` libffi does not support complex numbers: *New in version 1.11:* CFFI now supports complex numbers directly. Note however that libffi does not. This means that C functions that take directly as argument types or return type a complex type cannot be called by CFFI, unless they are directly using the API mode. `[8]` ``wchar_t``, ``char16_t`` and ``char32_t`` See `Unicode character types`_ below. .. _file: Support for 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``. Note, however, that CFFI does this by a best-effort approach. If you need finer control over buffering, flushing, and timely closing of the ``FILE *``, then you should not use this special support for ``FILE *``. Instead, you can handle regular ``FILE *`` cdata objects that you explicitly make using fdopen(), like this: .. code-block:: python ffi.cdef(''' FILE *fdopen(int, const char *); // from the C int fclose(FILE *); ''') myfile.flush() # make sure the file is flushed newfd = os.dup(myfile.fileno()) # make a copy of the file descriptor fp = lib.fdopen(newfd, "w") # make a cdata 'FILE *' around newfd lib.write_stuff_to_file(fp) # invoke the external function lib.fclose(fp) # when you're done, close fp (and newfd) The special support for ``FILE *`` is anyway implemented in a similar manner on CPython 3.x and on PyPy, because these Python implementations' files are not natively based on ``FILE *``. Doing it explicity offers more control. .. _unichar: Unicode character types +++++++++++++++++++++++ The ``wchar_t`` type has the same signedness as the underlying platform's. For example, on Linux, it is a signed 32-bit integer. However, the types ``char16_t`` and ``char32_t`` (*new in version 1.11*) are always unsigned. Note that CFFI assumes that these types are meant to contain UTF-16 or UTF-32 characters in the native endianness. More precisely: * ``char32_t`` is assumed to contain UTF-32, or UCS4, which is just the unicode codepoint; * ``char16_t`` is assumed to contain UTF-16, i.e. UCS2 plus surrogates; * ``wchar_t`` is assumed to contain either UTF-32 or UTF-16 based on its actual platform-defined size of 4 or 2 bytes. Whether this assumption is true or not is unspecified by the C language. In theory, the C library you are interfacing with could use one of these types with a different meaning. You would then need to handle it yourself---for example, by using ``uint32_t`` instead of ``char32_t`` in the ``cdef()``, and building the expected arrays of ``uint32_t`` manually. Python itself can be compiled with ``sys.maxunicode == 65535`` or ``sys.maxunicode == 1114111`` (Python >= 3.3 is always 1114111). This changes the handling of surrogates (which are pairs of 16-bit "characters" which actually stand for a single codepoint whose value is greater than 65535). If your Python is ``sys.maxunicode == 1114111``, then it can store arbitrary unicode codepoints; surrogates are automatically inserted when converting from Python unicodes to UTF-16, and automatically removed when converting back. On the other hand, if your Python is ``sys.maxunicode == 65535``, then it is the other way around: surrogates are removed when converting from Python unicodes to UTF-32, and added when converting back. In other words, surrogate conversion is done only when there is a size mismatch. Note that Python's internal representations is not specified. For example, on CPython >= 3.3, it will use 1- or 2- or 4-bytes arrays depending on what the string actually contains. With CFFI, when you pass a Python byte string to a C function expecting a ``char*``, then we pass directly a pointer to the existing data without needing a temporary buffer; however, the same cannot cleanly be done with *unicode* string arguments and the ``wchar_t*`` / ``char16_t*`` / ``char32_t*`` types, because of the changing internal representation. As a result, and for consistency, CFFI always allocates a temporary buffer for unicode strings. **Warning:** for now, if you use ``char16_t`` and ``char32_t`` with ``set_source()``, you have to make sure yourself that the types are declared by the C source you provide to ``set_source()``. They would be declared if you ``#include`` a library that explicitly uses them, for example, or when using C++11. Otherwise, you need ``#include `` on Linux, or more generally something like ``typedef uint16_t char16_t;``. This is not done automatically by CFFI because ``uchar.h`` is not standard across platforms, and writing a ``typedef`` like above would crash if the type happens to be already defined. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/doc/source/using.rst0000644000175100001770000012036200000000000017244 0ustar00runnerdocker00000000000000================================ 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))``. *New in version 1.11:* in addition to ``wchar_t``, the C types ``char16_t`` and ``char32_t`` work the same but with a known fixed size. In previous versions, this could be achieved using ``uint16_t`` and ``int32_t`` but without automatic conversion to Python unicodes. 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 Usually you don't need a weak dict: for example, to call a function with a ``char * *`` argument that contains a pointer to a ``char *`` pointer, it is enough to do this: .. code-block:: python p = ffi.new("char[]", "hello, world") # p is a 'char *' q = ffi.new("char **", p) # q is a 'char **' lib.myfunction(q) # p is alive at least until here, so that's fine However, this is always wrong (usage of freed memory): .. code-block:: python p = ffi.new("char **", ffi.new("char[]", "hello, world")) # WRONG! as soon as p is built, the inner ffi.new() gets freed! This is wrong too, for the same reason: .. code-block:: python p = ffi.new("struct my_stuff") p.foo = ffi.new("char[]", "hello, world") # WRONG! as soon as p.foo is set, the ffi.new() gets freed! 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). There is `ffi.addressof()`__, but only for some cases. You cannot take the "address" of a number in Python, for example; similarly, you cannot take the address of a CFFI pointer. If you have this kind of C code:: int x, y; fetch_size(&x, &y); opaque_t *handle; // some opaque pointer init_stuff(&handle); // initializes the variable 'handle' more_stuff(handle); // pass the handle around to more functions then you need to rewrite it like this, replacing the variables in C with what is logically pointers to the variables: .. code-block:: python px = ffi.new("int *") py = ffi.new("int *") arr = ffi.new("int[2]") lib.fetch_size(px, py) -OR- lib.fetch_size(arr, arr + 1) x = px[0] x = arr[0] y = py[0] y = arr[1] p_handle = ffi.new("opaque_t **") lib.init_stuff(p_handle) # pass the pointer to the 'handle' pointer handle = p_handle[0] # now we can read 'handle' out of 'p_handle' lib.more_stuff(handle) .. __: ref.html#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 or char16_t or char32_t can be initialized from a unicode string, and calling ``ffi.string()`` on the cdata object returns the current unicode string stored in the source array (adding surrogates if necessary). See the `Unicode character types`__ section for more details. .. __: ref.html#unichar 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 (Note that there is no guarantee that the ``char *`` passed to the function remains valid after the call is done. Similarly, if you write ``lib.f(x); lib.f(x)`` where ``x`` is a variable containing a byte string, the two calls to ``f()`` could sometimes receive different ``char *`` pointers, with each of them only valid during the corresponding call. This is important notably for PyPy which uses many optimizations tweaking the data underlying a byte string object. CFFI will not make and free a copy of the whole string at *every* call---it usually won't---but you *cannot* write code that relies on it: there are cases were that would break. If you need a pointer to remain valid, you need to make one explicitly, for example with ``ptr = ffi.new("char[]", x)``.) You can also pass unicode strings as ``wchar_t *`` or ``char16_t *`` or ``char32_t *`` arguments. Note that the C language makes no difference between argument declarations that use ``type *`` or ``type[]``. For example, ``int *`` is fully equivalent to ``int[]`` (or even ``int[5]``; the 5 is ignored). For CFFI, this means that you can always pass arguments that can be converted to either ``int *`` or ``int[]``. For example: .. code-block:: python # void do_something_with_array(int *array); lib.do_something_with_array([1, 2, 3, 4, 5]) # works for int[] See `Reference: conversions`__ for a similar way to pass ``struct foo_s *`` arguments---but in general, it is clearer in this case to pass ``ffi.new('struct foo_s *', initializer)``. __ ref.html#conversions CFFI supports passing and returning structs and unions 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`: For performance, non-variadic API-level functions that you get by writing ``lib.some_function`` are not ```` objects, but an object of a different type (on CPython, ````). This means you cannot pass them directly 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")``. There are a few (obscure) limitations to the supported argument and return types. These limitations come from libffi and apply only to calling ```` function pointers; in other words, they don't apply to non-variadic ``cdef()``-declared functions if you are using the API mode. The limitations are that you cannot pass directly as argument or return type: * a union (but a *pointer* to a union is fine); * a struct which uses bitfields (but a *pointer* to such a struct is fine); * a struct that was declared with "``...``" in the ``cdef()``. In API mode, you can work around these limitations: for example, if you need to call such a function pointer from Python, you can instead write a custom C function that accepts the function pointer and the real arguments and that does the call from C. Then declare that custom C function in the ``cdef()`` and use it from Python. 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);``.) Memory pressure (PyPy) ---------------------- This paragraph applies only to PyPy, because its garbage collector (GC) is different from CPython's. It is very common in C code to have pairs of functions, one which performs memory allocations or acquires other resources, and the other which frees them again. Depending on how you structure your Python code, the freeing function is only called when the GC decides a particular (Python) object can be freed. This occurs notably in these cases: * If you use a ``__del__()`` method to call the freeing function. * If you use ``ffi.gc()`` without also using ``ffi.release()``. * This does not occur if you call the freeing function at a deterministic time, like in a regular ``try: finally:`` block. It does however occur *inside a generator---* if the generator is not explicitly exhausted but forgotten at a ``yield`` point, then the code in the enclosing ``finally`` block is only invoked at the next GC. In these cases, you may have to use the built-in function ``__pypy__.add_memory_pressure(n)``. Its argument ``n`` is an estimate of how much memory pressure to add. For example, if the pair of C functions that we are talking about is ``malloc(n)`` and ``free()`` or similar, you would call ``__pypy__.add_memory_pressure(n)`` after ``malloc(n)``. Doing so is not always a complete answer to the problem, but it makes the next GC occur earlier, which is often enough. The same applies if the memory allocations are indirect, e.g. the C function allocates some internal data structures. In that case, call ``__pypy__.add_memory_pressure(n)`` with an argument ``n`` that is an rough estimation. Knowing the exact size is not important, and memory pressure doesn't have to be manually brought down again after calling the freeing function. If you are writing wrappers for the allocating / freeing pair of functions, you should probably call ``__pypy__.add_memory_pressure()`` in the former even if the user may invoke the latter at a known point with a ``finally:`` block. In case this solution is not sufficient, or if the acquired resource is not memory but something else more limited (like file descriptors), then there is no better way than restructuring your code to make sure the freeing function is called at a known point and not indirectly by the GC. Note that in PyPy <= 5.6 the discussion above also applies to ``ffi.new()``. In more recent versions of PyPy, both ``ffi.new()`` and ``ffi.new_allocator()()`` automatically account for the memory pressure they create. (In case you need to support both older and newer PyPy's, try calling ``__pypy__.add_memory_pressure()`` anyway; it is better to overestimate than not account for the memory pressure.) .. _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"``:: ffibuilder.cdef(""" extern "Python" int my_callback(int, int); void library_function(int(*callback)(int, int)); """) ffibuilder.set_source("_my_example", r""" #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:: ffibuilder.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 *); """) ffibuilder.set_source("_demo_cffi", r""" #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): print "got event!" @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): print "got event!" @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 declaration. (By default it needs to be static, but see `next paragraph`__.) 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. .. __: `extern-python-c`_ :: ffibuilder.set_source("_demo_cffi", r""" #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):: ffibuilder.cdef(""" extern "Python" int f(int); int my_algo(int); """) ffibuilder.set_source("_example_cffi", r""" 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-c: Extern "Python+C" ~~~~~~~~~~~~~~~~~ Functions declared with ``extern "Python"`` are generated as ``static`` functions in the C source. However, in some cases it is convenient to make them non-static, typically when you want to make them directly callable from other C source files. To do that, you can say ``extern "Python+C"`` instead of just ``extern "Python"``. *New in version 1.6.* +------------------------------------+--------------------------------------+ | if the cdef contains | then CFFI generates | +------------------------------------+--------------------------------------+ | ``extern "Python" int f(int);`` | ``static int f(int) { /* code */ }`` | +------------------------------------+--------------------------------------+ | ``extern "Python+C" int f(int);`` | ``int f(int) { /* code */ }`` | +------------------------------------+--------------------------------------+ The name ``extern "Python+C"`` comes from the fact that we want an extern function in both senses: as an ``extern "Python"``, and as a C function that is not static. You cannot make CFFI generate additional macros or other compiler-specific stuff like the GCC ``__attribute__``. You can only control whether the function should be ``static`` or not. But often, these attributes must be written alongside the function *header*, and it is fine if the function *implementation* does not repeat them:: ffibuilder.cdef(""" extern "Python+C" int f(int); /* not static */ """) ffibuilder.set_source("_example_cffi", r""" /* the forward declaration, setting a gcc attribute (this line could also be in some .h file, to be included both here and in the other C files of the project) */ int f(int) __attribute__((visibility("hidden"))); """) 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://github.com/python-cffi/cffi/blob/main/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 several issues with old-style callbacks: - On less common architecture, libffi is more likely to crash on callbacks (`e.g. on NetBSD`__); - On hardened systems like PAX and SELinux, the extra memory protections can interfere (for example, on SELinux you need to run with ``deny_execmem`` set to ``off``). - `On Mac OS X,`__ you need to give your application the entitlement ``com.apple.security.cs.allow-unsigned-executable-memory``. Note also that a cffi fix for this issue was attempted---see the ``ffi_closure_alloc`` branch---but was not merged because it creates potential `memory corruption`__ with ``fork()``. In other words: yes, it is dangerous to allow write+execute memory in your program; that's why the various "hardening" options above exist. But at the same time, these options open wide the door to another attack: if the program forks and then attempts to call any of the ``ffi.callback()``, then this immediately results in a crash---or, with a minimal amount of work from an attacker, arbitrary code execution. To me it sounds even more dangerous than the original problem, and that's why cffi is not playing along. To fix the issue once and for all on the affected platforms, you need to refactor the involved code so that it no longer uses ``ffi.callback()``. .. __: https://github.com/pyca/pyopenssl/issues/596 .. __: https://foss.heptapod.net/pypy/cffi/-/issues/391 .. __: 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: .. __: ref.html#new-handle .. 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): print "method called!" (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 ffibuilder = cffi.FFI() ffibuilder.cdef(""" int (*python_callback)(int how_many, int *values); void *const c_callback; /* pass this const ptr to C routines */ """) ffibuilder.set_source("_example", r""" #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= 3.8 * Fix two minor memory leaks (thanks Sebastian!) * Like many projects that had an IRC channel on freenode, we moved it to ``irc.libera.chat``. v1.14.5 ======= * Source fix for old gcc versions * This and future releases should include wheels on more platforms, thanks to our new release managers Matt and Matt! v1.14.4 ======= Release done for pip reasons. v1.14.3 ======= Release done for pip reasons. v1.14.2 ======= * CPython 3 on Windows: we again try to compile with ``Py_LIMITED_API`` by default. This flag is not added if you run the compilation with CPython 3.4, as it only works with CPython >= 3.5, but by now this version of Python is quite old (and we no longer distribute cffi wheels for it). This may require that you upgrade ``virtualenv`` (requires version 16 or newer) or at least copy manually ``python3.dll`` into your existing virtualenvs. For distributing wheels with your cffi modules, you may also need to upgrade ``wheel`` to the just-released version 0.35. You can manually disable ``Py_LIMITED_API`` by calling ``ffi.set_source(..., py_limited_api=False)``. v1.14.1 ======= * CFFI source code is now `hosted on Heptapod`_. * Improved support for ``typedef int my_array_t[...];`` with an explicit dot-dot-dot in API mode (`issue #453`_) * Windows (32 and 64 bits): multiple fixes for ABI-mode call to functions that return a structure. * Experimental support for MacOS 11 on aarch64. * and a few other minor changes and bug fixes. .. _`hosted on Heptapod`: https://foss.heptapod.net/pypy/cffi/ .. _`issue #453`: https://foss.heptapod.net/pypy/cffi/issues/453 v1.14 ===== * ``ffi.dlopen()`` can now be called with a handle (as a ``void *``) to an already-opened C library. * CPython only: fixed a stack overflow issue for calls like ``lib.myfunc([large list])``. If the function is declared as taking a ``float *`` argument, for example, then the array is temporarily converted into a C array of floats---however, the code used to use ``alloca()`` for this temporary storage, no matter how large. This is now fixed. The fix concerns all modes: in-line/out-of-line API/ABI. Also note that your API-mode C extension modules need to be regenerated with cffi 1.14 in order to get the fix; i.e. for API mode, the fix is in the generated C sources. (The C sources generated from cffi 1.14 should also work when running in a different environment in which we have an older version of cffi. Also, this change makes no difference on PyPy.) As a workaround that works on all versions of cffi, you can write ``lib.myfunc(ffi.new("float[]", [large list]))``, which is equivalent but explicity builds the intermediate array as a regular Python object on the heap. * fixed a memory leak inside ``ffi.getwinerror()`` on CPython 3.x. v1.13.2 ======= * re-release because the Linux wheels came with an attached version of libffi that was very old and buggy (`issue #432`_). .. _`issue #432`: https://foss.heptapod.net/pypy/cffi/-/issues/432 v1.13.1 ======= * deprecate the way to declare in ``cdef()`` a global variable with only ``void *foo;``. You should always use a storage class, like ``extern void *foo;`` or maybe ``static void *foo;``. These are all equivalent for the purposes of ``cdef()``, but the reason for deprecating the bare version is that (as far as I know) it would always be mistake in a real C header. * fix the regression ``RuntimeError: found a situation in which we try to build a type recursively`` (`issue #429`_). * fixed `issue #427`_ where a multithreading mistake in the embedding logic initialization code would cause deadlocks on CPython 3.7. .. _`issue #429`: https://foss.heptapod.net/pypy/cffi/-/issues/429 .. _`issue #427`: https://foss.heptapod.net/pypy/cffi/-/issues/427 v1.13 ===== * ``ffi.from_buffer("type *", ..)`` is now supported, in addition to ``"type[]"``. You can then write ``p.field`` to access the items, instead of only ``p[0].field``. Be careful that no bounds checking is performed, so ``p[n]`` might access data out of bounds. * fix for structs containing unnamed bitfields like ``int : 1;``. * when calling cdata of "function pointer" type, give a RuntimeError instead of a crash if the pointer happens to be NULL * support some more binary operations between constants in enum definitions (PR #96) * silence a warning incorrectly emitted if you use a quote in a preprocessor line * detect a corner case that would throw the C code into an infinite recursion, with ``ffi.cdef("""struct X { void(*fnptr)(struct X); };""")`` Older Versions ============== v1.12.3 ------- * Fix for nested struct types that end in a var-sized array (#405). * Add support for using ``U`` and ``L`` characters at the end of integer constants in ``ffi.cdef()`` (thanks Guillaume). * More 3.8 fixes. v1.12.2 ------- * Added temporary workaround to compile on CPython 3.8.0a2. v1.12.1 ------- * CPython 3 on Windows: we again no longer compile with ``Py_LIMITED_API`` by default because such modules *still* cannot be used with virtualenv. The problem is that it doesn't work in CPython <= 3.4, and for technical reason we can't enable this flag automatically based on the version of Python. Like before, `Issue #350`_ mentions a workaround if you still want the ``Py_LIMITED_API`` flag and *either* you are not concerned about virtualenv *or* you are sure your module will not be used on CPython <= 3.4: pass ``define_macros=[("Py_LIMITED_API", None)]`` as a keyword to the ``ffibuilder.set_source()`` call. v1.12 ----- * `Direct support for pkg-config`__. * ``ffi.from_buffer()`` takes a new optional *first* argument that gives the array type of the result. It also takes an optional keyword argument ``require_writable`` to refuse read-only Python buffers. * ``ffi.new()``, ``ffi.gc()`` or ``ffi.from_buffer()`` cdata objects can now be released at known times, either by using the ``with`` keyword or by calling the new ``ffi.release()``. * Windows, CPython 3.x: cffi modules are linked with ``python3.dll`` again. This makes them independant on the exact CPython version, like they are on other platforms. **It requires virtualenv 16.0.0.** * Accept an expression like ``ffi.new("int[4]", p)`` if ``p`` is itself another cdata ``int[4]``. * CPython 2.x: ``ffi.dlopen()`` failed with non-ascii file names on Posix * CPython: if a thread is started from C and then runs Python code (with callbacks or with the embedding solution), then previous versions of cffi would contain possible crashes and/or memory leaks. Hopefully, this has been fixed (see `issue #362`_). * Support for ``ffi.cdef(..., pack=N)`` where N is a power of two. Means to emulate ``#pragma pack(N)`` on MSVC. Also, the default on Windows is now ``pack=8``, like on MSVC. This might make a difference in corner cases, although I can't think of one in the context of CFFI. The old way ``ffi.cdef(..., packed=True)`` remains and is equivalent to ``pack=1`` (saying e.g. that fields like ``int`` should be aligned to 1 byte instead of 4). .. __: cdef.html#pkgconfig .. _`issue #362`: https://foss.heptapod.net/pypy/cffi/-/issues/362 v1.11.5 ------- * `Issue #357`_: fix ``ffi.emit_python_code()`` which generated a buggy Python file if you are using a ``struct`` with an anonymous ``union`` field or vice-versa. * Windows: ``ffi.dlopen()`` should now handle unicode filenames. * ABI mode: implemented ``ffi.dlclose()`` for the in-line case (it used to be present only in the out-of-line case). * Fixed a corner case for ``setup.py install --record=xx --root=yy`` with an out-of-line ABI module. Also fixed `Issue #345`_. * More hacks on Windows for running CFFI's own ``setup.py``. * `Issue #358`_: in embedding, to protect against (the rare case of) Python initialization from several threads in parallel, we have to use a spin-lock. On CPython 3 it is worse because it might spin-lock for a long time (execution of ``Py_InitializeEx()``). Sadly, recent changes to CPython make that solution needed on CPython 2 too. * CPython 3 on Windows: we no longer compile with ``Py_LIMITED_API`` by default because such modules cannot be used with virtualenv. `Issue #350`_ mentions a workaround if you still want that and are not concerned about virtualenv: pass ``define_macros=[("Py_LIMITED_API", None)]`` as a keyword to the ``ffibuilder.set_source()`` call. .. _`Issue #345`: https://foss.heptapod.net/pypy/cffi/-/issues/345 .. _`Issue #350`: https://foss.heptapod.net/pypy/cffi/-/issues/350 .. _`Issue #358`: https://foss.heptapod.net/pypy/cffi/-/issues/358 .. _`Issue #357`: https://foss.heptapod.net/pypy/cffi/-/issues/357 v1.11.4 ------- * Windows: reverted linking with ``python3.dll``, because virtualenv does not make this DLL available to virtual environments for now. See `Issue #355`_. On Windows only, the C extension modules created by cffi follow for now the standard naming scheme ``foo.cp36-win32.pyd``, to make it clear that they are regular CPython modules depending on ``python36.dll``. .. _`Issue #355`: https://foss.heptapod.net/pypy/cffi/-/issues/355 v1.11.3 ------- * Fix on CPython 3.x: reading the attributes ``__loader__`` or ``__spec__`` from the cffi-generated lib modules gave a buggy SystemError. (These attributes are always None, and provided only to help compatibility with tools that expect them in all modules.) * More Windows fixes: workaround for MSVC not supporting large literal strings in C code (from ``ffi.embedding_init_code(large_string)``); and an issue with ``Py_LIMITED_API`` linking with ``python35.dll/python36.dll`` instead of ``python3.dll``. * Small documentation improvements. v1.11.2 ------- * Fix Windows issue with managing the thread-state on CPython 3.0 to 3.5 v1.11.1 ------- * Fix tests, remove deprecated C API usage * Fix (hack) for 3.6.0/3.6.1/3.6.2 giving incompatible binary extensions (cpython issue `#29943`_) * Fix for 3.7.0a1+ .. _`#29943`: https://bugs.python.org/issue29943 v1.11 ----- * Support the modern standard types ``char16_t`` and ``char32_t``. These work like ``wchar_t``: they represent one unicode character, or when used as ``charN_t *`` or ``charN_t[]`` they represent a unicode string. The difference with ``wchar_t`` is that they have a known, fixed size. They should work at all places that used to work with ``wchar_t`` (please report an issue if I missed something). Note that with ``set_source()``, you need to make sure that these types are actually defined by the C source you provide (if used in ``cdef()``). * Support the C99 types ``float _Complex`` and ``double _Complex``. Note that libffi doesn't support them, which means that in the ABI mode you still cannot call C functions that take complex numbers directly as arguments or return type. * Fixed a rare race condition when creating multiple ``FFI`` instances from multiple threads. (Note that you aren't meant to create many ``FFI`` instances: in inline mode, you should write ``ffi = cffi.FFI()`` at module level just after ``import cffi``; and in out-of-line mode you don't instantiate ``FFI`` explicitly at all.) * Windows: using callbacks can be messy because the CFFI internal error messages show up to stderr---but stderr goes nowhere in many applications. This makes it particularly hard to get started with the embedding mode. (Once you get started, you can at least use ``@ffi.def_extern(onerror=...)`` and send the error logs where it makes sense for your application, or record them in log files, and so on.) So what is new in CFFI is that now, on Windows CFFI will try to open a non-modal MessageBox (in addition to sending raw messages to stderr). The MessageBox is only visible if the process stays alive: typically, console applications that crash close immediately, but that is also the situation where stderr should be visible anyway. * Progress on support for `callbacks in NetBSD`__. * Functions returning booleans would in some case still return 0 or 1 instead of False or True. Fixed. * `ffi.gc()`__ now takes an optional third parameter, which gives an estimate of the size (in bytes) of the object. So far, this is only used by PyPy, to make the next GC occur more quickly (`issue #320`__). In the future, this might have an effect on CPython too (provided the CPython `issue 31105`__ is addressed). * Add a note to the documentation: the ABI mode gives function objects that are *slower* to call than the API mode does. For some reason it is often thought to be faster. It is not! .. __: https://foss.heptapod.net/pypy/cffi/-/issues/321 .. __: ref.html#ffi-gc .. __: https://foss.heptapod.net/pypy/cffi/-/issues/320 .. __: http://bugs.python.org/issue31105 v1.10.1 ------- (only released inside PyPy 5.8.0) * Fixed the line numbers reported in case of ``cdef()`` errors. Also, I just noticed, but pycparser always supported the preprocessor directive ``# 42 "foo.h"`` to mean "from the next line, we're in file foo.h starting from line 42", which it puts in the error messages. v1.10 ----- * Issue #295: use calloc() directly instead of PyObject_Malloc()+memset() to handle ffi.new() with a default allocator. Speeds up ``ffi.new(large-array)`` where most of the time you never touch most of the array. * Some OS/X build fixes ("only with Xcode but without CLT"). * Improve a couple of error messages: when getting mismatched versions of cffi and its backend; and when calling functions which cannot be called with libffi because an argument is a struct that is "too complicated" (and not a struct *pointer*, which always works). * Add support for some unusual compilers (non-msvc, non-gcc, non-icc, non-clang) * Implemented the remaining cases for ``ffi.from_buffer``. Now all buffer/memoryview objects can be passed. The one remaining check is against passing unicode strings in Python 2. (They support the buffer interface, but that gives the raw bytes behind the UTF16/UCS4 storage, which is most of the times not what you expect. In Python 3 this has been fixed and the unicode strings don't support the memoryview interface any more.) * The C type ``_Bool`` or ``bool`` now converts to a Python boolean when reading, instead of the content of the byte as an integer. The potential incompatibility here is what occurs if the byte contains a value different from 0 and 1. Previously, it would just return it; with this change, CFFI raises an exception in this case. But this case means "undefined behavior" in C; if you really have to interface with a library relying on this, don't use ``bool`` in the CFFI side. Also, it is still valid to use a byte string as initializer for a ``bool[]``, but now it must only contain ``\x00`` or ``\x01``. As an aside, ``ffi.string()`` no longer works on ``bool[]`` (but it never made much sense, as this function stops at the first zero). * ``ffi.buffer`` is now the name of cffi's buffer type, and ``ffi.buffer()`` works like before but is the constructor of that type. * ``ffi.addressof(lib, "name")`` now works also in in-line mode, not only in out-of-line mode. This is useful for taking the address of global variables. * Issue #255: ``cdata`` objects of a primitive type (integers, floats, char) are now compared and ordered by value. For example, ```` compares equal to ``42`` and ```` compares equal to ``b'A'``. Unlike C, ```` does not compare equal to ``ffi.cast("unsigned int", -1)``: it compares smaller, because ``-1 < 4294967295``. * PyPy: ``ffi.new()`` and ``ffi.new_allocator()()`` did not record "memory pressure", causing the GC to run too infrequently if you call ``ffi.new()`` very often and/or with large arrays. Fixed in PyPy 5.7. * Support in ``ffi.cdef()`` for numeric expressions with ``+`` or ``-``. Assumes that there is no overflow; it should be fixed first before we add more general support for arbitrary arithmetic on constants. v1.9 ---- * Structs with variable-sized arrays as their last field: now we track the length of the array after ``ffi.new()`` is called, just like we always tracked the length of ``ffi.new("int[]", 42)``. This lets us detect out-of-range accesses to array items. This also lets us display a better ``repr()``, and have the total size returned by ``ffi.sizeof()`` and ``ffi.buffer()``. Previously both functions would return a result based on the size of the declared structure type, with an assumed empty array. (Thanks andrew for starting this refactoring.) * Add support in ``cdef()/set_source()`` for unspecified-length arrays in typedefs: ``typedef int foo_t[...];``. It was already supported for global variables or structure fields. * I turned in v1.8 a warning from ``cffi/model.py`` into an error: ``'enum xxx' has no values explicitly defined: refusing to guess which integer type it is meant to be (unsigned/signed, int/long)``. Now I'm turning it back to a warning again; it seems that guessing that the enum has size ``int`` is a 99%-safe bet. (But not 100%, so it stays as a warning.) * Fix leaks in the code handling ``FILE *`` arguments. In CPython 3 there is a remaining issue that is hard to fix: if you pass a Python file object to a ``FILE *`` argument, then ``os.dup()`` is used and the new file descriptor is only closed when the GC reclaims the Python file object---and not at the earlier time when you call ``close()``, which only closes the original file descriptor. If this is an issue, you should avoid this automatic convertion of Python file objects: instead, explicitly manipulate file descriptors and call ``fdopen()`` from C (...via cffi). v1.8.3 ------ * When passing a ``void *`` argument to a function with a different pointer type, or vice-versa, the cast occurs automatically, like in C. The same occurs for initialization with ``ffi.new()`` and a few other places. However, I thought that ``char *`` had the same property---but I was mistaken. In C you get the usual warning if you try to give a ``char *`` to a ``char **`` argument, for example. Sorry about the confusion. This has been fixed in CFFI by giving for now a warning, too. It will turn into an error in a future version. v1.8.2 ------ * Issue #283: fixed ``ffi.new()`` on structures/unions with nested anonymous structures/unions, when there is at least one union in the mix. When initialized with a list or a dict, it should now behave more closely like the ``{ }`` syntax does in GCC. v1.8.1 ------ * CPython 3.x: experimental: the generated C extension modules now use the "limited API", which means that, as a compiled .so/.dll, it should work directly on any version of CPython >= 3.2. The name produced by distutils is still version-specific. To get the version-independent name, you can rename it manually to ``NAME.abi3.so``, or use the very recent setuptools 26. * Added ``ffi.compile(debug=...)``, similar to ``python setup.py build --debug`` but defaulting to True if we are running a debugging version of Python itself. v1.8 ---- * Removed the restriction that ``ffi.from_buffer()`` cannot be used on byte strings. Now you can get a ``char *`` out of a byte string, which is valid as long as the string object is kept alive. (But don't use it to *modify* the string object! If you need this, use ``bytearray`` or other official techniques.) * PyPy 5.4 can now pass a byte string directly to a ``char *`` argument (in older versions, a copy would be made). This used to be a CPython-only optimization. v1.7 ---- * ``ffi.gc(p, None)`` removes the destructor on an object previously created by another call to ``ffi.gc()`` * ``bool(ffi.cast("primitive type", x))`` now returns False if the value is zero (including ``-0.0``), and True otherwise. Previously this would only return False for cdata objects of a pointer type when the pointer is NULL. * bytearrays: ``ffi.from_buffer(bytearray-object)`` is now supported. (The reason it was not supported was that it was hard to do in PyPy, but it works since PyPy 5.3.) To call a C function with a ``char *`` argument from a buffer object---now including bytearrays---you write ``lib.foo(ffi.from_buffer(x))``. Additionally, this is now supported: ``p[0:length] = bytearray-object``. The problem with this was that a iterating over bytearrays gives *numbers* instead of *characters*. (Now it is implemented with just a memcpy, of course, not actually iterating over the characters.) * C++: compiling the generated C code with C++ was supposed to work, but failed if you make use the ``bool`` type (because that is rendered as the C ``_Bool`` type, which doesn't exist in C++). * ``help(lib)`` and ``help(lib.myfunc)`` now give useful information, as well as ``dir(p)`` where ``p`` is a struct or pointer-to-struct. v1.6 ---- * `ffi.list_types()`_ * `ffi.unpack()`_ * `extern "Python+C"`_ * in API mode, ``lib.foo.__doc__`` contains the C signature now. On CPython you can say ``help(lib.foo)``, but for some reason ``help(lib)`` (or ``help(lib.foo)`` on PyPy) is still useless; I haven't yet figured out the hacks needed to convince ``pydoc`` to show more. (You can use ``dir(lib)`` but it is not most helpful.) * Yet another attempt at robustness of ``ffi.def_extern()`` against CPython's interpreter shutdown logic. .. _`ffi.list_types()`: ref.html#ffi-list-types .. _`ffi.unpack()`: ref.html#ffi-unpack .. _`extern "Python+C"`: using.html#extern-python-c 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 .. __: ref.html#ffi-init-once .. __: ref.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()`: ref.html#ffi-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 .. __: ref.html#ffi-new-allocator 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/pyproject.toml0000644000175100001770000000033600000000000016232 0ustar00runnerdocker00000000000000[build-system] requires = [ # first version that supports Python 3.12; older versions may work # with previous Python versions, but are not tested "setuptools >= 66.1" ] build-backend = "setuptools.build_meta" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1677678 cffi-1.16.0/setup.cfg0000644000175100001770000000071000000000000015133 0ustar00runnerdocker00000000000000[metadata] license_file = LICENSE license_files = LICENSE project_urls = Documentation = http://cffi.readthedocs.org/ Source Code = https://github.com/python-cffi/cffi Issue Tracker = https://github.com/python-cffi/cffi/issues Changelog = https://cffi.readthedocs.io/en/latest/whatsnew.html Downloads = https://github.com/python-cffi/cffi/releases Contact = https://groups.google.com/forum/#!forum/python-cffi [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/setup.py0000644000175100001770000002211000000000000015022 0ustar00runnerdocker00000000000000import sys, os, platform import subprocess import errno # the setuptools distutils shim should make distutils available, but this will definitely do # it, since setuptools is now required at build-time import setuptools sources = ['src/c/_cffi_backend.c'] libraries = ['ffi'] include_dirs = ['/usr/include/ffi', '/usr/include/libffi'] # may be changed by pkg-config define_macros = [('FFI_BUILDING', '1')] # for linking with libffi static library 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 no_compiler_found = False def no_working_compiler_found(): sys.stderr.write(""" No working compiler found, or bogus compiler options passed to the compiler from Python's standard "distutils" module. See the error messages above. Likely, the problem is not related to CFFI but generic to the setup.py of any Python package that tries to compile C code. (Hints: on OS/X 10.8, for errors about -mno-fused-madd see http://stackoverflow.com/questions/22313407/ Otherwise, see https://wiki.python.org/moin/CompLangPython or the IRC channel #python on irc.libera.chat.) Trying to continue anyway. If you are trying to install CFFI from a build done in a different context, you can ignore this warning. \n""") global no_compiler_found no_compiler_found = True def get_config(): 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') return config def ask_supports_thread(): config = get_config() ok = (sys.platform != 'win32' and 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() else: sys.stderr.write("Note: will not use '__thread' in the C code\n") _safe_to_ignore() def ask_supports_sync_synchronize(): if sys.platform == 'win32' or no_compiler_found: return config = get_config() ok = config.try_link('int main(void) { __sync_synchronize(); return 0; }') if ok: define_macros.append(('HAVE_SYNC_SYNCHRONIZE', None)) else: sys.stderr.write("Note: will not use '__sync_synchronize()'" " in the C code\n") _safe_to_ignore() def _safe_to_ignore(): sys.stderr.write("***** The above error message can be safely ignored.\n\n") def uses_msvc(): config = get_config() return config.try_compile('#ifndef _MSC_VER\n#error "not MSVC"\n#endif') 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" and uses_msvc(): if platform.machine() == "ARM64": include_dirs.append(os.path.join("src/c/libffi_arm64/include")) library_dirs.append(os.path.join("src/c/libffi_arm64")) else: COMPILE_LIBFFI = 'src/c/libffi_x86_x64' # from the CPython distribution 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() ask_supports_sync_synchronize() if 'darwin' in sys.platform: # priority is given to `pkg_config`, but always fall back on SDK's libffi. extra_compile_args += ['-iwithsysroot/usr/include/ffi'] if 'freebsd' in sys.platform: include_dirs.append('/usr/local/include') library_dirs.append('/usr/local/lib') forced_extra_objs = os.environ.get('CFFI_FORCE_STATIC', []) if forced_extra_objs: forced_extra_objs = forced_extra_objs.split(';') 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) install_requires = [] if cpython: install_requires.append('pycparser') 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.16.0', python_requires='>=3.8', packages=['cffi'] if cpython else [], package_dir={"": "src"}, package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', '_embedding.h', '_cffi_errors.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, extra_objects=forced_extra_objs, )] if cpython else [], install_requires=install_requires, entry_points = { "distutils.setup_keywords": [ "cffi_modules = cffi.setuptools_ext:cffi_modules", ], }, classifiers=[ 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'License :: OSI Approved :: MIT License', ], ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/setup_base.py0000644000175100001770000000162200000000000016021 0ustar00runnerdocker00000000000000import 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1157675 cffi-1.16.0/src/0000755000175100001770000000000000000000000014103 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1397676 cffi-1.16.0/src/c/0000755000175100001770000000000000000000000014325 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/_cffi_backend.c0000644000175100001770000103404100000000000017211 0ustar00runnerdocker00000000000000#define PY_SSIZE_T_CLEAN #include #include "structmember.h" #define CFFI_VERSION "1.16.0" #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 /* Convert from closure pointer to function pointer. */ #if defined(__hppa__) && !defined(__LP64__) #define CFFI_CLOSURE_TO_FNPTR(type, f) ((type)((unsigned int)(f) | 2)) #else #define CFFI_CLOSURE_TO_FNPTR(type, f) ((type)(f)) #endif /* Define the following macro ONLY if you trust libffi's version of * ffi_closure_alloc() more than the code in malloc_closure.h. * IMPORTANT: DO NOT ENABLE THIS ON LINUX, unless you understand exactly * why I recommend against it and decide that you trust it more than my * analysis below. * * There are two versions of this code: one inside libffi itself, and * one inside malloc_closure.h here. Both should be fine as long as the * Linux distribution does _not_ enable extra security features. If it * does, then the code in malloc_closure.h will cleanly crash because * there is no reasonable way to obtain a read-write-execute memory * page. On the other hand, the code in libffi will appear to * work---but will actually randomly crash after a fork() if the child * does not immediately call exec(). This second crash is of the kind * that can be turned into an attack vector by a motivated attacker. * So, _enabling_ extra security features _opens_ an attack vector. * That sounds like a horribly bad idea to me, and is the reason for why * I prefer CFFI crashing cleanly. * * Currently, we use libffi's ffi_closure_alloc() on NetBSD. It is * known that on the NetBSD kernel, a different strategy is used which * should not be open to the fork() bug. * * This is also used on macOS, provided we are executing on macOS 10.15 or * above. It's a mess because it needs runtime checks in that case. */ #ifdef __NetBSD__ # define CFFI_CHECK_FFI_CLOSURE_ALLOC 1 # define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 1 # define CFFI_CHECK_FFI_PREP_CLOSURE_LOC 1 # define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 1 # define CFFI_CHECK_FFI_PREP_CIF_VAR 0 # define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 0 #elif defined(__APPLE__) && defined(FFI_AVAILABLE_APPLE) # define CFFI_CHECK_FFI_CLOSURE_ALLOC __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *) # define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 1 # define CFFI_CHECK_FFI_PREP_CLOSURE_LOC __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *) # define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 1 # define CFFI_CHECK_FFI_PREP_CIF_VAR __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *) # define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 1 #else # define CFFI_CHECK_FFI_CLOSURE_ALLOC 0 # define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 0 # define CFFI_CHECK_FFI_PREP_CLOSURE_LOC 0 # define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 0 # define CFFI_CHECK_FFI_PREP_CIF_VAR 0 # define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 0 #endif /* always includes this, even if it turns out not to be used on NetBSD because calls are behind "if (0)" */ #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 # if PY_VERSION_HEX >= 0x03030000 # define PyText_GetSize PyUnicode_GetLength # else # define PyText_GetSize PyUnicode_GetSize # endif # 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 #if PY_VERSION_HEX < 0x030900a4 # define Py_SET_REFCNT(obj, val) (Py_REFCNT(obj) = (val)) #endif #if PY_VERSION_HEX >= 0x03080000 # define USE_WRITEUNRAISABLEMSG #endif /************************************************************/ /* base type flag: exactly one of the following: */ #define CT_PRIMITIVE_SIGNED 0x001 /* signed integer */ #define CT_PRIMITIVE_UNSIGNED 0x002 /* unsigned integer */ #define CT_PRIMITIVE_CHAR 0x004 /* char, wchar_t, charN_t */ #define CT_PRIMITIVE_FLOAT 0x008 /* float, double, long double */ #define CT_POINTER 0x010 /* pointer, excluding ptr-to-func */ #define CT_ARRAY 0x020 /* array */ #define CT_STRUCT 0x040 /* struct */ #define CT_UNION 0x080 /* union */ #define CT_FUNCTIONPTR 0x100 /* pointer to function */ #define CT_VOID 0x200 /* void */ #define CT_PRIMITIVE_COMPLEX 0x400 /* float _Complex, double _Complex */ /* other flags that may also be set in addition to the base flag: */ #define CT_IS_VOIDCHAR_PTR 0x00001000 #define CT_PRIMITIVE_FITS_LONG 0x00002000 #define CT_IS_OPAQUE 0x00004000 #define CT_IS_ENUM 0x00008000 #define CT_IS_PTR_TO_OWNED 0x00010000 /* only owned if CDataOwning_Type */ #define CT_CUSTOM_FIELD_POS 0x00020000 #define CT_IS_LONGDOUBLE 0x00040000 #define CT_IS_BOOL 0x00080000 #define CT_IS_FILE 0x00100000 #define CT_IS_VOID_PTR 0x00200000 #define CT_WITH_VAR_ARRAY 0x00400000 /* with open-ended array, anywhere */ /* unused 0x00800000 */ #define CT_LAZY_FIELD_LIST 0x01000000 #define CT_WITH_PACKED_CHANGE 0x02000000 #define CT_IS_SIGNED_WCHAR 0x04000000 #define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \ CT_PRIMITIVE_UNSIGNED | \ CT_PRIMITIVE_CHAR | \ CT_PRIMITIVE_FLOAT | \ CT_PRIMITIVE_COMPLEX) 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; unsigned char cf_flags; /* BF_... */ struct cfieldobject_s *cf_next; } CFieldObject; #define BS_REGULAR (-1) /* a regular field, not with bitshift */ #define BS_EMPTY_ARRAY (-2) /* a field declared 'type[0]' or 'type[]' */ #define BF_IGNORE_IN_CTOR 0x01 /* union field not in the first place */ static PyTypeObject CTypeDescr_Type; static PyTypeObject CField_Type; static PyTypeObject CData_Type; static PyTypeObject CDataOwning_Type; static PyTypeObject CDataOwningGC_Type; static PyTypeObject CDataFromBuf_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) == &CDataFromBuf_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; /* for ffi.new_handle() or ffi.new("struct *") */ } CDataObject_own_structptr; typedef struct { CDataObject head; Py_ssize_t length; /* same as CDataObject_own_length up to here */ Py_buffer *bufferview; } CDataObject_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 { CDataObject head; ffi_closure *closure; } CDataObject_closure; 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; #define ADD_WRAPAROUND(x, y) ((Py_ssize_t)(((size_t)(x)) + ((size_t)(y)))) #define MUL_WRAPAROUND(x, y) ((Py_ssize_t)(((size_t)(x)) * ((size_t)(y)))) /* 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 PyUnicode_KIND /* Python >= 3.3 */ # include "wchar_helper_3.h" #else # include "wchar_helper.h" #endif #include "../cffi/_cffi_errors.h" 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_SET_REFCNT(ct, 43); PyDict_DelItem(unique_cache, ct->ct_unique_key); assert(Py_REFCNT(ct) == 42); Py_SET_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 */ /* returns 0 if the struct ctype is opaque, 1 if it is not, or -1 if an exception occurs */ #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.CType", 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}, {"flags", T_UBYTE, OFF(cf_flags), 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; } #ifdef __GNUC__ /* This is a workaround for what I think is a GCC bug on several platforms. See issue #378. */ __attribute__((noinline)) #endif void _cffi_memcpy(char *target, const void *src, size_t size) { memcpy(target, src, size); } #define _write_raw_data(type) \ do { \ if (size == sizeof(type)) { \ type r = (type)source; \ _cffi_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 Py_complex read_raw_complex_data(char *target, int size) { Py_complex r = {0.0, 0.0}; if (size == 2*sizeof(float)) { float real_part, imag_part; memcpy(&real_part, target + 0, sizeof(float)); memcpy(&imag_part, target + sizeof(float), sizeof(float)); r.real = real_part; r.imag = imag_part; return r; } if (size == 2*sizeof(double)) { memcpy(&r, target, 2*sizeof(double)); return r; } Py_FatalError("read_raw_complex_data: bad complex size"); return r; } 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); } #define _write_raw_complex_data(type) \ do { \ if (size == 2*sizeof(type)) { \ type r = (type)source.real; \ type i = (type)source.imag; \ _cffi_memcpy(target, &r, sizeof(type)); \ _cffi_memcpy(target+sizeof(type), &i, sizeof(type)); \ return; \ } \ } while(0) static void write_raw_complex_data(char *target, Py_complex source, int size) { _write_raw_complex_data(float); _write_raw_complex_data(double); Py_FatalError("write_raw_complex_data: bad complex size"); } 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 PyObject * new_sized_cdata(char *data, CTypeDescrObject *ct, Py_ssize_t length) { CDataObject_own_length *scd; 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 = data; scd->head.c_weakreflist = NULL; scd->length = length; return (PyObject *)scd; } 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) { if (ct->ct_flags & CT_IS_BOOL) { PyObject *x; switch ((int)value) { case 0: x = Py_False; break; case 1: x = Py_True; break; default: PyErr_Format(PyExc_ValueError, "got a _Bool of value %d, expected 0 or 1", (int)value); return NULL; } Py_INCREF(x); return x; } 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)*/ switch (ct->ct_size) { case sizeof(char): return PyBytes_FromStringAndSize(data, 1); case 2: return _my_PyUnicode_FromChar16((cffi_char16_t *)data, 1); case 4: return _my_PyUnicode_FromChar32((cffi_char32_t *)data, 1); } } else if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { Py_complex value = read_raw_complex_data(data, ct->ct_size); return PyComplex_FromCComplex(value); } 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; } static cffi_char16_t _convert_to_char16_t(PyObject *init) { char err_got[80]; err_got[0] = 0; if (PyUnicode_Check(init)) { cffi_char16_t ordinal; if (_my_PyUnicode_AsSingleChar16(init, &ordinal, err_got) == 0) return ordinal; } if (CData_Check(init) && (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && (((CDataObject *)init)->c_type->ct_size == 2)) { char *data = ((CDataObject *)init)->c_data; /*READ(data, 2)*/ return *(cffi_char16_t *)data; } PyErr_Format(PyExc_TypeError, "initializer for ctype 'char16_t' must be a unicode string " "of length 1, not %.200s", err_got[0] == 0 ? Py_TYPE(init)->tp_name : err_got); return (cffi_char16_t)-1; } static cffi_char32_t _convert_to_char32_t(PyObject *init) { char err_got[80]; err_got[0] = 0; if (PyUnicode_Check(init)) { cffi_char32_t ordinal; if (_my_PyUnicode_AsSingleChar32(init, &ordinal, err_got) == 0) return ordinal; } if (CData_Check(init) && (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && (((CDataObject *)init)->c_type->ct_size == 4)) { char *data = ((CDataObject *)init)->c_data; /*READ(data, 4)*/ return *(cffi_char32_t *)data; } PyErr_Format(PyExc_TypeError, "initializer for ctype 'char32_t' must be a unicode string " "of length 1, not %.200s", err_got[0] == 0 ? Py_TYPE(init)->tp_name : err_got); return (cffi_char32_t)-1; } static int _convert_error(PyObject *init, CTypeDescrObject *ct, const char *expected) { if (CData_Check(init)) { CTypeDescrObject *ct2 = ((CDataObject *)init)->c_type; if (strcmp(ct->ct_name, ct2->ct_name) != 0) PyErr_Format(PyExc_TypeError, "initializer for ctype '%s' must be a %s, " "not cdata '%s'", ct->ct_name, expected, ct2->ct_name); else if (ct != ct2) { /* 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->ct_name, ct2->ct_name); } else { PyErr_Format(PyExc_SystemError, "initializer for ctype '%s' is correct, but we get " "an internal mismatch--please report a bug", ct->ct_name); } } else PyErr_Format(PyExc_TypeError, "initializer for ctype '%s' must be a %s, " "not %.200s", ct->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(CTypeDescrObject *ctitem, 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 */ int length; if (ctitem->ct_size == 2) length = _my_PyUnicode_SizeAsChar16(value); else length = _my_PyUnicode_SizeAsChar32(value); return length + 1; } else { Py_ssize_t explicitlength; explicitlength = PyNumber_AsSsize_t(value, PyExc_OverflowError); if (explicitlength < 0) { if (PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_TypeError)) PyErr_Format(PyExc_TypeError, "expected new array length or list/tuple/str, " "not %.200s", Py_TYPE(value)->tp_name); } else 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 add_varsize_length(Py_ssize_t offset, Py_ssize_t itemsize, Py_ssize_t varsizelength, Py_ssize_t *optvarsize) { /* update '*optvarsize' to account for an array of 'varsizelength' elements, each of size 'itemsize', that starts at 'offset'. */ Py_ssize_t size = ADD_WRAPAROUND(offset, MUL_WRAPAROUND(itemsize, varsizelength)); if (size < 0 || ((size - offset) / itemsize) != varsizelength) { PyErr_SetString(PyExc_OverflowError, "array size would overflow a Py_ssize_t"); return -1; } if (size > *optvarsize) *optvarsize = size; return 0; } static int convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init, Py_ssize_t *optvarsize); /* forward */ 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( cf->cf_type->ct_itemdescr, &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 */ assert(data == NULL); return add_varsize_length(cf->cf_offset, cf->cf_type->ct_itemdescr->ct_size, varsizelength, optvarsize); } /* 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 if ((cf->cf_type->ct_flags & CT_WITH_VAR_ARRAY) != 0 && !CData_Check(value)) { Py_ssize_t subsize = cf->cf_type->ct_size; if (convert_struct_from_object(NULL, cf->cf_type, value, &subsize) < 0) return -1; return add_varsize_length(cf->cf_offset, 1, subsize, optvarsize); } else return 0; } static int must_be_array_of_zero_or_one(const char *data, Py_ssize_t n) { Py_ssize_t i; for (i = 0; i < n; i++) { if (((unsigned char)data[i]) > 1) { PyErr_SetString(PyExc_ValueError, "an array of _Bool can only contain \\x00 or \\x01"); return -1; } } 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 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); if (ctitem->ct_flags & CT_IS_BOOL) if (must_be_array_of_zero_or_one(srcdata, n) < 0) return -1; memcpy(data, srcdata, n); return 0; } else { Py_ssize_t n; if (!PyUnicode_Check(init)) { expected = "unicode or list or tuple"; goto cannot_convert; } if (ctitem->ct_size == 4) n = _my_PyUnicode_SizeAsChar32(init); else n = _my_PyUnicode_SizeAsChar16(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++; if (ctitem->ct_size == 4) return _my_PyUnicode_AsChar32(init, (cffi_char32_t *)data, n); else return _my_PyUnicode_AsChar16(init, (cffi_char16_t *)data, n); } } else { expected = "list or tuple"; goto cannot_convert; } cannot_convert: if ((ct->ct_flags & CT_ARRAY) && CData_Check(init)) { CDataObject *cd = (CDataObject *)init; if (cd->c_type == ct) { Py_ssize_t n = get_array_length(cd); memcpy(data, cd->c_data, n * ctitem->ct_size); return 0; } } return _convert_error(init, ct, expected); } static int convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init, Py_ssize_t *optvarsize) { /* does not accept 'init' being already a CData */ 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 (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; icf_flags & BF_IGNORE_IN_CTOR)) cf = cf->cf_next; if (cf == NULL) { PyErr_Format(PyExc_ValueError, "too many initializers for '%s' (got %zd)", ct->ct_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, 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) { int combined_flags = ct->ct_flags | ctinit->ct_flags; if (combined_flags & CT_IS_VOID_PTR) ; /* accept "void *" as either source or target */ else if (combined_flags & CT_IS_VOIDCHAR_PTR) { /* for backward compatibility, accept "char *" as either source of target. This is not what C does, though, so emit a warning that will eventually turn into an error. The warning is turned off if both types are pointers to single bytes. */ char *msg = (ct->ct_flags & CT_IS_VOIDCHAR_PTR ? "implicit cast to 'char *' from a different pointer type: " "will be forbidden in the future (check that the types " "are as you expect; use an explicit ffi.cast() if they " "are correct)" : "implicit cast from 'char *' to a different pointer type: " "will be forbidden in the future (check that the types " "are as you expect; use an explicit ffi.cast() if they " "are correct)"); if ((ct->ct_flags & ctinit->ct_flags & CT_POINTER) && ct->ct_itemdescr->ct_size == 1 && ctinit->ct_itemdescr->ct_size == 1) { /* no warning */ } else if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) return -1; } 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 > 1ULL) /* value != 0 && value != 1 */ goto overflow; } else { 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) { switch (ct->ct_size) { case sizeof(char): { int res = _convert_to_char(init); if (res < 0) return -1; data[0] = res; return 0; } case 2: { cffi_char16_t res = _convert_to_char16_t(init); if (res == (cffi_char16_t)-1 && PyErr_Occurred()) return -1; *(cffi_char16_t *)data = res; return 0; } case 4: { cffi_char32_t res = _convert_to_char32_t(init); if (res == (cffi_char32_t)-1 && PyErr_Occurred()) return -1; *(cffi_char32_t *)data = res; return 0; } } } 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); } if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { Py_complex value = PyComplex_AsCComplex(init); if (PyErr_Occurred()) return -1; write_raw_complex_data(data, value, ct->ct_size); return 0; } 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, 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 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) { /* for ffi.new("struct *") */ 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) { 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 = ((CDataObject_closure *)cd)->closure; PyObject *args = (PyObject *)(closure->user_data); Py_XDECREF(args); #if CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE if (CFFI_CHECK_FFI_CLOSURE_ALLOC) { ffi_closure_free(closure); } else #endif cffi_closure_free(closure); } else { Py_FatalError("cdata CDataOwningGC_Type with unexpected type flags"); } cdata_dealloc(cd); } static void cdatafrombuf_dealloc(CDataObject *cd) { Py_buffer *view = ((CDataObject_frombuf *)cd)->bufferview; PyObject_GC_UnTrack(cd); cdata_dealloc(cd); PyBuffer_Release(view); PyObject_Free(view); } 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 = ((CDataObject_closure *)cd)->closure; PyObject *args = (PyObject *)(closure->user_data); Py_VISIT(args); } return 0; } static int cdatafrombuf_traverse(CDataObject *cd, visitproc visit, void *arg) { Py_buffer *view = ((CDataObject_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 = ((CDataObject_closure *)cd)->closure; PyObject *args = (PyObject *)(closure->user_data); closure->user_data = NULL; Py_XDECREF(args); } return 0; } static int cdatafrombuf_clear(CDataObject *cd) { Py_buffer *view = ((CDataObject_frombuf *)cd)->bufferview; PyBuffer_Release(view); return 0; } /* forward */ static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb, 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 { PyObject *t, *v, *tb; PyErr_Fetch(&t, &v, &tb); /* Don't use error capture here, because it is very much * like errors at __del__(), and these ones are not captured * either */ /* ecap = _cffi_start_error_capture(); */ _my_PyErr_WriteUnraisable(t, v, tb, "From callback for ffi.gc ", origobj, NULL); /* _cffi_stop_error_capture(ecap); */ } Py_DECREF(destructor); /* Restore the saved exception */ PyErr_Restore(error_type, error_value, error_traceback); } Py_XDECREF(origobj); } 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); } static void cdatagcp_dealloc(CDataObject_gcp *cd) { PyObject *destructor = cd->destructor; PyObject *origobj = cd->origobj; PyObject_GC_UnTrack(cd); 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 Py_ssize_t _cdata_var_byte_size(CDataObject *cd) { /* If 'cd' is a 'struct foo' or 'struct foo *' allocated with ffi.new(), and if the struct foo contains a varsize array, then return the real allocated size. Otherwise, return -1. */ if (!CDataOwn_Check(cd)) return -1; if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { cd = (CDataObject *)((CDataObject_own_structptr *)cd)->structobj; } if (cd->c_type->ct_flags & CT_WITH_VAR_ARRAY) { return ((CDataObject_own_length *)cd)->length; } return -1; } static PyObject *_frombuf_repr(CDataObject *cd, const char *cd_type_name) { Py_buffer *view = ((CDataObject_frombuf *)cd)->bufferview; const char *obj_tp_name; if (view->obj == NULL) { return PyText_FromFormat( "", cd_type_name); } obj_tp_name = Py_TYPE(view->obj)->tp_name; if (cd->c_type->ct_flags & CT_ARRAY) { Py_ssize_t buflen = get_array_length(cd); return PyText_FromFormat( "", cd_type_name, buflen, obj_tp_name); } else { return PyText_FromFormat( "", cd_type_name, obj_tp_name); } } static Py_ssize_t cdataowning_size_bytes(CDataObject *cd) { Py_ssize_t size = _cdata_var_byte_size(cd); if (size < 0) { 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 size; } static PyObject *cdataowning_repr(CDataObject *cd) { Py_ssize_t size = cdataowning_size_bytes(cd); 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 */ ffi_closure *closure = ((CDataObject_closure *)cd)->closure; PyObject *args = (PyObject *)closure->user_data; if (args == NULL) return cdata_repr(cd); else return _cdata_repr2(cd, "calling", PyTuple_GET_ITEM(args, 1)); } return cdataowning_repr(cd); /* but should be unreachable */ } static PyObject *cdatafrombuf_repr(CDataObject *cd) { return _frombuf_repr(cd, cd->c_type->ct_name); } static int cdata_nonzero(CDataObject *cd) { if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) { if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_UNSIGNED | CT_PRIMITIVE_CHAR)) return read_raw_unsigned_data(cd->c_data, cd->c_type->ct_size) != 0; if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) return read_raw_longdouble_data(cd->c_data) != 0.0; return read_raw_float_data(cd->c_data, cd->c_type->ct_size) != 0.0; } if (cd->c_type->ct_flags & CT_PRIMITIVE_COMPLEX) { Py_complex value = read_raw_complex_data(cd->c_data, cd->c_type->ct_size); return value.real != 0.0 || value.imag != 0.0; } } 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)) { PyObject *result = convert_to_object(cd->c_data, cd->c_type); if (result != NULL && PyBool_Check(result)) result = PyInt_FromLong(PyInt_AsLong(result)); return result; } else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) { /*READ(cd->c_data, cd->c_type->ct_size)*/ switch (cd->c_type->ct_size) { case sizeof(char): return PyInt_FromLong((unsigned char)cd->c_data[0]); case 2: return PyInt_FromLong((long)*(cffi_char16_t *)cd->c_data); case 4: if (cd->c_type->ct_flags & CT_IS_SIGNED_WCHAR) return PyInt_FromLong((long)*(int32_t *)cd->c_data); else if (sizeof(long) > 4) return PyInt_FromLong(*(uint32_t *)cd->c_data); else return PyLong_FromUnsignedLong(*(uint32_t *)cd->c_data); } } 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 v_is_ptr, w_is_ptr; PyObject *pyres; assert(CData_Check(v)); /* Comparisons involving a primitive cdata work differently than * comparisons involving a struct/array/pointer. * * If v or w is a struct/array/pointer, then the other must be too * (otherwise we return NotImplemented and leave the case to * Python). If both are, then we compare the addresses. * * If v and/or w is a primitive cdata, then we convert the cdata(s) * to regular Python objects and redo the comparison there. */ v_is_ptr = !(((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY); w_is_ptr = CData_Check(w) && !(((CDataObject *)w)->c_type->ct_flags & CT_PRIMITIVE_ANY); if (v_is_ptr && w_is_ptr) { int res; char *v_cdata = ((CDataObject *)v)->c_data; char *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; } else if (v_is_ptr || w_is_ptr) { pyres = Py_NotImplemented; } else { PyObject *aa[2]; int i; aa[0] = v; Py_INCREF(v); aa[1] = w; Py_INCREF(w); pyres = NULL; for (i = 0; i < 2; i++) { v = aa[i]; if (!CData_Check(v)) continue; w = convert_to_object(((CDataObject *)v)->c_data, ((CDataObject *)v)->c_type); if (w == NULL) goto error; if (CData_Check(w)) { Py_DECREF(w); PyErr_Format(PyExc_NotImplementedError, "cannot use in a comparison", ((CDataObject *)v)->c_type->ct_name); goto error; } aa[i] = w; Py_DECREF(v); } pyres = PyObject_RichCompare(aa[0], aa[1], op); error: Py_DECREF(aa[1]); Py_DECREF(aa[0]); return pyres; } Py_INCREF(pyres); return pyres; } #if PY_MAJOR_VERSION < 3 typedef long Py_hash_t; #endif static Py_hash_t cdata_hash(PyObject *v) { if (((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY) { PyObject *vv = convert_to_object(((CDataObject *)v)->c_data, ((CDataObject *)v)->c_type); if (vv == NULL) return -1; if (!CData_Check(vv)) { Py_hash_t hash = PyObject_Hash(vv); Py_DECREF(vv); return hash; } Py_DECREF(vv); } return _Py_HashPointer(((CDataObject *)v)->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"); 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"); 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) { char *cdata; Py_ssize_t bounds[2]; 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; cdata = cd->c_data + ct->ct_itemdescr->ct_size * bounds[0]; return new_sized_cdata(cdata, ct, bounds[1]); } 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" or bytearray, which also adds support for Python 3: otherwise, you get integers while enumerating the string, and you can't set them to characters :-/ */ if ((ct->ct_flags & CT_PRIMITIVE_CHAR) && itemsize == sizeof(char)) { char *src; Py_ssize_t srclen; if (PyBytes_Check(v)) { srclen = PyBytes_GET_SIZE(v); src = PyBytes_AS_STRING(v); } else if (PyByteArray_Check(v)) { srclen = PyByteArray_GET_SIZE(v); src = PyByteArray_AS_STRING(v); } else goto other_types; if (srclen != length) { PyErr_Format(PyExc_ValueError, "need a string of length %zd, got %zd", length, srclen); return -1; } memcpy(cdata, src, length); return 0; } other_types: 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; diff = cdv->c_data - cdw->c_data; if (itemsize > 1) { if (diff % itemsize) { PyErr_SetString(PyExc_ValueError, "pointer subtraction: the distance between the two " "pointers is not a multiple of the item size"); return NULL; } diff = diff / 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 void _cdata_attr_errmsg(char *errmsg, CDataObject *cd, PyObject *attr) { const char *text; if (!PyErr_ExceptionMatches(PyExc_AttributeError)) return; PyErr_Clear(); text = PyText_AsUTF8(attr); if (text == NULL) return; PyErr_Format(PyExc_AttributeError, errmsg, cd->c_type->ct_name, text); } static PyObject * cdata_getattro(CDataObject *cd, PyObject *attr) { CFieldObject *cf; CTypeDescrObject *ct = cd->c_type; char *errmsg = "cdata '%s' has no attribute '%s'"; PyObject *x; 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; Py_ssize_t array_len, size; if (cf->cf_bitshift == BS_REGULAR) { return convert_to_object(data, cf->cf_type); } else if (cf->cf_bitshift != BS_EMPTY_ARRAY) { return convert_to_object_bitfield(data, cf); } /* variable-length array: */ /* if reading variable length array from variable length struct, calculate array type from allocated length */ size = _cdata_var_byte_size(cd) - cf->cf_offset; if (size >= 0) { array_len = size / cf->cf_type->ct_itemdescr->ct_size; return new_sized_cdata(data, cf->cf_type, array_len); } return new_simple_cdata(data, (CTypeDescrObject *)cf->cf_type->ct_stuff); } errmsg = "cdata '%s' has no field '%s'"; break; case -1: return NULL; default: errmsg = "cdata '%s' points to an opaque type: cannot read fields"; break; } } x = PyObject_GenericGetAttr((PyObject *)cd, attr); if (x == NULL) _cdata_attr_errmsg(errmsg, cd, attr); return x; } static int cdata_setattro(CDataObject *cd, PyObject *attr, PyObject *value) { CFieldObject *cf; CTypeDescrObject *ct = cd->c_type; char *errmsg = "cdata '%s' has no attribute '%s'"; int x; 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; } } errmsg = "cdata '%s' has no field '%s'"; break; case -1: return -1; default: errmsg = "cdata '%s' points to an opaque type: cannot write fields"; break; } } x = PyObject_GenericSetAttr((PyObject *)cd, attr, value); if (x < 0) _cdata_attr_errmsg(errmsg, cd, attr); return x; } static PyObject * convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/ static cif_description_t * fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, Py_ssize_t, 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_IS_VOIDCHAR_PTR) || ((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); if (ctitem->ct_flags & CT_IS_BOOL) if (must_be_array_of_zero_or_one(*output_data, PyBytes_GET_SIZE(init)) < 0) return -1; 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 */ if (ctitem->ct_size == 2) length = _my_PyUnicode_SizeAsChar16(init); else length = _my_PyUnicode_SizeAsChar32(init); length += 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 = MUL_WRAPAROUND(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; struct freeme_s { struct freeme_s *next; union_alignment alignment; } *freeme = NULL; 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 (cd->c_data == NULL) { PyErr_Format(PyExc_RuntimeError, "cannot call null pointer pointer from cdata '%s'", 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, nargs_declared, 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 { if (datasize <= 512) { tmpbuf = alloca(datasize); } else { struct freeme_s *fp = (struct freeme_s *)PyObject_Malloc( offsetof(struct freeme_s, alignment) + (size_t)datasize); if (fp == NULL) { PyErr_NoMemory(); goto error; } fp->next = freeme; freeme = fp; tmpbuf = (char *)&fp->alignment; } 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, CFFI_CLOSURE_TO_FNPTR(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: while (freeme != NULL) { void *p = (void *)freeme; freeme = freeme->next; PyObject_Free(p); } 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_dir(PyObject *cd, PyObject *noarg) { CTypeDescrObject *ct = ((CDataObject *)cd)->c_type; /* replace the type 'pointer-to-t' with just 't' */ if (ct->ct_flags & CT_POINTER) { ct = ct->ct_itemdescr; } if ((ct->ct_flags & (CT_STRUCT | CT_UNION)) && !(ct->ct_flags & CT_IS_OPAQUE)) { /* for non-opaque structs or unions */ if (force_lazy_struct(ct) < 0) return NULL; return PyDict_Keys(ct->ct_stuff); } else { return PyList_New(0); /* empty list for the other cases */ } } static PyObject *cdata_complex(PyObject *cd_, PyObject *noarg) { CDataObject *cd = (CDataObject *)cd_; if (cd->c_type->ct_flags & CT_PRIMITIVE_COMPLEX) { Py_complex value = read_raw_complex_data(cd->c_data, cd->c_type->ct_size); PyObject *op = PyComplex_FromCComplex(value); return op; } /* or cannot be directly converted by calling complex(), just like cannot be directly converted by calling float() */ PyErr_Format(PyExc_TypeError, "complex() not supported on cdata '%s'", cd->c_type->ct_name); return NULL; } static int explicit_release_case(PyObject *cd) { CTypeDescrObject *ct = ((CDataObject *)cd)->c_type; if (Py_TYPE(cd) == &CDataOwning_Type) { if ((ct->ct_flags & (CT_POINTER | CT_ARRAY)) != 0) /* ffi.new() */ return 0; } else if (Py_TYPE(cd) == &CDataFromBuf_Type) { return 1; /* ffi.from_buffer() */ } else if (Py_TYPE(cd) == &CDataGCP_Type) { return 2; /* ffi.gc() */ } PyErr_SetString(PyExc_ValueError, "only 'cdata' object from ffi.new(), ffi.gc(), ffi.from_buffer() " "or ffi.new_allocator()() can be used with the 'with' keyword or " "ffi.release()"); return -1; } static PyObject *cdata_enter(PyObject *cd, PyObject *noarg) { if (explicit_release_case(cd) < 0) /* only to check the ctype */ return NULL; Py_INCREF(cd); return cd; } static PyObject *cdata_exit(PyObject *cd, PyObject *args) { /* 'args' ignored */ CTypeDescrObject *ct; Py_buffer *view; switch (explicit_release_case(cd)) { case 0: /* ffi.new() */ /* no effect on CPython: raw memory is allocated with the same malloc() as the object itself, so it can't be released independently. If we use a custom allocator, then it's implemented with ffi.gc(). */ ct = ((CDataObject *)cd)->c_type; if (ct->ct_flags & CT_IS_PTR_TO_OWNED) { PyObject *x = ((CDataObject_own_structptr *)cd)->structobj; if (Py_TYPE(x) == &CDataGCP_Type) { /* this is a special case for ffi.new_allocator()("struct-or-union *") */ cdatagcp_finalize((CDataObject_gcp *)x); } } break; case 1: /* ffi.from_buffer() */ view = ((CDataObject_frombuf *)cd)->bufferview; PyBuffer_Release(view); break; case 2: /* ffi.gc() or ffi.new_allocator()("not-struct-nor-union") */ /* call the destructor immediately */ cdatagcp_finalize((CDataObject_gcp *)cd); break; default: return NULL; } Py_INCREF(Py_None); return Py_None; } 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 PyMethodDef cdata_methods[] = { {"__dir__", cdata_dir, METH_NOARGS}, {"__complex__", cdata_complex, METH_NOARGS}, {"__enter__", cdata_enter, METH_NOARGS}, {"__exit__", cdata_exit, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; static PyTypeObject CData_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend._CDataBase", 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 */ 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 */ "The internal base type for CData objects. Use FFI.CData to access " "it. Always check with isinstance(): subtypes are sometimes returned " "on CPython, for performance reasons.", /* 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 */ cdata_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ PyObject_Del, /* tp_free */ }; 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, /* inherited */ /* tp_as_number */ 0, /* tp_as_sequence */ &CDataOwn_as_mapping, /* tp_as_mapping */ 0, /* inherited */ /* tp_hash */ 0, /* inherited */ /* tp_call */ 0, /* tp_str */ 0, /* inherited */ /* tp_getattro */ 0, /* inherited */ /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ "This is an internal subtype of _CDataBase for performance only on " "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* inherited */ /* tp_richcompare */ 0, /* inherited */ /* tp_weaklistoffset */ 0, /* inherited */ /* tp_iter */ 0, /* tp_iternext */ 0, /* inherited */ /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ &CData_Type, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ free, /* tp_free */ }; static PyTypeObject CDataOwningGC_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.__CDataOwnGC", sizeof(CDataObject_own_structptr), 0, (destructor)cdataowninggc_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)cdataowninggc_repr, /* tp_repr */ 0, /* inherited */ /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* inherited */ /* tp_as_mapping */ 0, /* inherited */ /* tp_hash */ 0, /* inherited */ /* tp_call */ 0, /* tp_str */ 0, /* inherited */ /* tp_getattro */ 0, /* inherited */ /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES /* tp_flags */ | Py_TPFLAGS_HAVE_GC, "This is an internal subtype of _CDataBase for performance only on " "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */ (traverseproc)cdataowninggc_traverse, /* tp_traverse */ (inquiry)cdataowninggc_clear, /* tp_clear */ 0, /* inherited */ /* tp_richcompare */ 0, /* inherited */ /* tp_weaklistoffset */ 0, /* inherited */ /* tp_iter */ 0, /* tp_iternext */ 0, /* inherited */ /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ &CDataOwning_Type, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; static PyTypeObject CDataFromBuf_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.__CDataFromBuf", sizeof(CDataObject_frombuf), 0, (destructor)cdatafrombuf_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)cdatafrombuf_repr, /* tp_repr */ 0, /* inherited */ /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* inherited */ /* tp_as_mapping */ 0, /* inherited */ /* tp_hash */ 0, /* inherited */ /* tp_call */ 0, /* tp_str */ 0, /* inherited */ /* tp_getattro */ 0, /* inherited */ /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES /* tp_flags */ | Py_TPFLAGS_HAVE_GC, "This is an internal subtype of _CDataBase for performance only on " "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */ (traverseproc)cdatafrombuf_traverse, /* tp_traverse */ (inquiry)cdatafrombuf_clear, /* tp_clear */ 0, /* inherited */ /* tp_richcompare */ 0, /* inherited */ /* tp_weaklistoffset */ 0, /* inherited */ /* tp_iter */ 0, /* tp_iternext */ 0, /* inherited */ /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ &CData_Type, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; 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, /* inherited */ /* tp_repr */ 0, /* inherited */ /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* inherited */ /* tp_as_mapping */ 0, /* inherited */ /* tp_hash */ 0, /* inherited */ /* tp_call */ 0, /* tp_str */ 0, /* inherited */ /* tp_getattro */ 0, /* inherited */ /* 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, "This is an internal subtype of _CDataBase for performance only on " "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */ (traverseproc)cdatagcp_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* inherited */ /* tp_richcompare */ 0, /* inherited */ /* tp_weaklistoffset */ 0, /* inherited */ /* tp_iter */ 0, /* tp_iternext */ 0, /* inherited */ /* 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, /* inherited */ /* 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.__CData_iterator", /* 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, int dont_clear) { /* note: objects with &CDataOwning_Type are always allocated with either a plain malloc() or calloc(), and freed with free(). */ CDataObject *cd; if (dont_clear) cd = malloc(size); else cd = calloc(size, 1); 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) { /* also accepts unions, for the API mode */ CDataObject *cd; Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment); Py_ssize_t datasize = ct->ct_size; if (datasize < 0) { PyErr_SetString(PyExc_TypeError, "return type is an opaque structure or union"); return NULL; } if (ct->ct_flags & CT_WITH_VAR_ARRAY) { PyErr_SetString(PyExc_TypeError, "return type is a struct/union with a varsize array member"); return NULL; } cd = allocate_owning_object(dataoffset + datasize, ct, /*dont_clear=*/1); 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, allocator->ca_dont_clear); 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)) { if (force_lazy_struct(ctitem) < 0) /* for CT_WITH_VAR_ARRAY */ return NULL; if (ctitem->ct_flags & CT_WITH_VAR_ARRAY) { assert(ct->ct_flags & CT_IS_PTR_TO_OWNED); dataoffset = offsetof(CDataObject_own_length, alignment); if (init != Py_None) { Py_ssize_t optvarsize = datasize; if (convert_struct_from_object(NULL, ctitem, init, &optvarsize) < 0) return NULL; datasize = optvarsize; } } } } else if (ct->ct_flags & CT_ARRAY) { dataoffset = offsetof(CDataObject_own_nolength, alignment); datasize = ct->ct_size; if (datasize < 0) { explicitlength = get_new_array_length(ct->ct_itemdescr, &init); if (explicitlength < 0) return NULL; ctitem = ct->ct_itemdescr; dataoffset = offsetof(CDataObject_own_length, alignment); datasize = MUL_WRAPAROUND(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, /*dont_clear=*/1); if (cd == NULL) { Py_DECREF(cds); return NULL; } /* store the only reference to cds into cd */ ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds; /* store information about the allocated size of the struct */ if (dataoffset == offsetof(CDataObject_own_length, alignment)) { ((CDataObject_own_length *)cds)->length = datasize; } 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 else if (PyUnicode_Check(ob)) { char err_buf[80]; cffi_char32_t ordinal; if (_my_PyUnicode_AsSingleChar32(ob, &ordinal, err_buf) < 0) { PyErr_Format(PyExc_TypeError, "cannot cast %s to ctype '%s'", err_buf, ct->ct_name); return NULL; } /* the types char16_t and char32_t are both unsigned. However, wchar_t might be signed. In theory it does not matter, because 'ordinal' comes from a regular Python unicode. */ #ifdef HAVE_WCHAR_H if (ct->ct_flags & CT_IS_SIGNED_WCHAR) value = (wchar_t)ordinal; else #endif value = ordinal; } 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; } /* returns -1 if cannot cast, 0 if we don't get a value, 1 if we do */ static int check_bytes_for_float_compatible(PyObject *io, double *out_value) { if (PyBytes_Check(io)) { if (PyBytes_GET_SIZE(io) != 1) goto error; *out_value = (unsigned char)PyBytes_AS_STRING(io)[0]; return 1; } else if (PyUnicode_Check(io)) { char ignored[80]; cffi_char32_t ordinal; if (_my_PyUnicode_AsSingleChar32(io, &ordinal, ignored) < 0) goto error; /* the signness of the 32-bit version of wide chars should not * matter here, because 'ordinal' comes from a normal Python * unicode string */ *out_value = ordinal; return 1; } *out_value = 0; /* silence a gcc warning if this function is inlined */ return 0; error: Py_DECREF(io); *out_value = 0; /* silence a gcc warning if this function is inlined */ return -1; } 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; int res; 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); } res = check_bytes_for_float_compatible(io, &value); if (res == -1) goto cannot_cast; if (res == 0) { 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; } 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 if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { /* cast to a complex */ Py_complex value; PyObject *io; int res; 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); } res = check_bytes_for_float_compatible(io, &value.real); if (res == -1) goto cannot_cast; if (res == 1) { // got it from string value.imag = 0.0; } else { value = PyComplex_AsCComplex(io); } Py_DECREF(io); if (PyErr_Occurred()) { return NULL; } cd = _new_casted_primitive(ct); if (cd != NULL) { write_raw_complex_data(cd->c_data, value, ct->ct_size); } 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; int dl_auto_close; } DynLibObject; static void dl_dealloc(DynLibObject *dlobj) { if (dlobj->dl_handle != NULL && dlobj->dl_auto_close) dlclose(dlobj->dl_handle); free(dlobj->dl_name); PyObject_Del(dlobj); } static PyObject *dl_repr(DynLibObject *dlobj) { return PyText_FromFormat("", dlobj->dl_name); } static int dl_check_closed(DynLibObject *dlobj) { if (dlobj->dl_handle == NULL) { PyErr_Format(PyExc_ValueError, "library '%s' has already been closed", dlobj->dl_name); return -1; } return 0; } static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args) { CTypeDescrObject *ct; char *funcname; void *funcptr; if (!PyArg_ParseTuple(args, "O!s:load_function", &CTypeDescr_Type, &ct, &funcname)) return NULL; if (dl_check_closed(dlobj) < 0) return NULL; if (!(ct->ct_flags & (CT_FUNCTIONPTR | CT_POINTER | CT_ARRAY))) { PyErr_Format(PyExc_TypeError, "function or pointer or array 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_AttributeError, "function/symbol '%s' not found in library '%s': %s", funcname, dlobj->dl_name, error); return NULL; } if ((ct->ct_flags & CT_ARRAY) && ct->ct_length < 0) { ct = (CTypeDescrObject *)ct->ct_stuff; } 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; if (dl_check_closed(dlobj) < 0) 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; if (dl_check_closed(dlobj) < 0) 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 PyObject *dl_close_lib(DynLibObject *dlobj, PyObject *no_args) { if (dlobj->dl_handle != NULL) { dlclose(dlobj->dl_handle); dlobj->dl_handle = 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}, {"close_lib", (PyCFunction)dl_close_lib, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; static PyTypeObject dl_type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CLibrary", /* 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 void *b_do_dlopen(PyObject *args, const char **p_printable_filename, PyObject **p_temp, int *auto_close) { /* Logic to call the correct version of dlopen(). Returns NULL in case of error. Otherwise, '*p_printable_filename' will point to a printable char version of the filename (maybe utf-8-encoded). '*p_temp' will be set either to NULL or to a temporary object that must be freed after looking at printable_filename. */ void *handle; char *filename_or_null; int flags = 0; *p_temp = NULL; *auto_close = 1; 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; *p_printable_filename = ""; } else if (CData_Check(PyTuple_GET_ITEM(args, 0))) { CDataObject *cd; if (!PyArg_ParseTuple(args, "O|i:load_library", &cd, &flags)) return NULL; /* 'flags' is accepted but ignored in this case */ if ((cd->c_type->ct_flags & CT_IS_VOID_PTR) == 0) { PyErr_Format(PyExc_TypeError, "dlopen() takes a file name or 'void *' handle, not '%s'", cd->c_type->ct_name); return NULL; } handle = cd->c_data; if (handle == NULL) { PyErr_Format(PyExc_RuntimeError, "cannot call dlopen(NULL)"); return NULL; } *p_temp = PyText_FromFormat("%p", handle); *p_printable_filename = PyText_AsUTF8(*p_temp); *auto_close = 0; return handle; } else { PyObject *s = PyTuple_GET_ITEM(args, 0); #ifdef MS_WIN32 PyObject *filename_unicode; if (PyArg_ParseTuple(args, "U|i:load_library", &filename_unicode, &flags)) { Py_ssize_t sz1; wchar_t *w1; #if PY_MAJOR_VERSION < 3 s = PyUnicode_AsUTF8String(s); if (s == NULL) return NULL; *p_temp = s; #endif *p_printable_filename = PyText_AsUTF8(s); if (*p_printable_filename == NULL) return NULL; sz1 = PyText_GetSize(filename_unicode) + 1; sz1 *= 2; /* should not be needed, but you never know */ w1 = alloca(sizeof(wchar_t) * sz1); sz1 = PyUnicode_AsWideChar((PyUnicodeObject *)filename_unicode, w1, sz1 - 1); if (sz1 < 0) return NULL; w1[sz1] = 0; handle = dlopenW(w1); goto got_handle; } PyErr_Clear(); #endif if (!PyArg_ParseTuple(args, "et|i:load_library", Py_FileSystemDefaultEncoding, &filename_or_null, &flags)) return NULL; #if PY_MAJOR_VERSION < 3 if (PyUnicode_Check(s)) { s = PyUnicode_AsUTF8String(s); if (s == NULL) { PyMem_Free(filename_or_null); return NULL; } *p_temp = s; } #endif *p_printable_filename = PyText_AsUTF8(s); if (*p_printable_filename == NULL) { PyMem_Free(filename_or_null); return NULL; } } if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0) flags |= RTLD_NOW; #ifdef MS_WIN32 if (filename_or_null == NULL) { PyErr_SetString(PyExc_OSError, "dlopen(None) not supported on Windows"); return NULL; } #endif handle = dlopen(filename_or_null, flags); PyMem_Free(filename_or_null); #ifdef MS_WIN32 got_handle: #endif if (handle == NULL) { const char *error = dlerror(); PyErr_Format(PyExc_OSError, "cannot load library '%s': %s", *p_printable_filename, error); return NULL; } return handle; } static PyObject *b_load_library(PyObject *self, PyObject *args) { const char *printable_filename; PyObject *temp; void *handle; DynLibObject *dlobj = NULL; int auto_close; handle = b_do_dlopen(args, &printable_filename, &temp, &auto_close); if (handle == NULL) goto error; dlobj = PyObject_New(DynLibObject, &dl_type); if (dlobj == NULL) { dlclose(handle); goto error; } dlobj->dl_handle = handle; dlobj->dl_name = strdup(printable_filename); dlobj->dl_auto_close = auto_close; error: Py_XDECREF(temp); 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, *res; 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() */ /* the 'value' in unique_cache doesn't count as 1, but don't use Py_DECREF(x) here because it will confuse debug builds into thinking there was an extra DECREF in total. */ res = (PyObject *)x; Py_SET_REFCNT(res, Py_REFCNT(res) - 1); return res; error: Py_DECREF(x); return NULL; } /* according to the C standard, these types should be equivalent to the _Complex types for the purposes of storage (not arguments in calls!) */ typedef float cffi_float_complex_t[2]; typedef double cffi_double_complex_t[2]; 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 ) \ EPTYPE2(fc, "float _Complex", cffi_float_complex_t, CT_PRIMITIVE_COMPLEX ) \ EPTYPE2(dc, "double _Complex", cffi_double_complex_t, CT_PRIMITIVE_COMPLEX ) \ ENUM_PRIMITIVE_TYPES_WCHAR \ EPTYPE2(c16, "char16_t", cffi_char16_t, CT_PRIMITIVE_CHAR ) \ EPTYPE2(c32, "char32_t", cffi_char32_t, CT_PRIMITIVE_CHAR ) \ 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) \ EPTYPE2(ssz, "ssize_t", Py_ssize_t, CT_PRIMITIVE_SIGNED) #ifdef HAVE_WCHAR_H # define ENUM_PRIMITIVE_TYPES_WCHAR \ EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR | \ (((wchar_t)-1) > 0 ? 0 : CT_IS_SIGNED_WCHAR)) #else # define ENUM_PRIMITIVE_TYPES_WCHAR /* nothing */ #endif #define EPTYPE(code, typename, flags) EPTYPE2(code, #typename, typename, flags) #define EPTYPE2(code, export_name, typename, flags) \ struct aligncheck_##code { char x; typename y; }; ENUM_PRIMITIVE_TYPES #undef EPTYPE2 CTypeDescrObject *td; static const struct descr_s { const char *name; int size, align, flags; } types[] = { #define EPTYPE2(code, export_name, typename, flags) \ { export_name, \ sizeof(typename), \ offsetof(struct aligncheck_##code, y), \ flags \ }, ENUM_PRIMITIVE_TYPES #undef EPTYPE2 #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 if (ptypes->flags & CT_PRIMITIVE_COMPLEX) { /* As of March 2017, still no libffi support for complex. It fails silently if we try to use ffi_type_complex_float or ffi_type_complex_double. Better not use it at all. */ ffitype = NULL; } 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_IS_VOIDCHAR_PTR; /* '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; } else { sprintf(extra_text, "[%llu]", (unsigned PY_LONG_LONG)length); arraysize = MUL_WRAPAROUND(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 flags) { 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; cf->cf_flags = flags; 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 #ifdef MS_WIN32 # define SF_DEFAULT_PACKING 8 #else # define SF_DEFAULT_PACKING 0x40000000 /* a huge power of two */ #endif 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(__APPLE__) && defined(__arm64__) sflags |= SF_GCC_X86_BITFIELDS; # elif 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 \"...;\" as the last field 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; } #define ROUNDUP_BYTES(bytes, bits) ((bytes) + ((bits) > 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 byteoffset, i, nb_fields, byteoffsetmax, alignedsize; int bitoffset; Py_ssize_t byteoffsetorg; Py_ssize_t totalsize = -1; int totalalignment = -1; CFieldObject **previous; int prev_bitfield_size, prev_bitfield_free; int sflags = 0, fflags; int pack = 0; if (!PyArg_ParseTuple(args, "O!O!|Oniii:complete_struct_or_union", &CTypeDescr_Type, &ct, &PyList_Type, &fields, &ignored, &totalsize, &totalalignment, &sflags, &pack)) return NULL; sflags = complete_sflags(sflags); if (sflags & SF_PACKED) pack = 1; else if (pack <= 0) pack = SF_DEFAULT_PACKING; else sflags |= SF_PACKED; 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 | CT_WITH_PACKED_CHANGE); alignment = 1; byteoffset = 0; /* the real value is 'byteoffset+bitoffset*8', which */ bitoffset = 0; /* counts the offset in bits */ byteoffsetmax = 0; /* the maximum value of byteoffset-rounded-up-to-byte */ 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; } } else if (ftype->ct_flags & (CT_STRUCT|CT_UNION)) { if (force_lazy_struct(ftype) < 0) /* for CT_WITH_VAR_ARRAY */ return NULL; /* GCC (or maybe C99) accepts var-sized struct fields that are not the last field of a larger struct. That's why there is no check here for "last field": we propagate the flag CT_WITH_VAR_ARRAY to any struct that contains either an open- ended array or another struct that recursively contains an open-ended array. */ if (ftype->ct_flags & CT_WITH_VAR_ARRAY) ct->ct_flags |= CT_WITH_VAR_ARRAY; } if (is_union) byteoffset = bitoffset = 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 */ falignorg = get_alignment(ftype); if (falignorg < 0) goto error; falign = (pack < falignorg) ? pack : falignorg; 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; fflags = (is_union && i > 0) ? BF_IGNORE_IN_CTOR : 0; 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 */ /* first, pad to the next byte, * then pad to 'falign' or 'falignorg' bytes */ byteoffset = ROUNDUP_BYTES(byteoffset, bitoffset); bitoffset = 0; byteoffsetorg = (byteoffset + falignorg-1) & ~(falignorg-1); byteoffset = (byteoffset + falign-1) & ~(falign-1); if (byteoffsetorg != byteoffset) { ct->ct_flags |= CT_WITH_PACKED_CHANGE; } 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, byteoffset, foffset, "wrong offset for field '", PyText_AS_UTF8(fname), "'") < 0) goto error; byteoffset = foffset; } 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, byteoffset + cfsrc->cf_offset, cfsrc->cf_bitshift, cfsrc->cf_bitsize, cfsrc->cf_flags | fflags); 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, byteoffset, bs_flag, -1, fflags); if (*previous == NULL) goto error; previous = &(*previous)->cf_next; } if (ftype->ct_size >= 0) byteoffset += ftype->ct_size; 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 = byteoffset; 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 byteoffset to a value aligned for "ftype" */ if (ROUNDUP_BYTES(byteoffset, bitoffset) > field_offset_bytes) { field_offset_bytes += falign; assert(byteoffset < field_offset_bytes); } byteoffset = field_offset_bytes; bitoffset = 0; } 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 = (byteoffset-field_offset_bytes) * 8 + bitoffset; 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(byteoffset < field_offset_bytes); byteoffset = field_offset_bytes; bitoffset = 0; bitshift = 0; } else { bitshift = bits_already_occupied; assert(bitshift >= 0); } bitoffset += fbitsize; byteoffset += (bitoffset >> 3); bitoffset &= 7; } 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 */ byteoffset = ROUNDUP_BYTES(byteoffset, bitoffset); bitoffset = 0; /* align */ byteoffset = (byteoffset + falign-1) & ~(falign-1); byteoffset += ftype->ct_size; bitshift = 0; prev_bitfield_size = ftype->ct_size; prev_bitfield_free = 8 * prev_bitfield_size; } prev_bitfield_free -= fbitsize; field_offset_bytes = byteoffset - ftype->ct_size; } if (sflags & SF_GCC_BIG_ENDIAN) bitshift = 8 * ftype->ct_size - fbitsize - bitshift; if (PyText_GetSize(fname) > 0) { *previous = _add_field(interned_fields, fname, ftype, field_offset_bytes, bitshift, fbitsize, fflags); if (*previous == NULL) goto error; previous = &(*previous)->cf_next; } } } assert(bitoffset == (bitoffset & 7)); if (ROUNDUP_BYTES(byteoffset, bitoffset) > byteoffsetmax) byteoffsetmax = ROUNDUP_BYTES(byteoffset, bitoffset); } *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. */ alignedsize = (byteoffsetmax + 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 < byteoffsetmax) { PyErr_Format(PyExc_TypeError, "%s cannot be of size %zd: there are fields at least " "up to %zd", ct->ct_name, totalsize, byteoffsetmax); 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; } } #define SUPPORTED_IN_API_MODE \ " are only supported as %s if the function is " \ "'API mode' and non-variadic (i.e. declared inside ffibuilder" \ ".cdef()+ffibuilder.set_source() and not taking a final '...' " \ "argument)" static ffi_type *fb_unsupported(CTypeDescrObject *ct, const char *place, const char *detail) { PyErr_Format(PyExc_NotImplementedError, "ctype '%s' not supported as %s. %s. " "Such structs" SUPPORTED_IN_API_MODE, ct->ct_name, place, detail, place); return NULL; } 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 & ~CT_PRIMITIVE_COMPLEX)) { 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). Note: it seems we only get in this case with ffi.verify(). */ 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 */ return fb_unsupported(ct, place, "It is a struct declared with \"...;\", but the C " "calling convention may depend on the missing fields; " "or, it contains anonymous struct/unions"); } /* Another reason: __attribute__((packed)) is not supported by libffi. */ if (ct->ct_flags & CT_WITH_PACKED_CHANGE) { return fb_unsupported(ct, place, "It is a 'packed' structure, with a different layout than " "expected by libffi"); } 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) { return fb_unsupported(ct, place, "It is a struct with bit fields, which libffi does not " "support"); } flat = 1; ct1 = cf->cf_type; while (ct1->ct_flags & CT_ARRAY) { flat *= ct1->ct_length; ct1 = ct1->ct_itemdescr; } if (flat <= 0) { return fb_unsupported(ct, place, "It is a struct with a zero-length array, which libffi " "does not support"); } 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 (PyErr_Occurred()) return NULL; 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 if (ct->ct_flags & CT_UNION) { PyErr_Format(PyExc_NotImplementedError, "ctype '%s' not supported as %s by libffi. " "Unions" SUPPORTED_IN_API_MODE, ct->ct_name, place, place); return NULL; } else { char *extra = ""; if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) extra = " (the support for complex types inside libffi " "is mostly missing at this point, so CFFI only " "supports complex types as arguments or return " "value in API-mode functions)"; PyErr_Format(PyExc_NotImplementedError, "ctype '%s' (size %zd) not supported as %s%s", ct->ct_name, ct->ct_size, place, extra); return NULL; } } #define ALIGN_TO(n, a) ((n) + ((a)-1)) & ~((a)-1) #define ALIGN_ARG(n) ALIGN_TO(n, 8) 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*); /* then enough room for the result --- which means at least sizeof(ffi_arg), according to the ffi docs, but we also align according to the result type, for issue #531 */ exchange_offset = ALIGN_TO(exchange_offset, fb->rtype->alignment); exchange_offset = ALIGN_ARG(exchange_offset); cif_descr->exchange_offset_arg[0] = exchange_offset; 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_TO(exchange_offset, atype->alignment); 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 #undef ALIGN_TO static void fb_cat_name(struct funcbuilder_s *fb, const 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, const char *repl, CTypeDescrObject **pfargs, Py_ssize_t nargs, CTypeDescrObject *fresult, int ellipsis) { Py_ssize_t i; 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); if (repl[0] != '(' && fresult->ct_name[fresult->ct_name_position - 1] != '*') fb_cat_name(fb, " ", 1); /* add a space */ fb_cat_name(fb, repl, strlen(repl)); if (fb->fct) { i = strlen(repl) - 1; /* between '(*' and ')' */ assert(repl[i] == ')'); fb->fct->ct_name_position = fresult->ct_name_position + i; } fb_cat_name(fb, "(", 1); /* 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, **pfargs; Py_ssize_t nargs; char *repl = "(*)"; fb->nb_bytes = 0; fb->bufferp = NULL; fb->fct = NULL; pfargs = (CTypeDescrObject **)&PyTuple_GET_ITEM(fargs, 0); nargs = PyTuple_GET_SIZE(fargs); #if defined(MS_WIN32) && !defined(_WIN64) if (fabi == FFI_STDCALL) repl = "(__stdcall *)"; #endif /* compute the total size needed for the name */ if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0) return NULL; /* allocate the function type */ fct = ctypedescr_new(fb->nb_bytes); if (fct == NULL) return NULL; fb->fct = fct; /* call again fb_build_name() to really build the ct_name */ fb->bufferp = fct->ct_name; if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0) goto error; assert(fb->bufferp == fct->ct_name + fb->nb_bytes); fct->ct_extra = NULL; fct->ct_size = sizeof(void(*)(void)); fct->ct_flags = CT_FUNCTIONPTR; return fct; error: Py_DECREF(fct); return NULL; } static cif_description_t *fb_prepare_cif(PyObject *fargs, CTypeDescrObject *fresult, Py_ssize_t variadic_nargs_declared, ffi_abi fabi) { char *buffer; cif_description_t *cif_descr; struct funcbuilder_s funcbuffer; ffi_status status = (ffi_status)-1; 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; /* use `ffi_prep_cif_var` if necessary and available */ #if CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE if (variadic_nargs_declared >= 0) { if (CFFI_CHECK_FFI_PREP_CIF_VAR) { status = ffi_prep_cif_var(&cif_descr->cif, fabi, variadic_nargs_declared, funcbuffer.nargs, funcbuffer.rtype, funcbuffer.atypes); } } #endif if (status == (ffi_status)-1) { status = ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs, funcbuffer.rtype, funcbuffer.atypes); } if (status != 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, -1, 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 | CT_POINTER | CT_FUNCTIONPTR)) { /* zero extension: fill the '*result' with zeros, and (on big- endian machines) correct the 'result' pointer to write to. We also do that for pointers, even though we're normally not in this branch because ctype->ct_size == sizeof(ffi_arg) for pointers---except on some architectures like x32 (issue #372). */ 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(PyObject *t, PyObject *v, PyObject *tb, char *objdescr, PyObject *obj, char *extra_error_line) { /* like PyErr_WriteUnraisable(), but write a full traceback */ #ifdef USE_WRITEUNRAISABLEMSG /* PyErr_WriteUnraisable actually writes the full traceback anyway from Python 3.4, but we can't really get the formatting of the custom text to be what we want. We can do better from Python 3.8 by calling the new _PyErr_WriteUnraisableMsg(). Luckily it's also Python 3.8 that adds new functionality that people might want: the new sys.unraisablehook(). */ PyObject *s; int first_char; assert(objdescr != NULL && objdescr[0] != 0); /* non-empty */ first_char = objdescr[0]; if (first_char >= 'A' && first_char <= 'Z') first_char += 'a' - 'A'; /* lower() the very first character */ if (extra_error_line == NULL) extra_error_line = ""; if (obj != NULL) s = PyUnicode_FromFormat("%c%s%R%s", first_char, objdescr + 1, obj, extra_error_line); else s = PyUnicode_FromFormat("%c%s%s", first_char, objdescr + 1, extra_error_line); PyErr_Restore(t, v, tb); if (s != NULL) { _PyErr_WriteUnraisableMsg(PyText_AS_UTF8(s), NULL); Py_DECREF(s); } else PyErr_WriteUnraisable(obj); /* best effort */ PyErr_Clear(); #else /* version for Python 2.7 and < 3.8 */ PyObject *f; #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); #endif } 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) { #ifdef USE_WRITEUNRAISABLEMSG extra_error_line = ", trying to convert the result back to C"; #else extra_error_line = "Trying to convert the result back to C:\n"; #endif 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) { PyObject *ecap, *t, *v, *tb; PyErr_Fetch(&t, &v, &tb); ecap = _cffi_start_error_capture(); _my_PyErr_WriteUnraisable(t, v, tb, "From cffi callback ", py_ob, extra_error_line); _cffi_stop_error_capture(ecap); } 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... */ PyObject *ecap; PyErr_Fetch(&exc2, &val2, &tb2); ecap = _cffi_start_error_capture(); _my_PyErr_WriteUnraisable(exc1, val1, tb1, "From cffi callback ", py_ob, extra_error_line); #ifdef USE_WRITEUNRAISABLEMSG _my_PyErr_WriteUnraisable(exc2, val2, tb2, "during handling of the above exception by 'onerror'", NULL, NULL); #else extra_error_line = ("\nDuring the call to 'onerror', " "another exception occurred:\n\n"); _my_PyErr_WriteUnraisable(exc2, val2, tb2, NULL, NULL, extra_error_line); #endif _cffi_stop_error_capture(ecap); } } 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); #if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000 /* We must setup the GIL here, in case the callback is invoked in some other non-Pythonic thread. This is the same as ctypes. But PyEval_InitThreads() is always a no-op from CPython 3.7 (the call from ctypes was removed some time later I think). */ PyEval_InitThreads(); #endif return infotuple; } /* messily try to silence a gcc/clang deprecation warning for ffi_prep_closure. Don't miss the "pragma pop" after the function. This is done around the whole function because very old GCCs don't support it inside a function. */ #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-declarations" #elif defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif static PyObject *b_callback(PyObject *self, PyObject *args) { CTypeDescrObject *ct; CDataObject_closure *cd; PyObject *ob, *error_ob = Py_None, *onerror_ob = Py_None; PyObject *infotuple; cif_description_t *cif_descr; ffi_closure *closure; ffi_status status; void *closure_exec; 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; #if CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE if (CFFI_CHECK_FFI_CLOSURE_ALLOC) { closure = ffi_closure_alloc(sizeof(ffi_closure), &closure_exec); } else #endif { closure = cffi_closure_alloc(); closure_exec = closure; } if (closure == NULL) { Py_DECREF(infotuple); PyErr_SetString(PyExc_MemoryError, "Cannot allocate write+execute memory for ffi.callback(). " "You might be running on a system that prevents this. " "For more information, see " "https://cffi.readthedocs.io/en/latest/using.html#callbacks"); return NULL; } cd = PyObject_GC_New(CDataObject_closure, &CDataOwningGC_Type); if (cd == NULL) goto error; Py_INCREF(ct); cd->head.c_type = ct; cd->head.c_data = CFFI_CLOSURE_TO_FNPTR(char *, closure_exec); cd->head.c_weakreflist = NULL; closure->user_data = NULL; cd->closure = closure; 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 CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE if (CFFI_CHECK_FFI_PREP_CLOSURE_LOC) { status = ffi_prep_closure_loc(closure, &cif_descr->cif, invoke_callback, infotuple, closure_exec); } else #endif { #if defined(__APPLE__) && defined(FFI_AVAILABLE_APPLE) && !FFI_LEGACY_CLOSURE_API PyErr_Format(PyExc_SystemError, "ffi_prep_closure_loc() is missing"); goto error; #else status = ffi_prep_closure(closure, &cif_descr->cif, invoke_callback, infotuple); #endif } if (status != FFI_OK) { PyErr_SetString(PyExc_SystemError, "libffi failed to build this callback"); goto error; } if (closure->user_data != infotuple) { /* Issue #266. Should not occur, but could, if we are using at runtime a version of libffi compiled with a different 'ffi_closure' structure than the one we expect from ffi.h (e.g. difference in details of the platform): a difference in FFI_TRAMPOLINE_SIZE means that the 'user_data' field ends up somewhere else, and so the test above fails. */ PyErr_SetString(PyExc_SystemError, "ffi_prep_closure(): bad user_data (it seems that the " "version of the libffi library seen at runtime is " "different from the 'ffi.h' file seen at compile-time)"); goto error; } PyObject_GC_Track(cd); return (PyObject *)cd; error: closure->user_data = NULL; if (cd == NULL) { #if CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE if (CFFI_CHECK_FFI_CLOSURE_ALLOC) { ffi_closure_free(closure); } else #endif cffi_closure_free(closure); } else Py_DECREF(cd); Py_XDECREF(infotuple); return NULL; } #if defined(__clang__) # pragma clang diagnostic pop #elif defined(__GNUC__) # pragma GCC diagnostic pop #endif 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)) { const 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 Py_ssize_t direct_sizeof_cdata(CDataObject *cd) { Py_ssize_t size; if (cd->c_type->ct_flags & CT_ARRAY) size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; else { size = -1; if (cd->c_type->ct_flags & (CT_STRUCT | CT_UNION)) size = _cdata_var_byte_size(cd); if (size < 0) size = cd->c_type->ct_size; } return size; } static PyObject *b_sizeof(PyObject *self, PyObject *arg) { Py_ssize_t size; if (CData_Check(arg)) { size = direct_sizeof_cdata((CDataObject *)arg); } 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 { Py_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 = MUL_WRAPAROUND(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) && !(cd->c_type->ct_itemdescr->ct_flags & CT_IS_BOOL)) { 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); } else if (cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) { switch (cd->c_type->ct_itemdescr->ct_size) { case 2: { const cffi_char16_t *start = (cffi_char16_t *)cd->c_data; if (length < 0) { /*READ(start, 2)*/ length = 0; while (start[length]) length++; /*READ(start, 2 * length)*/ } else { /*READ(start, 2 * length)*/ maxlen = length; length = 0; while (length < maxlen && start[length]) length++; } return _my_PyUnicode_FromChar16(start, length); } case 4: { const cffi_char32_t *start = (cffi_char32_t *)cd->c_data; if (length < 0) { /*READ(start, 4)*/ length = 0; while (start[length]) length++; /*READ(start, 4 * length)*/ } else { /*READ(start, 4 * length)*/ maxlen = length; length = 0; while (length < maxlen && start[length]) length++; } return _my_PyUnicode_FromChar32(start, length); } } } } 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); else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) { switch (cd->c_type->ct_size) { case 2: return _my_PyUnicode_FromChar16((cffi_char16_t *)cd->c_data, 1); case 4: return _my_PyUnicode_FromChar32((cffi_char32_t *)cd->c_data, 1); } } } PyErr_Format(PyExc_TypeError, "string(): unexpected cdata '%s' argument", cd->c_type->ct_name); return NULL; } static PyObject *b_unpack(PyObject *self, PyObject *args, PyObject *kwds) { CDataObject *cd; CTypeDescrObject *ctitem; Py_ssize_t i, length, itemsize; PyObject *result; char *src; int casenum; static char *keywords[] = {"cdata", "length", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!n:unpack", keywords, &CData_Type, &cd, &length)) return NULL; if (!(cd->c_type->ct_flags & (CT_ARRAY|CT_POINTER))) { PyErr_Format(PyExc_TypeError, "expected a pointer or array, got '%s'", cd->c_type->ct_name); return NULL; } if (length < 0) { PyErr_SetString(PyExc_ValueError, "'length' cannot be negative"); return NULL; } if (cd->c_data == NULL) { PyObject *s = cdata_repr(cd); if (s != NULL) { PyErr_Format(PyExc_RuntimeError, "cannot use unpack() on %s", PyText_AS_UTF8(s)); Py_DECREF(s); } return NULL; } /* byte- and unicode strings */ ctitem = cd->c_type->ct_itemdescr; if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) { switch (ctitem->ct_size) { case sizeof(char): return PyBytes_FromStringAndSize(cd->c_data, length); case 2: return _my_PyUnicode_FromChar16((cffi_char16_t *)cd->c_data,length); case 4: return _my_PyUnicode_FromChar32((cffi_char32_t *)cd->c_data,length); } } /* else, the result is a list. This implementation should be equivalent to but much faster than '[p[i] for i in range(length)]'. (Note that on PyPy, 'list(p[0:length])' should be equally fast, but arguably, finding out that there *is* such an unexpected way to write things down is the real problem.) */ result = PyList_New(length); if (result == NULL) return NULL; src = cd->c_data; itemsize = ctitem->ct_size; if (itemsize < 0) { Py_DECREF(result); PyErr_Format(PyExc_ValueError, "'%s' points to items of unknown size", cd->c_type->ct_name); return NULL; } /* Determine some common fast-paths for the loop below. The case -1 is the fall-back, which always gives the right answer. */ #define ALIGNMENT_CHECK(align) \ (((align) & ((align) - 1)) == 0 && \ (((uintptr_t)src) & ((align) - 1)) == 0) casenum = -1; if ((ctitem->ct_flags & CT_PRIMITIVE_ANY) && ALIGNMENT_CHECK(ctitem->ct_length)) { /* Source data is fully aligned; we can directly read without memcpy(). The unaligned case is expected to be rare; in this situation it is ok to fall back to the general convert_to_object() in the loop. For now we also use this fall-back for types that are too large. */ if (ctitem->ct_flags & CT_PRIMITIVE_SIGNED) { if (itemsize == sizeof(long)) casenum = 3; else if (itemsize == sizeof(int)) casenum = 2; else if (itemsize == sizeof(short)) casenum = 1; else if (itemsize == sizeof(signed char)) casenum = 0; } else if (ctitem->ct_flags & CT_PRIMITIVE_UNSIGNED) { /* Note: we never pick case 6 if sizeof(int) == sizeof(long), so that case 6 below can assume that the 'unsigned int' result would always fit in a 'signed long'. */ if (ctitem->ct_flags & CT_IS_BOOL) casenum = 11; else if (itemsize == sizeof(unsigned long)) casenum = 7; else if (itemsize == sizeof(unsigned int)) casenum = 6; else if (itemsize == sizeof(unsigned short)) casenum = 5; else if (itemsize == sizeof(unsigned char)) casenum = 4; } else if (ctitem->ct_flags & CT_PRIMITIVE_FLOAT) { if (itemsize == sizeof(double)) casenum = 9; else if (itemsize == sizeof(float)) casenum = 8; } } else if (ctitem->ct_flags & (CT_POINTER | CT_FUNCTIONPTR)) { casenum = 10; /* any pointer */ } #undef ALIGNMENT_CHECK for (i = 0; i < length; i++) { PyObject *x; switch (casenum) { /* general case */ default: x = convert_to_object(src, ctitem); break; /* special cases for performance only */ case 0: x = PyInt_FromLong(*(signed char *)src); break; case 1: x = PyInt_FromLong(*(short *)src); break; case 2: x = PyInt_FromLong(*(int *)src); break; case 3: x = PyInt_FromLong(*(long *)src); break; case 4: x = PyInt_FromLong(*(unsigned char *)src); break; case 5: x = PyInt_FromLong(*(unsigned short *)src); break; case 6: x = PyInt_FromLong((long)*(unsigned int *)src); break; case 7: x = PyLong_FromUnsignedLong(*(unsigned long *)src); break; case 8: x = PyFloat_FromDouble(*(float *)src); break; case 9: x = PyFloat_FromDouble(*(double *)src); break; case 10: x = new_simple_cdata(*(char **)src, ctitem); break; case 11: switch (*(unsigned char *)src) { case 0: x = Py_False; Py_INCREF(x); break; case 1: x = Py_True; Py_INCREF(x); break; default: x = convert_to_object(src, ctitem); /* error */ } break; } if (x == NULL) { Py_DECREF(result); return NULL; } PyList_SET_ITEM(result, i, x); src += itemsize; } return result; } static PyObject * b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { /* this is the constructor of the type implemented in minibuffer.h */ CDataObject *cd; Py_ssize_t size = -1; int explicit_size; static char *keywords[] = {"cdata", "size", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:buffer", keywords, &CData_Type, &cd, &size)) return NULL; explicit_size = size >= 0; if (size < 0) size = _cdata_var_byte_size(cd); 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; } if (explicit_size && CDataOwn_Check(cd)) { Py_ssize_t size_max = cdataowning_size_bytes(cd); if (size > size_max) { char msg[256]; sprintf(msg, "ffi.buffer(cdata, bytes): creating a buffer of %llu " "bytes over a cdata that owns only %llu bytes. This " "will crash if you access the extra memory", (unsigned PY_LONG_LONG)size, (unsigned PY_LONG_LONG)size_max); if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) 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); /* must be "void *" */ 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_IS_VOIDCHAR_PTR)) { 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 PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x, int require_writable) { CDataObject *cd; Py_buffer *view; Py_ssize_t arraylength, minimumlength = 0; if (!(ct->ct_flags & (CT_ARRAY | CT_POINTER))) { PyErr_Format(PyExc_TypeError, "expected a pointer or array ctype, got '%s'", ct->ct_name); return NULL; } /* PyPy 5.7 can obtain buffers for string (python 2) or bytes (python 3). from_buffer(u"foo") is disallowed. */ if (PyUnicode_Check(x)) { PyErr_SetString(PyExc_TypeError, "from_buffer() cannot return the address " "of a unicode object"); return NULL; } view = PyObject_Malloc(sizeof(Py_buffer)); if (view == NULL) { PyErr_NoMemory(); return NULL; } if (_my_PyObject_GetContiguousBuffer(x, view, require_writable) < 0) goto error1; if (ct->ct_flags & CT_POINTER) { arraylength = view->len; /* number of bytes, not used so far */ } else { /* ct->ct_flags & CT_ARRAY */ if (ct->ct_length >= 0) { /* it's an array with a fixed length; make sure that the buffer contains enough bytes. */ minimumlength = ct->ct_size; arraylength = ct->ct_length; } else { /* it's an open 'array[]' */ if (ct->ct_itemdescr->ct_size == 1) { /* fast path, performance only */ arraylength = view->len; } else if (ct->ct_itemdescr->ct_size > 0) { /* give it as many items as fit the buffer. Ignore a partial last element. */ arraylength = view->len / ct->ct_itemdescr->ct_size; } else { /* it's an array 'empty[]'. Unsupported obscure case: the problem is that setting the length of the result to anything large (like SSIZE_T_MAX) is dangerous, because if someone tries to loop over it, it will turn effectively into an infinite loop. */ PyErr_Format(PyExc_ZeroDivisionError, "from_buffer('%s', ..): the actual length of the array " "cannot be computed", ct->ct_name); goto error2; } } } if (view->len < minimumlength) { PyErr_Format(PyExc_ValueError, "buffer is too small (%zd bytes) for '%s' (%zd bytes)", view->len, ct->ct_name, minimumlength); goto error2; } cd = (CDataObject *)PyObject_GC_New(CDataObject_frombuf, &CDataFromBuf_Type); if (cd == NULL) goto error2; Py_INCREF(ct); cd->c_type = ct; cd->c_data = view->buf; cd->c_weakreflist = NULL; ((CDataObject_frombuf *)cd)->length = arraylength; ((CDataObject_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; int require_writable = 0; if (!PyArg_ParseTuple(args, "O!O|i", &CTypeDescr_Type, &ct, &x, &require_writable)) return NULL; return direct_from_buffer(ct, x, require_writable); } 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; Py_ssize_t ignored; /* for pypy */ static char *keywords[] = {"cdata", "destructor", "size", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O|n:gc", keywords, &CData_Type, &origobj, &destructor, &ignored)) return NULL; if (destructor == Py_None) { if (!PyObject_TypeCheck(origobj, &CDataGCP_Type)) { PyErr_SetString(PyExc_TypeError, "Can remove destructor only on a object " "previously returned by ffi.gc()"); return NULL; } Py_CLEAR(((CDataObject_gcp *)origobj)->destructor); Py_RETURN_NONE; } cd = allocate_gcp_object(origobj, origobj->c_type, destructor); return (PyObject *)cd; } static PyObject *b_release(PyObject *self, PyObject *arg) { if (!CData_Check(arg)) { PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); return NULL; } return cdata_exit(arg, NULL); } /************************************************************/ 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; } #if 0 /* libffi doesn't properly support complexes currently */ /* also, MSVC might not support _Complex... */ /* if this is enabled one day, remember to also add _Complex * arguments in addition to return values. */ static float _Complex _testfunc24(float a, float b) { return a + I*2.0*b; } static double _Complex _testfunc25(double a, double b) { return a + I*2.0*b; } #endif 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; #if 0 case 24: f = &_testfunc24; break; case 25: f = &_testfunc25; break; #endif 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}, {"unpack", (PyCFunction)b_unpack, 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}, {"release", b_release, METH_O}, #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); } /* backward-compatibility hack: instead of _cffi_to_c_char16_t() and * _cffi_to_c_char32_t(), we have _cffi_to_c_wchar_t() handling whatever * size is wchar_t, and _cffi_to_c_wchar3216_t() handling the opposite. */ #ifdef HAVE_WCHAR_H typedef wchar_t cffi_wchar_t; #else typedef uint16_t cffi_wchar_t; /* random pick... */ #endif static cffi_wchar_t _cffi_to_c_wchar_t(PyObject *init) { if (sizeof(cffi_wchar_t) == 2) return (cffi_wchar_t)_convert_to_char16_t(init); else return (cffi_wchar_t)_convert_to_char32_t(init); } static PyObject *_cffi_from_c_wchar_t(cffi_wchar_t x) { if (sizeof(cffi_wchar_t) == 2) { cffi_char16_t input = x; return _my_PyUnicode_FromChar16(&input, 1); } else { cffi_char32_t input = x; return _my_PyUnicode_FromChar32(&input, 1); } } static int _cffi_to_c_wchar3216_t(PyObject *init) { if (sizeof(cffi_wchar_t) == 4) return (int)_convert_to_char16_t(init); else return (int)_convert_to_char32_t(init); } static PyObject *_cffi_from_c_wchar3216_t(int x) { if (sizeof(cffi_wchar_t) == 4) { cffi_char16_t input = x; return _my_PyUnicode_FromChar16(&input, 1); } else { cffi_char32_t input = x; return _my_PyUnicode_FromChar32(&input, 1); } } 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, _cffi_to_c_wchar_t, _cffi_from_c_wchar_t, _cffi_to_c_long_double, _cffi_to_c__Bool, _prepare_pointer_call_argument, convert_array_from_object, cffi_call_python, _cffi_to_c_wchar3216_t, _cffi_from_c_wchar3216_t, }; 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; static PyTypeObject *all_types[] = { &dl_type, &CTypeDescr_Type, &CField_Type, &CData_Type, &CDataOwning_Type, &CDataOwningGC_Type, &CDataFromBuf_Type, &CDataGCP_Type, &CDataIter_Type, &MiniBuffer_Type, &FFI_Type, &Lib_Type, &GlobSupport_Type, NULL }; 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; } /* readify all types and add them to the module */ for (i = 0; all_types[i] != NULL; i++) { PyTypeObject *tp = all_types[i]; PyObject *tpo = (PyObject *)tp; if (strncmp(tp->tp_name, "_cffi_backend.", 14) != 0) { PyErr_Format(PyExc_ImportError, "'%s' is an ill-formed type name", tp->tp_name); INITERROR; } if (PyType_Ready(tp) < 0) INITERROR; Py_INCREF(tpo); if (PyModule_AddObject(m, tp->tp_name + 14, tpo) < 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; init_cffi_tls_zombie(); 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 } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/call_python.c0000644000175100001770000002313100000000000017005 0ustar00runnerdocker00000000000000#if PY_VERSION_HEX >= 0x03080000 # define HAVE_PYINTERPSTATE_GETDICT #endif static PyObject *_current_interp_key(void) { PyInterpreterState *interp = PyThreadState_GET()->interp; #ifdef HAVE_PYINTERPSTATE_GETDICT return PyInterpreterState_GetDict(interp); /* shared reference */ #else return interp->modules; #endif } static PyObject *_get_interpstate_dict(void) { /* Hack around to return a dict that is subinterpreter-local. Does not return a new reference. Returns NULL in case of error, but without setting any exception. (If called late during shutdown, we *can't* set an exception!) */ static PyObject *attr_name = NULL; PyThreadState *tstate; PyObject *d, *interpdict; int err; PyInterpreterState *interp; tstate = PyThreadState_GET(); if (tstate == NULL) { /* no thread state! */ return NULL; } interp = tstate->interp; #ifdef HAVE_PYINTERPSTATE_GETDICT interpdict = PyInterpreterState_GetDict(interp); /* shared reference */ #else interpdict = interp->builtins; #endif if (interpdict == NULL) { /* subinterpreter was cleared already, or is being cleared right now, to a point that is too much for us to continue */ return NULL; } /* from there on, we know the (sub-)interpreter is still valid */ if (attr_name == NULL) { attr_name = PyText_InternFromString("__cffi_backend_extern_py"); if (attr_name == NULL) goto error; } d = PyDict_GetItem(interpdict, attr_name); if (d == NULL) { d = PyDict_New(); if (d == NULL) goto error; err = PyDict_SetItem(interpdict, attr_name, d); Py_DECREF(d); /* if successful, there is one ref left in interpdict */ if (err < 0) goto error; } return d; error: PyErr_Clear(); /* typically a MemoryError */ return NULL; } static PyObject *_ffi_def_extern_decorator(PyObject *outer_args, PyObject *fn) { const 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 PyErr_NoMemory(); } 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) return 4; /* oops, shutdown issue? */ 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 = _current_interp_key(); 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)) # if defined(HAVE_SYNC_SYNCHRONIZE) # define read_barrier() __sync_synchronize() # elif defined(_AIX) # define read_barrier() __lwsync() # elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) # include # define read_barrier() __compiler_barrier() # elif defined(__hpux) # define read_barrier() _Asm_mf() # else # define read_barrier() /* missing */ # warning "no definition for read_barrier(), missing synchronization for\ multi-thread initialization in embedded mode" # endif #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 _update_cache_to_call_python(), but to avoid the extra dict lookups, we cache in (reserved1, reserved2) the last seen pair (interp->modules, infotuple). The first item in this tuple is a random PyObject that identifies the subinterpreter. */ 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 != _current_interp_key()) { /* 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?)", "@ffi.def_extern() was not called in the current subinterpreter", "got internal exception (shutdown issue?)", }; 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(); } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/cdlopen.c0000644000175100001770000003071600000000000016124 0ustar00runnerdocker00000000000000/* ffi.dlopen() interface with dlopen()/dlsym()/dlclose() */ static void *cdlopen_fetch(PyObject *libname, void *libhandle, const 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) { const char *modname; PyObject *temp, *result = NULL; void *handle; int auto_close; handle = b_do_dlopen(args, &modname, &temp, &auto_close); if (handle != NULL) { result = (PyObject *)lib_internal_new((FFIObject *)self, modname, handle, auto_close); } Py_XDECREF(temp); return result; } 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; if (libhandle != NULL) { lib->l_libhandle = 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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/cffi1_module.c0000644000175100001770000001534000000000000017031 0ustar00runnerdocker00000000000000 #include "parse_c_type.c" #include "realize_c_type.c" #define CFFI_VERSION_MIN 0x2601 #define CFFI_VERSION_CHAR16CHAR32 0x2801 #define CFFI_VERSION_MAX 0x28FF 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 (!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; if (PyDict_SetItemString(FFI_Type.tp_dict, "buffer", (PyObject *)&MiniBuffer_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; } 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; if (version >= CFFI_VERSION_CHAR16CHAR32) num_exports = 28; 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, 0); 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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/cglob.c0000644000175100001770000000644700000000000015572 0ustar00runnerdocker00000000000000 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) "_cffi_backend.__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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/commontypes.c0000644000175100001770000001416000000000000017050 0ustar00runnerdocker00000000000000/* 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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/ffi_obj.c0000644000175100001770000012564700000000000016106 0ustar00runnerdocker00000000000000 /* 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, const 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, const 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) { const 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; if (CData_Check(arg)) { size = direct_sizeof_cdata((CDataObject *)arg); } else { CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL); if (ct == NULL) return NULL; size = ct->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_unpack_doc, "Unpack an array of C data of the given length,\n" "returning a Python string/unicode/list.\n" "\n" "If 'cdata' is a pointer to 'char', returns a byte string.\n" "It does not stop at the first null. This is equivalent to:\n" "ffi.buffer(cdata, length)[:]\n" "\n" "If 'cdata' is a pointer to 'wchar_t', returns a unicode string.\n" "'length' is measured in wchar_t's; it is not the size in bytes.\n" "\n" "If 'cdata' is a pointer to anything else, returns a list of\n" "'length' items. This is a faster equivalent to:\n" "[cdata[i] for i in range(length)]"); #define ffi_unpack b_unpack /* ffi_unpack() => b_unpack() 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 or unicode\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(FFIObject *self, PyObject *args, PyObject *kwds) { PyObject *cdecl1, *python_buf = NULL; CTypeDescrObject *ct; int require_writable = 0; static char *keywords[] = {"cdecl", "python_buffer", "require_writable", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oi:from_buffer", keywords, &cdecl1, &python_buf, &require_writable)) return NULL; if (python_buf == NULL) { python_buf = cdecl1; ct = g_ct_chararray; } else { ct = _ffi_type(self, cdecl1, ACCEPT_STRING|ACCEPT_CTYPE); if (ct == NULL) return NULL; } return direct_from_buffer(ct, python_buf, require_writable); } 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.\n" "\n" "The optional 'size' gives an estimate of the size, used to\n" "trigger the garbage collection more eagerly. So far only used\n" "on PyPy. It tells the GC that the returned object keeps alive\n" "roughly 'size' bytes of external memory."); #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_list_types_doc, "Returns the user type names known to this FFI instance.\n" "This returns a tuple containing three lists of names:\n" "(typedef_names, names_of_structs, names_of_unions)"); static PyObject *ffi_list_types(FFIObject *self, PyObject *noargs) { Py_ssize_t i, n1 = self->types_builder.ctx.num_typenames; Py_ssize_t n23 = self->types_builder.ctx.num_struct_unions; PyObject *o, *lst[3] = {NULL, NULL, NULL}, *result = NULL; lst[0] = PyList_New(n1); if (lst[0] == NULL) goto error; lst[1] = PyList_New(0); if (lst[1] == NULL) goto error; lst[2] = PyList_New(0); if (lst[2] == NULL) goto error; for (i = 0; i < n1; i++) { o = PyText_FromString(self->types_builder.ctx.typenames[i].name); if (o == NULL) goto error; PyList_SET_ITEM(lst[0], i, o); } for (i = 0; i < n23; i++) { const struct _cffi_struct_union_s *s; int err, index; s = &self->types_builder.ctx.struct_unions[i]; if (s->name[0] == '$') continue; o = PyText_FromString(s->name); if (o == NULL) goto error; index = (s->flags & _CFFI_F_UNION) ? 2 : 1; err = PyList_Append(lst[index], o); Py_DECREF(o); if (err < 0) goto error; } result = PyTuple_Pack(3, lst[0], lst[1], lst[2]); /* fall-through */ error: Py_XDECREF(lst[2]); Py_XDECREF(lst[1]); Py_XDECREF(lst[0]); return result; } 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 */ 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_DECREF(res); res = NULL; } Py_XDECREF(tup); } } PyThread_release_lock(lock); Py_DECREF(lockobj); return res; } PyDoc_STRVAR(ffi_release_doc, "Release now the resources held by a 'cdata' object from ffi.new(),\n" "ffi.gc() or ffi.from_buffer(). The cdata object must not be used\n" "afterwards.\n" "\n" "'ffi.release(cdata)' is equivalent to 'cdata.__exit__()'.\n" "\n" "Note that on CPython this method has no effect (so far) on objects\n" "returned by ffi.new(), because the memory is allocated inline with the\n" "cdata object and cannot be freed independently. It might be fixed in\n" "future releases of cffi."); #define ffi_release b_release /* ffi_release() => b_release() from _cffi_backend.c */ #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}, {"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_VKW, 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}, {"list_types", (PyCFunction)ffi_list_types, METH_NOARGS, ffi_list_types_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}, {"release", (PyCFunction)ffi_release, METH_O, ffi_release_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}, {"unpack", (PyCFunction)ffi_unpack, METH_VKW, ffi_unpack_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) "_cffi_backend.FFI", 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 */ } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/file_emulator.h0000644000175100001770000000425500000000000017333 0ustar00runnerdocker00000000000000 /* 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; int fd; const 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; } else { f = PyCapsule_GetPointer(ob_capsule, "FILE"); } Py_DECREF(ob_capsule); /* assumes still at least one reference */ return f; fail: Py_XDECREF(ob_mode); Py_XDECREF(ob_capsule); return NULL; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/lib_obj.c0000644000175100001770000005654700000000000016112 0ustar00runnerdocker00000000000000 /* 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. The original idea was to raise AttributeError for anything else, even attrs like '__class__', but it breaks various things; now, standard attrs are returned, but in the unlikely case where a user cdef()s the same name, then the standard attr is hidden (and the various things like introspection might break). 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; char doc[1]; }; 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 */ int l_auto_close; /* if we must dlclose() this handle */ }; static struct CPyExtFunc_s *_cpyextfunc_get(PyObject *x) { PyObject *y; LibObject *lo; PyCFunctionObject *fo; if (!PyCFunction_Check(x)) return NULL; y = PyCFunction_GET_SELF(x); if (!LibObject_Check(y)) return NULL; fo = (PyCFunctionObject *)x; lo = (LibObject *)y; if (lo->l_libname != fo->m_module) return NULL; return (struct CPyExtFunc_s *)(fo->m_ml); } 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, const char *symbol); static void lib_dealloc(LibObject *lib) { PyObject_GC_UnTrack(lib); if (lib->l_auto_close) cdlopen_close_ignore_errors(lib->l_libhandle); Py_DECREF(lib->l_dict); Py_DECREF(lib->l_libname); Py_DECREF(lib->l_ffi); PyObject_GC_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(). */ PyObject *result = NULL; CTypeDescrObject **pfargs = NULL; CTypeDescrObject *fresult; Py_ssize_t nargs = 0; struct CPyExtFunc_s *xfunc; int i, type_index = _CFFI_GETARG(g->type_op); _cffi_opcode_t *opcodes = lib->l_types_builder->ctx.types; static const char *const format = ";\n\nCFFI C function from %s.lib"; const char *libname = PyText_AS_UTF8(lib->l_libname); struct funcbuilder_s funcbuilder; /* return type: */ fresult = realize_c_func_return_type(lib->l_types_builder, opcodes, type_index); if (fresult == NULL) goto error; /* argument types: */ /* 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. */ i = type_index + 1; while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) i++; pfargs = alloca(sizeof(CTypeDescrObject *) * (i - type_index - 1)); i = type_index + 1; while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) { CTypeDescrObject *ct = realize_c_type(lib->l_types_builder, opcodes, i); if (ct == NULL) goto error; pfargs[nargs++] = ct; i++; } memset(&funcbuilder, 0, sizeof(funcbuilder)); if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0) goto error; /* The few bytes of memory we allocate here appear to leak, but this is not a real leak. Indeed, CPython never unloads its C extension modules. There is only one PyMem_Malloc() per real C function in a CFFI C extension module. That means that this PyMem_Malloc() could also have been written with a static global variable generated for each CPYTHON_BLTN defined in the C extension, and the effect would be the same (but a bit more complicated). */ xfunc = PyMem_Malloc(sizeof(struct CPyExtFunc_s) + funcbuilder.nb_bytes + strlen(format) + strlen(libname)); if (xfunc == NULL) { PyErr_NoMemory(); goto error; } 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 = xfunc->doc; xfunc->direct_fn = g->size_or_direct_fn; xfunc->type_index = type_index; /* build the docstring */ funcbuilder.bufferp = xfunc->doc; if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0) goto error; sprintf(funcbuilder.bufferp - 1, format, libname); /* done building the docstring */ result = PyCFunction_NewEx(&xfunc->md, (PyObject *)lib, lib->l_libname); /* fall-through */ error: Py_XDECREF(fresult); while (nargs > 0) { --nargs; Py_DECREF(pfargs[nargs]); } return result; } 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; const 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 { /* The few bytes of memory we allocate here appear to leak, but this is not a real leak. Indeed, CPython never unloads its C extension modules. There is only one PyMem_Malloc() per real non-integer C constant in a CFFI C extension module. That means that this PyMem_Malloc() could also have been written with a static global variable generated for each OP_CONSTANT defined in the C extension, and the effect would be the same (but a bit more complicated). Note that we used to do alloca(), but see issue #198. We could still do alloca(), or explicit PyMem_Free(), in some cases; but there is no point and it only makes the remaining less-common cases more suspicious. */ 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) { const 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: /*** ATTRIBUTEERROR IS SET HERE ***/ 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 *)&PyModule_Type; /* ^^^ used to be Py_TYPE(lib). But HAAAAAACK! That makes help() behave correctly. I couldn't find a more reasonable way. Urgh. */ Py_INCREF(x); return x; } /* this hack is for Python 3.5, and also to give a more module-like behavior */ if (strcmp(p, "__name__") == 0) { PyErr_Clear(); return PyText_FromFormat("%s.lib", PyText_AS_UTF8(lib->l_libname)); } #if PY_MAJOR_VERSION >= 3 if (strcmp(p, "__loader__") == 0 || strcmp(p, "__spec__") == 0) { /* some more module-like behavior hacks */ PyErr_Clear(); Py_INCREF(Py_None); return Py_None; } #endif 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) "_cffi_backend.Lib", 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 | Py_TPFLAGS_HAVE_GC, /* 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, const char *module_name, void *dlopen_libhandle, int auto_close) { LibObject *lib; PyObject *libname, *dict; libname = PyText_FromString(module_name); if (libname == NULL) goto err1; dict = PyDict_New(); if (dict == NULL) goto err2; lib = (LibObject *)PyType_GenericAlloc(&Lib_Type, 0); 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; lib->l_auto_close = auto_close; return lib; err3: Py_DECREF(dict); err2: Py_DECREF(libname); err1: if (auto_close) 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; } ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1397676 cffi-1.16.0/src/c/libffi_arm64/0000755000175100001770000000000000000000000016571 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_arm64/ffi.lib0000644000175100001770000020073600000000000020035 0ustar00runnerdocker00000000000000! / 1635407542 0 1106 ` 1 Š Š Š Š Š Š1H1H1H1H1H_Ê_Ê_Ê_Ê_Êd€d€d€d€ÂĐÂĐÂĐÂĐÂĐÂĐŚČŚČŚČŚČŚČŚČê0ê0ê0ê0ê0ê0ê0ê0ê0ê0ê0ê0đÌđÌđÌđÌđÌcompress_hfa_typeextend_hfa_typeffi_call_SYSVffi_call_SYSV_fakeffi_closure_SYSVffi_closure_SYSV_Vffi_callffi_closure_SYSV_innerffi_prep_cif_machdepffi_prep_cif_machdep_varffi_prep_closure_locffi_tramp_allocffi_tramp_freeffi_tramp_get_addrffi_tramp_is_supportedffi_tramp_set_parmsffi_closure_allocffi_closure_freeffi_data_to_code_pointerffi_tramp_is_presentffi_java_ptrarray_to_rawffi_java_raw_callffi_java_raw_sizeffi_java_raw_to_ptrarrayffi_prep_java_raw_closureffi_prep_java_raw_closure_locffi_prep_raw_closureffi_prep_raw_closure_locffi_ptrarray_to_rawffi_raw_callffi_raw_sizeffi_raw_to_ptrarrayffi_type_doubleffi_type_floatffi_type_pointerffi_type_sint16ffi_type_sint32ffi_type_sint64ffi_type_sint8ffi_type_uint16ffi_type_uint32ffi_type_uint64ffi_type_uint8ffi_type_voidffi_get_struct_offsetsffi_prep_cifffi_prep_cif_coreffi_prep_cif_varffi_prep_closure/ 1635407542 0 1044 ` Š H1Ê_€dĐÂČŚ0êÌđ1compress_hfa_typeextend_hfa_typeffi_callffi_call_SYSVffi_call_SYSV_fakeffi_closure_SYSVffi_closure_SYSV_Vffi_closure_SYSV_innerffi_closure_allocffi_closure_freeffi_data_to_code_pointerffi_get_struct_offsetsffi_java_ptrarray_to_rawffi_java_raw_callffi_java_raw_sizeffi_java_raw_to_ptrarrayffi_prep_cifffi_prep_cif_coreffi_prep_cif_machdepffi_prep_cif_machdep_varffi_prep_cif_varffi_prep_closureffi_prep_closure_locffi_prep_java_raw_closureffi_prep_java_raw_closure_locffi_prep_raw_closureffi_prep_raw_closure_locffi_ptrarray_to_rawffi_raw_callffi_raw_sizeffi_raw_to_ptrarrayffi_tramp_allocffi_tramp_freeffi_tramp_get_addrffi_tramp_is_presentffi_tramp_is_supportedffi_tramp_set_parmsffi_type_doubleffi_type_floatffi_type_pointerffi_type_sint16ffi_type_sint32ffi_type_sint64ffi_type_sint8ffi_type_uint16ffi_type_uint32ffi_type_uint64ffi_type_uint8ffi_type_void// 1635407542 0 104 ` src\aarch64\win64_armasm.objsrc\aarch64\ffi.objsrc\closures.objsrc\java_raw_api.objsrc\prep_cif.obj/0 1635407535 100666 10114 ` dȘŻVza\^.textÜl p`.pdata(x  @0@.xdata0@0@.debug$S4Df@B.debug$T@@Bę{Ÿ©ę‘=x©ęȘ‘éȘèȘŁ©€86à@­âA­äB­æC­àH©âI©äJ©æK©ÿ‘ ?ÖŁA©ż‘œ{@©…„„ ‹ ÖÀ_Ö Ő`ùÀ_Ö`©À_Ö} ÔÀ_Ö} ÔÀ_Ö} ÔÀ_Ö} ÔÀ_Ö} ÔÀ_Ö`  À_Ö`  À_Ö`-À_Ö`œÀ_Ö`€ À_Ö`€ À_Ö`mÀ_Ö`ęÀ_Öc €= Őb€= Ő`­À_Ö`€=À_ÖS`ùÀ_Ö Ő<S`ùÀ_Ö Őà*`ùÀ_Ö Ő@“`ùÀ_Ö Ő<@“`ùÀ_Ö Ő|@“`ùÀ_Ö Őę{Ż©ę‘à‡­â­ä—­æŸ­ę{Ż©ę‘à ©â ©ä ©æ © †A©"@ùăC‘äC‘ćC‘æȘ”Á! ‹ăC‘ Ö> Ő`@ù;`@©9} Ô Ő} Ô Ő} Ô Ő} Ô Ő} Ô Őc @œ Őb@œ Ő`@-)`@œ'c @ę Őb@ę Ő`@m!`@ęc À= ŐbÀ= Ő`@­`À=`@9} Ô Ő`@y} Ô Ő`@č } Ô Ő`€9 } Ô Ő`€y} Ô Ő`€č Őę{ŃšÀ_ÖŁBB Ńc‹`Ö0D@-2LA-, Ő0D@-2@œ) Ő0D@-' Ő Ő0@œ$ Ő Ő0D@m2LAm Ő0D@m2@ę Ő0D@m Ő Ő0@ę Ő Ő0D@­2Ì@­  Ő0D@­2À=  Ő0D@­ Ő Ő0À= €=€=€=€=À_ÖŁBB Ńc‹`Ö0D@­2LA­  À_Ö0D@­2À=  À_Ö0D@­€ À_Ö Ő0À=€ À_Ö Ő0D@­2LA­€ À_Ö0D@­2À=€ À_Ö0D@­„ À_Ö Ő0À=„ À_Ö Ő0D@­2LA­  Ő0D@­2À=  Ő0D@­D­À_Ö Ő0À=€=À_Ö €=€=D­À_ÖŽ;&= \G3Y $XZáƒäáĄäS@QÀáĄäĄä8ä8äń˜ZC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\aarch64\win64_armasm.obj:<öVuMicrosoft (R) ARM Macro AssembleróTC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\aarch64\win64_armasm.asmô`Û5 •%P(łÜW˜4šĘŽjÚò ! Ő€Ù€ Ú€Û€Ę€Ț€â€ ć€$æ€(ç€,è€0é€4î€8ï€<đ€@ń€Dô€Hö€Lű€Pû€Tü€Xÿ€\€`€d€h €l €p €t €x€|€€€„€ˆ€Œ€€”€˜€œ€ €€€š€Ź€°€Ž€ž€Œ€À €Ä!€È"€Ì#€Đ$€Ô%€Ű&€Ü'€à(€ä)€è*€ì+€đ,€ô-€ű.€ü/€0€1€2€ 3€4€5€6€7€ 8€$9€(:€,;€0<€4=€8>€<?€@@€DA€HB€LC€PD€TE€XF€\G€`H€dI€h]€p`€ta€xb€|c€€e€„i€Œm€n€”o€˜p€œs€ t€€w€šx€Źy€°z€Ž|€ž€Œ€€À€Ä‚€Èƒ€Ì†€Đˆ€Ô‰€ŰЀ܋€àŒ€ä€èŽ€ì€đ€ô‘€ű’€ü“€”€•€–€ —€˜€™€š€›€ œ€$€(ž€,Ÿ€0 €4Ą€8ą€<Ł€@€€D„€HŠ€L§€Pš€T©€XȘ€\«€`Ź€d­€hź€lŻ€p°€t±€xČ€|ł€€Ž€„”€ˆ¶€Œ·€ž€”č€˜ș€œ»€ Œ€€œ€šŸ€Źż€°À€ŽÁ€žÂ€ŒĂ€ÀÄ€ÄĆ€ÈÉ€ÌÊ€Đń€Ôò€Űó€Üô€àő€äù€èú€ìû€đü€ôț€űÿ€ü€€€€ €€€ € €  €$ €(€,€0€4€8€<€@€D€H€L€P€T€X€\€`€d!€h"€l#€p$€t&€x'€|(€€)€„+€ˆ,€Œ-€.€”0€˜1€œ4€ 6€€8€š:€Ź;€°D€ŽE€žF€ŒG€ÀH€ÄL€ÈM€ÌN€ĐO€ÔQ€ŰR€ÜS€àT€äV€èW€ìX€đY€ô[€ű\€ü]€^€`€a€ b€c€e€f€g€ h€$j€(k€,l€0m€4o€8p€<q€@r€Dt€Hu€Lv€Pw€Ty€Xz€\{€`|€d~€h€l€€p€tƒ€x„€|…€€ˆ€„Š€ˆ‹€ŒŒ€ńŒ:ààcompress_hfa_type"ffi_closure_SYSV_epilog"ffi_closure_SYSV_V_end"ffi_call_SYSV_fake_enddo_closure& ffi_closure_SYSV_xdata&ffi_closure_SYSV_return_base. ffi_closure_SYSV_V_xdata_prolog:ffi_closure_SYSV_V* compress_hfa_type_xdata_end:hhffi_call_SYSV_fake&compress_hfa_type_store_q3&compress_hfa_type_store_q4"extend_hfa_type_store_1"extend_hfa_type_store_2"extend_hfa_type_store_3* extend_hfa_type_xdata_prolog"extend_hfa_type_store_46ààextend_hfa_type&extend_hfa_type_jump_base. compress_hfa_type_xdata_prolog* ffi_closure_SYSV_xdata_end& ffi_call_SYSV_fake_xdata&compress_hfa_type_jump_baseffi_call_SYSV. ffi_call_SYSV_fake_xdata_prologTemp.00000000Temp.00000001Temp.00000002"compress_hfa_type_endTemp.00000003Temp.000000046LLffi_closure_SYSVTemp.00000005Temp.00000006Temp.00000007* ffi_closure_SYSV_V_xdata_end* ffi_call_SYSV_fake_xdata_end*ffi_closure_SYSV_save_argumentffi_call_SYSV_L1*ffi_closure_SYSV_epilog1_start* ffi_closure_SYSV_xdata_prologextend_hfa_type_endffi_call_SYSV_return& compress_hfa_type_xdata" extend_hfa_type_xdataffi_closure_SYSV_end& extend_hfa_type_xdata_end& ffi_closure_SYSV_V_xdata. ffi_closure_SYSV_xdata_epilog1,0 t x  ˜ œ  Œ À  à ä     $ D H  p t ž Œ  à %ä $ &( & H ,L  p -t  ˜ .œ  Œ /À  à 0ä   1 0 24  p 3t 3  9”  Œ :À ì <đ = <>@ d?h „@ˆ °AŽ ÌBĐ èCì D (E, DFH |G€G œM  žNŒ ÔOŰ ôPű Q$ HRL tSx T” ÀUÄ èVì W  ,X0 TYX tZx ˜[œ À\Ä è]ì @comp.idVuÿÿ@feat.00ÿÿ.filețÿgC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\aarch64\win64_armasm.asm.text.pdata( .xdata0.debug$Sf.debug$T° à.bfeB!.efàeŽÈ.„Eh\€g~Đ› »h !&.bfe\(.efefÎ0ê (h3.bfeÒ5.efheLę„€3šK€c {$˜œ°Đ 5àG.bfeïI.efàe=ÀäÚ,ù +DÄ`nŽ œȘh žÎlÜ„ ê„ IL.bfeh.efLeËûˆ ÈÌ%B_Œ~4ÈźÌ°àhő(  #Đ8(RkŠcompress_hfa_typeffi_closure_SYSV_epilogffi_closure_SYSV_V_endffi_call_SYSV_fake_enddo_closureffi_closure_SYSV_xdataffi_closure_SYSV_return_baseffi_closure_SYSV_V_xdata_prologffi_closure_SYSV_Vcompress_hfa_type_xdata_endffi_call_SYSV_fakecompress_hfa_type_store_q3compress_hfa_type_store_q4extend_hfa_type_store_1extend_hfa_type_store_2extend_hfa_type_store_3extend_hfa_type_xdata_prologextend_hfa_type_store_4extend_hfa_typeextend_hfa_type_jump_basecompress_hfa_type_xdata_prologffi_closure_SYSV_innerffi_closure_SYSV_xdata_endffi_call_SYSV_fake_xdatacompress_hfa_type_jump_baseffi_call_SYSVffi_call_SYSV_fake_xdata_prologTemp.00000000Temp.00000001Temp.00000002compress_hfa_type_endTemp.00000003Temp.00000004ffi_closure_SYSVTemp.00000005Temp.00000006Temp.00000007ffi_closure_SYSV_V_xdata_endffi_call_SYSV_fake_xdata_endffi_closure_SYSV_save_argumentffi_call_SYSV_L1ffi_closure_SYSV_epilog1_startffi_closure_SYSV_xdata_prologextend_hfa_type_endffi_call_SYSV_returncompress_hfa_type_xdataextend_hfa_type_xdataffi_closure_SYSV_endextend_hfa_type_xdata_endffi_closure_SYSV_V_xdataffi_closure_SYSV_xdata_epilog1/29 1635407531 100666 11846 ` dȘ"«Vza•ö.drectve‹d .debug$Sœï@B.rdata‹@@@.text$mnT› @`.text$mn0ï @`.text$mn @`.text$mnx'Ÿ @`.text$mnł» @`.text$mn˜Ć] @`.text$mn<Á ę  @`.text$mnÀ% ć @`.text$mn(5] @`.text$mn(…­ @`.text$mnÌŐĄ @`.text$mnT- @`.text$mnt‹ÿ @`.text$mnŰ á @`.xdataő@0@.pdata @0@.xdata %@0@.pdata19@0@.xdata M@0@.pdataYa@0@.pdatau}@0@.pdata‡@0@.xdata™@0@.pdata©±@0@.xdataĆ@0@.pdataĘć@0@.xdata ù@0@.pdata#+@0@.xdata ?_@0@.pdataiq@0@.chks64… /DEFAULTLIB:"uuid.lib" /DEFAULTLIB:"uuid.lib" /DEFAULTLIB:"MSVCRT" /DEFAULTLIB:"OLDNAMES" /EXPORT:ffi_prep_closure_loc /EXPORT:ffi_call ńQC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\aarch64\ffi.obj:<"öVuVuMicrosoft (R) Optimizing CompilerXńÿÿÖ)@č?!qÂT(*A‘(č ‹À_Ö)@ù€Ò ń(čh ˆš Ń*Ńh Ș ‘‰‹)ù€‹À_Ö @ù_ ń€ÒH ˆš Ń*Ńh Ș ‘‰‹ ù€‹À_Ö|©À_Ö*Q_5qšTé(IȘ8( ‹Ö@9À_ÖÀ9}@“À_Ö@yÀ_ÖÀy}@“À_Ö@čÀ_րčÀ_Ö@ùÀ_֐@ùÖùęęęíïòôśùûûęûûû\%`%€Ò7ę{ș©óS©ő[©śc©ùk©û+ùę‘”ÿĂŃú‘ùȘD‡©( @ùśȘC ù,'C) €Ò @ù(rH č(y3ˆJù“(6ÿń늚w”sS06 €ÒhA,‹ Ą‘*=‘_ ëLTêß|ČOęDӔÿs/Ëń€R_ùö‘‘H@,‹€R €ÒHùĄ‘—š(@č€RHčqMT €Ò(@ùfXuűYuűȘ@y€@ù_=qèT)(YȘžI ( ‹ÖàȘ” 4‰€R)K(@č qH @č @z@TŠ _!qˆTˆ~@Ó A‘â*À ‹ô *  ?!qhT@Óâ*À‹ű *áȘ”L@ù‘C @ùO€RŸ@ń©TL@ùfL5‹€Ò €ÒË@ùŸ"q"Tˆ~@Ó A‘Ëz)ű”?ˆ‘ ęCÓIA4‹?!ńÈTˆ~@Ó A‘À ‹T €Rš@y €Ò!ń(1ˆšI@ùŃ)Ń)Ș*‘I‹Iù@‹âȘáȘ”C @ù‘L@ù!KQ5qTI (I«8( ‹ÖË@9ÓÿÿÈÀ9 }@“ĐÿÿË@yÎÿÿÈÀy }@“ËÿÿË@čÉÿÿˀčÇÿÿš@y‰Ń€R!ńš1ˆšŃ)Ș*‘L‹Ki"űLùH@č” €Òżk+đÿTE‹A©ä*A@ùăȘàȘ”ł06B@ùáȘàȘ”_‘ÿєû+@ùùkD©ścC©ő[B©óSA©ę{ÆšÀ_֐@ù?Ö ±ÿÿÿ±ÿÿÿ±ÿÿÿ±ÿÿÿ±ÿÿÿÆíííșŒżÁÄÆí>œ</ˆ3@?ü6?=<%@%óSŸ©ț ù@ùôȘ3Ë?ÖâȘáȘ@ù?Öț @ùóSšÀ_Ö* *$+(+ę{ș©óS©ő[©śc©ùk©û+ùę‘”ÿĂŃú‘öȘB‡©Ù@čśȘE›©ûȘ(}Ó =‘?ëLTéß|Č/ęDӔÉ@čÿs/Ë€R_čő‘_ù€R"r(yBč8ˆXč€Ò€R?q­T €ÒÈ@ùYtűȘ@yŠ@ù_=qTI(YȘži( ‹Ö"qâTh~@Ó A‘ê ‹sȘZ4űß ńh1†š‰ŃŃ)Ș*‘D‹J‹DùȘZ4ű€RàȘ” 4È@č @’Š€ÒK Ë qD@zàThA3‹!ńèTh~@Ó A‘â*á ‹à ‹s ”èȘD@ùC@čY4ű €ÒB@čf€RB@čHčš@y €ÒC@č€R!ń(1ˆšщŃ)Ș*‘K‹@‹KùèȘä ȘY4ű €ÒQhA#‹!ńèTi|@Óâ*á ‹c à ‹Cč”èȘD@ùC@čY4ű €ÒB@č@š@y €Ò‰ŃB@č€RCč!ńh1ˆšŃ)Ș*‘D‹@‹DùèȘY4ű/ß@ń‰T"qTh~@Ó A‘ê ‹s €ÒH@ù#ˆŃ @Č*‘D!‘J‹DùH@ù€R €ÒÈ‘ ęCÓiA3‹?!ńTh~@Ó A‘ê ‹s €ÒȘZ4űš@y €Ò‰Ń€R!ńh1ˆšŃ)Ș*‘D‹DùH‹šZ4ű”ŸkëëÿTX@čI«A©rCŁB©âȘàȘ!Šš?Öà*_‘ÿєû+@ùùkD©ścC©ő[B©óSA©ę{ÆšÀ_֐@ù?֔>X< /h44T=t%x%ÿCŃó{©âȘE@ùS@čȘ@y_=qˆTÉ(IȘ8( ‹Ö€R †€RÆ€R€RF€R†€RÆ€R&€RàȘ”æ*Æ5š@ùAń‰T€Rs"‘aTF€R!ń*€RI€RF‰D@č€RŸqMTC@ù`Űeű” 5„żkkÿÿTÆ2iQFč* 2KKč€Ró{@©ÿC‘À_֐@ù?ÖÆÒÖÖÖÈÎÊĐÌÒÔÔÖÔÖt/Ì/ %%ÿCŃó{©óȘe @ùb@čȘ@y_=qˆTÉ(IȘ8( ‹Ö€R †€RÆ€R€RF€R†€RÆ€R&€RàȘ”æ*Æ5š@ùAń‰T€RB ‘aTF€R!ń*€RI€RF‰d@č€RŸqMTc@ù`Űeű” 5„żkkÿÿTÆ2IQ* 2KÈ2k")€Ró{@©ÿC‘À_֐@ù?ÖÆÒÖÖÖÈÎÊĐÌÒÔÔÖÔÖt/Ì/ %%óSœ©ő[©țùóȘh@čôȘőȘöȘ Q?qÉT@€Rț@ùő[A©óSĂšÀ_֐‘k@č )@©r‰*© ‘ H‘)ˆš H@ù‰ ù?֐@ù€ÒáȘ?֐@ù?֐@ù€ÒáȘ?֓Ö©€R–ùț@ùő[A©óSĂšÀ_Ö@DX8\8`9d9l*p*|+€+*”*œ+ +óSŸ©ț ù@ù€Ôމ@ù‰Ž€Ò @y4QyráTà Ș”€ű6s‘‰zsűéțÿ”ț @ùóSšÀ_Ö4-óSŸ©ő{©@ùô*”Ž©@ùiŽ€Ò*@yH5QyràT_k T€Rő{A©óSšÀ_Öá*à Ș” ÿÿ4s‘©zsű țÿ” €Rő{A©óSšÀ_ÖP.țű @yj Q_5q(T© (IȘ8( ‹Öh uS/€Ò@KțAűÀ_Ö@ù @ù+@yh Q q(Th uSO€Ò@KțAűÀ_Ö @ùšŃńńèT@ùé@ù+@yh5QyrTîȘ, @ù €ìމ@ù €Ò‰Ž+@yh5QyrTà Ș”ë*‹ű6J‘‰yjűÉțÿ”«ű6È!‘ @ùîȘëÿÿ q`T q`TqTŻęCÓż ëàT€RțAűÀ_ÖŻęBÓż ëaÿÿTÿń(ÿÿTé@ù鎀Ò*@yH5Qyr€T_ kțÿT- @ùMŽ©@ù €ÒéŽ*@yH5Qyr€T_ küÿTá *à Ș”àûÿ4Œ‘©ylűițÿ”Αéxnű‰üÿ”h uS@KțAűÀ_֗——ÓÓÓÓÓÓÓөӝÄ-”.3€ /ÒÄȂ&äăăAÆJ@@ÖäUÌJ@@ÖäiÒ=",U"-€ÖB$ä.Țv FpŐaäă/äæP ÇáÒ ÉˆÉȄÈ‹äăăă˜ÿÿÿ;7êđP ŐáÒ ÉˆÉȄÈ‹äăăă˜ÿÿÿ;Ăđű°t~Жłç]ÿ0ûMȚ^8QûV*愅4`gŸd‹_E°ŃȘ)ô:ÍiÝ2Ê@ÔsMđÒt,—ĄJÆŒ‰ü|ÂŚ›dwÂpYÿg$.bžlđO`xEG»”Źϐ’ÏÉȚ}ÎÂHcĂwŹÆÙ @±ŒšÜ=~ű’Ę˜‘Sû±Bm”‰%ŽĘEd”űŻUpőĄ zÒ Ä[x€ŻUpőĄ zÒ Ä[x€ŻUpőĄ zâ›JŽ.KjźtdBń¶8Čőæ~œûŻUpőĄ zÛqDÀ’­&]ŻUpőĄ zÏźÀ­èËF:ŻUpőĄ zoiN„OÈŻŻUpőĄ z@comp.idVuÿÿ@feat.00€ÿÿ.drectve‹)ŁH.debug$Sœ.rdata\žm.text$mnTÔFm».text$mn0{R'ł.text$mnüŐ.text$mnxhę`>.text$mnfâs.text$mn ˜ WR/.text$mn <qж'.text$mn ÀĆRà’.text$mn (H}‘Ź.text$mn (Póșœ.text$mnÌBUeĘ.text$mnTÉ9.text$mntÔd „.text$mnŰS­.: ffi_call O d }•± is_hfa0 is_hfa1 Á arg_init Í ß ó   2 @ M ^ q ˆ __chkstk ™ Ż memcpy $LN2@$LN12__swt $LN70 $LN13` $LN18p $LN88 $LN11P $LN9@ $LN12X $LN10H $LN15h $LN58 $LN38 $LN30  $LN28Ž $LN39˜ $LN26€ $LN5è $LN34ä $LL6È $LN59 __swt $LN90 $LN15` $LN20p $LN108 $LN13P $LN11@ $LN14X $LN12H $LN17h $LN61 $LN41 $LN32  $LN30Ž $LN42˜ $LN28€ $LN7è $LN37ä $LL8È $LN62 $LN8H$LL4 $LN3d$LN8H$LN2X$LN148$LL4 __swtÈ$LN15$$LN22l$LN18<$LN14$LN83đ$LN49Ü$LL45°$LL8˜$LN28 $LN30$LN29,ÆŽ$LN38\$LN11š$LN58Œ$LN52œ$LL54p$LL13@__swth$LN9L$LN4$LN5$$LN60$LN78$LN8D$LN13T$LN14\$LN2 __swtH $LN86T $LN2Ô ĐT $LN27 __swtˆ $LN55  $LN50p $LN51x $LN52„ $LN53Œ $LN54˜ $LN59Œ $LN118H $LN81H $LN8l $LN13€ $LN11x $LN80˜ $LN3è $LN42< $LN32€ $LN34h $LN37œ $LN82„ $LN83 $LN38Ü $LN25š $LN40 $LN418 $LN48À $LL4è $LN43 __swt€ $LN72Ì $LN2 $LN9Ì $LN19 $LN89€ $LN67€ $LN66T $LN3, $LN36t $LN42ì $LN24d $LN26Ü $LN28ˆ $LN30  $LN32ž $LN54 $LN69 $LN34è $LL4  $LN90 .xdataűp; à.pdataę.xdata ĄÉèk .pdata 6.xdata ĄÉèk R.pdata s.pdataÊVôÒ “.pdata&‰f,Ș.xdataöŠ\č.pdataÉ.xdata±{ùŰ.pdataì.xdata üv; ÿ.pdata .xdata  À$3w ( .pdata! G!.chks64"e?trampoline@?1??ffi_prep_closure_loc@@9@9__imp_abortffi_prep_closure_locffi_prep_cif_machdepffi_prep_cif_machdep_var__imp_GetCurrentProcess__imp_FlushInstructionCacheffi_clear_cacheis_vfp_typeallocate_to_stackextend_integer_typeextend_hfa_typecompress_hfa_typeallocate_int_to_reg_or_stackffi_call_SYSVffi_call_intffi_closure_SYSVffi_closure_SYSV_Vffi_closure_SYSV_inner__GSHandlerCheck__security_pop_cookie__security_push_cookie$done$109$do_pointer$121$unwind$ffi_prep_closure_loc$pdata$ffi_prep_closure_loc$unwind$ffi_prep_cif_machdep$pdata$ffi_prep_cif_machdep$unwind$ffi_prep_cif_machdep_var$pdata$ffi_prep_cif_machdep_var$pdata$ffi_clear_cache$pdata$is_hfa0$unwind$is_hfa1$pdata$is_hfa1$unwind$is_vfp_type$pdata$is_vfp_type$unwind$ffi_call_int$pdata$ffi_call_int$unwind$ffi_closure_SYSV_inner$pdata$ffi_closure_SYSV_innersrc\tramp.obj/ 1635407526 100666 1146 ` dȘŠVza{.drectve/T .debug$S˜ƒ@B.text$mn @`.text$mn# @`.text$mn' @`.text$mn/ @`.text$mn7 @`.chks64@; /DEFAULTLIB:"MSVCRT" /DEFAULTLIB:"OLDNAMES" ń‰KC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\tramp.obj:<"öVuVuMicrosoft (R) Optimizing Compiler€ÒÀ_ÖÀ_րÒÀ_րRÀ_ÖÀ_Övö«ûVHȚÇa4 ܍ÒÓÈD 1EF”ł\‘ÓÈD 1E/afMœ”F”ł\‘@comp.idVuÿÿ@feat.00€ÿÿ.drectve/.debug$S˜.text$mnÄ^Ë.text$mn%f˜&.text$mnÄ^Ë.text$mnVîûz.text$mn%f˜&  + ? R .chks64@affi_tramp_is_supportedffi_tramp_allocffi_tramp_set_parmsffi_tramp_get_addrffi_tramp_free/49 1635407523 100666 24084 ` dȘ3ŁVzaŠ9Ì.drectveb  .debug$S˜n@B.bssÈ€PÀ.text$mn   @`.text$mnàF & @`.text$mn°”D @`.text$mnšŒd @`.text$mn0ȘÚ @`.text$mnxűp @`.text$mnP˜è @`.text$mn8 @`.text$mn> @`.text$mnBV @`.text$mnDâ& @`.text$mnè:" @`.text$mn,Jv" @`.text$mnTŠ"Ț% @`.text$mn0ü% @`.text$mnÈ,&ô) @`.text$mnl€*ì+ @`.text$mn,,@1 @`.text$mn˜T1ì3 @`.text$mn`4`4 @`.text$mn t4 @`.text$mn8€4ž4 @`.text$mn4Ì45 @`.text$mnÄ5Ű5 @`.xdata6@0@.pdata66@0@.pdata,646@0@.xdata>6@0@.pdataR6Z6@0@.xdatan6@0@.pdata~6†6@0@.pdataš6ą6@0@.xdataŹ6@0@.pdataŒ6Ä6@0@.pdataŰ6à6@0@.pdataê6ò6@0@.xdataü6@0@.pdata77@0@.pdata,747@0@.pdata>7F7@0@.xdataP7@0@.pdata`7h7@0@.xdata|7@0@.pdata”7œ7@0@.xdata°7@0@.pdataÄ7Ì7@0@.pdataà7è7@0@.chks64˜ò7 /DEFAULTLIB:"MSVCRT" /DEFAULTLIB:"OLDNAMES" /EXPORT:ffi_closure_alloc /EXPORT:ffi_closure_free ńŒNC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\closures.obj:<"öVuVuMicrosoft (R) Optimizing Compiler@ù   ‘I@ùß ëŁTH@ù  ‹ß ëƒTJ @ù ÿÿ” €ÒH%@©Æ‘/‹ì=щ @òè Ë @’늚i ‹?ëÍ1‰š(@òéË* @’늚H Ë !Ńl‹ ù(@Č ùˆù@ù‰ ‹ €Ò§A‘*ùi€Òù   ‘©ùqH%@©è$©J-A©ê,©ŽùžùT,‹‹_űh‹ Àù ‹*_ű_ ëTŸűk€Ò©á‘Œù«ù?ëTȘÁ‘H!‘)!‘ ùêȘ?ëcÿÿTżë Tš@ùŻËê@Č ù’©ùéęCÓÊù?ńŻùâT(‘ @č ‹(€R!Éê ȘŸjáTˆ*čn ùN ùÊ-©À_Öj @ù @ù_ëƒ Tn ùN ùÊ-©À_ÖèęHÓh” €ÒêÿŸÒ ëiTé€Òê*HQ }S+L!ˈ@Q }S*M ‹!Êh@Q }S*h!Ê }S* KK Kl9ˆé%Ț+@’ŠySiA*‹Éù+€Rßù()‘ßù @čk!É ‹Ÿ jáTˆ *čNùÊùÎ9©À_ÖJ@ù(€ÒIË?}ń舚I@ùë!Ț(ő~’ë€ThęÓ ‘L ‹kùӈ@ù(ŽêȘH@ù ő~’?ëÁțÿT @ù_ ëTH @ù ëŁT ùN ùÈ)©ßùÀ_Ö @ùŸëŁTŽùÊùÎ9©À_֐@ùֈ Œ  JJóSœ©ő[©țùà4ŽH@ù 3‘@Ń5€Rh””`5hbCčh6j’ ‘Hę_ˆh5Uę ˆ©ÿÿ5ż;Ո4È@ù€R?Öj’ ‘Hę_ˆh5Uę ˆ©ÿÿ5ż;Őèțÿ5m@ùŸ ëC2Tˆ@ù26ő~’Ì‹è7‰@ù‰6)ù’(‹‘áȘ€ ˔€.5hȘAù ËiȘùp” ˟ ë.‹ƒ/Th@ùŸë T+ęCӉ@ùńBTŠ @ù_ ëáTj@č©"Ëë)*h hčh‘ ‹_ë`T_ ëĂ,T?ë`T? ëC,TI ù* ùm@ùŸ ëą+T‰@ùi+6 7h@ùŸëá Th @ù ‹‹h ùH@Čtùˆùi@ùŸ ëaTùùh@ù_ë 'T€ÒàȘ”4@ù?ëàTˆ @ù ëĂ'T ù( ù‹ą‘i@ù‰”‹‚‘i@ùÉŽ(Ą‘ @ùŠ”(‘ @ùŠŽëȘé Șűÿÿ ëC%Tùm@ù/ùÿފ@ùkB ‘hyjűŸëATiy*ű)”ˆ@ùȘ"Èh@čë**i ičșÿÿÿ ëă"Tè@ùëaTéùéù)öÿŽh@ù?ëŁ!T/ùŠ@ùÊŽh@ù_ëă T*ùIùŠ@ùŠôÿŽh@ù_ë T*ùIùžÿÿˆ@ù @’? ńaóÿTnùˆ@ùÊ@Č ù’‰ùŠùŽj.űáh@ùŸëATh@ù ‹‹hù(@Čtùˆù)i4űŐ(ő~’‰ @ù ęCӁń‹âTŠ @ù_ ëáTj@č©"Ëë)*h hčTh‘  ‹_ ë`T_ ëT? ë`T? ëƒTI ù* ùG@ù? ëàTˆ @ù ëcT ù( ù‹Ą‘i@ù‰”‹‘i@ùÉŽ(Ą‘ @ùŠ”(‘ @ùŠŽëȘé Șűÿÿ ëăTùm@ùOފ@ùkB ‘hyjűŸëATiy*ű)”ˆ@ùȘ"Èh@čë**i ičÿ ëƒTè@ù ëaTéùéùiŽh@ù?ëCT/ùŠ@ùÊŽh@ù_ëƒT*ùIùŠ@ùÊŽh@ù_ëŁT*ùIùÈ@ȈùÎi4űi@ùŸ ëTnùk(ù’É@Ȉù‰ùÎi4űÉęCÓ?ńÂTh‘l@č  ‹š"Éê Ș jáT *hčt ùT ùŠ.©Vj @ùh@ù_ëC Tt ùT ùŠ.©NÈęHÓh” €ÒêÿŸÒ ëiTé€Òê*HQ }S+L!ˈ@Q }S*M ‹!Êh@Q }S*h!Ê }S* KK Kl9ˆÉ%Ț+@’ŠySiA*‹‰ùhB ‘Ÿ~©l@č«"É ‹ jáTh *hčTùŠù”R© J@ù(€ÒIË?}ń舚I@ùË!Ț(ő~’ë€ThęÓ ‘L ‹kùӈ@ù(ŽêȘH@ù ő~’?ëÁțÿTi@ù_ ëTH @ù ëŁT ùT ùˆ*©ŸùhbCčˆ6h’ ‘柈ż;Őț@ùő[A©óSĂšÀ_Öh@ùŸëŁT”ùŠù”R©ńÿÿ@ù?Ö    ,RXE\EÀMÀZÔJŰJóSœ©ő[©țùH@ù 3‘ôȘ5€Rh””`5hbCčh6j’ ‘Hę_ˆh5Uę ˆ©ÿÿ5ż;Ո4È@ù€R?Öj’ ‘Hę_ˆh5Uę ˆ©ÿÿ5ż;Őèțÿ5ŸÂń(Tˆ>‘n@č ń}’Ÿ^ń €ÒT1‰šŠțCÓÈ%Êr Tè(* @’- ‹j‘K ‹l @ù‰ @ù ëÁT©"Íê)*H hčh@ù?ëŁ Ti ù+ ù©ń}Ó(@Č* ‹ˆùI@ù€A‘(@ČHùœi@ùŸ ëé T( 4y !Êé K+ hQ } SLhQ %ÌH}S ‹ L%Ɉ}S j ‹%Éh}S h%ÉL S%Ê  - h‘ Q-‹‹ @ùi @ùŸ ëÁTš"Íé(** jčh@ù?ëăT‰ ù, ùš}}Ó Ë_ńBT©}}Ó(@Č* ‹hùI@ù`A‘(@ČHùbˆ@Čo‹hùI@Čéùêi*űh@ùˆŽ ęCÓg@čh‘l@ù ‹š"ÉíȘjT*hčÍ @ùh@ùżëTÌ ùŹ ù9©jù`A‘oùC@ù?Öh@čH4áȘàȘ” Ÿ±cT€’ ˆ>‘i@čń}’©4áȘàȘ”À”i@ùŸ ëHT*Ëk@ù_ńƒTh‹hùi‹H@Čjù(ù`A‘*i*ű‰@Čiùù(@Čù* ‹hùI@ù`A‘(@ČHùh @ùŸë‚TËh ùj@ù@ČI‹iù@A‘(ùˆ@ČHùáȘàȘ”hbCčˆ6h’ ‘柈ż;Őț@ùő[A©óSĂšÀ_Ö    (RTEXE„JˆJ \Đ[ˆXóSŸ©ő{©őȘuŽ”óȘŽH Ą ‘I@ù ëŁTH@ù  ‹ ëƒTJ @ù ÿÿ” €ÒH @ù ‹©ù”à4€R”ôȘô”àȘ”€Òő{A©óSšÀ_ÖàȘ” ùtùàȘő{A©óSšÀ_ÖC  X?d@tDŒAÿCŃó{©óȘ”`4`@ù”àȘ”ó{@©ÿC‘À_Ö ?B DóSŸ©ț ùHĄ ‘ôȘi@ùŸ ëŁTh@ù  ‹Ÿ ëăTs @ùÿÿ”àȘț @ùóSšÀ_֔À5h@ù‹ț @ùóSšÀ_ր@ù”ț @ùóSšÀ_Ö  H?hAțűH Ą ‘I@ù ëŁTH@ù  ‹ ëĂTJ @ù ÿÿ”€RțAűÀ_֔€ÿÿ4 €RțAűÀ_Ö  <?+@ù   ‘_ ëŁT(@ùi‹_ ëŁTJ @ù*ÿÿ”€RÀ_Ö €RÀ_Ö‘!© @‘)%©€‘!© À‘)%©‘!© @‘)%©€‘!© À‘)%©‘!© @‘)%©€‘!© À‘)%©‘!© @‘)%©€‘!© À‘)%©‘!© @‘)%©€‘!© À‘)%©‘!© @‘)%©€‘!© À‘)%©‘!© @‘)%©€‘!© À‘)%©‘!© @‘)%©€‘!© À‘)%©À_ÖóSœ©ő[©țùÿĂѐ‘h@ù”€’hù  Òè€RiùÊ‘h*č4€RHę_ˆh5Tę ˆ©ÿÿ5ż;Ո4š@ù€R?ÖÊ‘Hę_ˆh5Tę ˆ©ÿÿ5ż;Őèțÿ5h@ù(” ‹Ò «òhù )‘h*@č?eč(ačÈ‘柈ż;Ր@ùà‘?Öé+@čê@č(ŃjŠ© êTHŃ ê€T@ù?րRÿÑț@ùő[A©óSĂšÀ_Ö  48\E`Elœ   °ŒFÀFđJôJ(@òéË* @’늚I Ël‹ ù(@Č ùˆù@ù‰ ‹ €Ò*ùùÀ_Ö( , óSŸ©ț ùˆ @ùóȘ(‹)Ń Ę‘è)ȘTŠŸëiT@ù€R†R ráȘ€Ò?Ö€’ń ˆšŸ±ÀTˆ @òéË* @’늚ˆ Ë Ńi@Čm ‹ii,űHČ© ‹šùk€Ò+ę©h@ùŸëBTlùhȘAùiźAù ‹jȘù_ ëITjźù A‘ț @ùóSšÀ_րÒț @ùóSšÀ_Ö  0G4G(@òéË* @’늚H@òéË* @’f‹ëŠšl‹ˆËi@ČËÉù@ù΋Ÿë!T@ùùé‹(@Č ùÈùÀ@‘À_Ö@ùŸëAT@ùù ‹(@Č ùÈùÀ@‘)i.űÀ_ֈ@ù%€R 7ő~’èüCӁń"TŠ%A©_ ëáT @č© Èë)*h čY‘ ‹_ ë€T @ù_ëƒT? ë€T @ù?ëăTI ù* ùJŠ @ù@ù_ ëT‰ @ù @ù?ëƒT* ùI ù‹Ą‘j@ùŠ”‹‘j@ùÊŽHĄ‘ @ù‰”H‘ @ù‰ŽëȘê Șűÿÿ @ùëăTùmވ@ù )‘ xkűŸ ëAT x+űJ”ˆ@ùȘ È@čë**i č @ùżëƒTš@ù ëaTȘùȘùjŽ @ù_ëCTMù‰@ùÉŽ @ù?ëƒTIù*ù‰@ùÉŽ @ù?ëŁTIù*ùì ‹ï‹ˆ@ùê@Č ù’‰ùéęCÓÊù?ńïi.űT(‘ ‹@čŹ Éê ȘŸjTˆ*čÀ@‘n ùN ùÊ-©À_Öj @ù @ù_ëĂ Tn ùÀ@‘N ùÊ-©À_ÖèęHÓh” €ÒêÿŸÒ ëiTé€Òê*HQ }S+L!ˈ@Q }S*M ‹!Êh@Q }S*h!Ê }S* KK Kl9ˆé%Ț+@’ŠySiA*‹Éù()‘ß}© @čŹ É ‹Ÿ jTˆ *čÀ@‘NùÊùÎ9©À_ÖJ@ù(€ÒIË?}ń舚I@ùë!Ț(ő~’ë€ThęÓ ‘L ‹kùӈ@ùHŽêȘH@ù ő~’?ëÁțÿT @ù_ ëCTH @ù ëăT ùÀ@‘N ùÈ)©ßùÀ_Ö @ùŸëĂTŽùÀ@‘ÊùÎ9©À_֐@ùÖ J$JóS»©ő[©śc©ùk©û{©ôȘ˜ą ‘ @ù€ÒvŽ;€RÀ@ùÙ @ù@òéË* @’늚s‹h@ùő~’š7Ś@ùȘ‹è‹ !Ń_ ëăTˆ@ùëTŸùŸùJj@ùl@ù_ëTi @ùˆ@ù?ëCT* ùI ùką‘j@ùŠ”k‚‘j@ùÊŽHĄ‘ @ù‰”H‘ @ù‰ŽëȘê Șűÿÿˆ@ùëŁTùlŽh@ù )‘‰zkű ëATŠz+űJ”h@ùj#Ȉ@čë**i ‰čˆ@ùŸëCTˆ@ùëaTŠùŠùjވ@ù_ëTLùi@ùÉŽˆ@ù?ëCTIù*ùi@ùÉŽˆ@ù?ëc TIù*ùáȘ”5ˆȘAùú‹öȘ ˉȘù ùNšțHÓh” €ÒêÿŸÒ ëiTé€Òê*HQ }S+L!ˈ@Q }S*M ‹!Êh@Q }S*h!Ê }S* KK Kl9ˆ©&Ț+@’ŠySiA*‹iù()‘~©‹@čl#ÉŠ‹Ÿ jáTˆ *ˆčSùjùsN© J@ù(€ÒIË?}ń舚I@ù«"Ț(ő~’ë€ThęÓ ‘L ‹kùӈ@ùHŽêȘH@ù ő~’?ëÁțÿT‰@ù_ ë#TH @ù ëĂT ùS ùh*©ùűȘöȘéÿ”àȘû{D©ùkC©ścB©ő[A©óSĆšÀ_ֈ@ùŸëŁT“ùjùsN©đÿÿ@ù?ÖŹMHJLJ  ‘ @ù? ëŁT@ù  ‹? ëƒT@ùÿÿ”€ÒÀ_ÖóSœ©ő[©ś{©óȘőȘ”hbCč 6‘€’h6È@ùżëTË @ùh‹iŃ Ę‘è)ȘTŠŸë)T@ù€R†R ráȘ€Ò?Öń —šŸ±€Tˆ @òéË* @’늚ˆ Ë Ńi@Čm ‹ii,űHČ© ‹šùk€Ò+ę©h@ùŸëBTlùhȘAùiźAù ‹jȘù_ ëITjźù A‘@”Ë @ùh‹iŃ %‘è)ȘTŠŸë)T@ù€R†RáȘ€Ò?Öń—š?±ÀThȘAùiźAù ‹jȘù_ ëITjźùn@ù”,‹aùa¶ùtșù‹_űh‹kÂù ‹*_ű_ ë€T@ù?ÖÈ@ùŸűàȘhù” J‘ ëAT(@òéË* @’늚ˆ Ë !Ńl‹i ù(@ČlùˆùÈ@ù‰ ‹ €Ò*ùhù0h‚_ű ő~’*‹LAш @òéË* @’늚( Ë  Ë*‹m ‹K!Ńmùh@Čk ùšùÈ@ù© ‹ €Ò*ùhùją ‘êŽL5@©‹ ‹? ë€TJ @ùjÿÿ”h@ù?ëBTaùją ‘êŽ(‹B@ù_ë TJ @ùŠÿÿ”#€RâȘàȘ”h @ùżëbTj@ùËh ù@ČI‹iù@A‘(ù©@ČIùś{B©ő[A©óSĂšÀ_Ö(‹I @ù_ű ëÀLúCûÿTß ëûÿTš‹Hùl@ùˆ @òéËh @ù* @’늚‰ Ë*‹m ‹j ùH@ČmùšùÈ@ù© ‹ €Ò+ùhùÓÿÿI @ù_ű ëaùÿTăȘH@ùAùàȘ ‹Iù”ś{B©ő[A©óSĂšÀ_Ö€Òś{B©ő[A©óSĂšÀ_ÖR  XG\GGGJ”JŹU° Ž ŒW VóSœ©ő[©ś{©ôȘ€Ò?±â TŒ@ùŹ Ž‰ @ù( ‘?ëiT W‘ë @ù(Ë ‹ ŃK”€>ÔI ˚(© ›“ą ‘JËV Ëi@ùŸ ëŁTh@ù  ‹Ÿ ëƒTs @ùÿÿ”€Òk@ùë#Ti@ùˆą ‘j ‹ ëcT ëCT @ùhÿÿ”(Ë ‹áȘ”`5őȘ6Žh@ù ËiùŠȘAùŒ@ùHˈȘùˆ @òéˈ @ù* @’늚m ‹  ˍù*ËH@Ȋ ùšùè@ù© ‹ €Ò+ùˆùàȘ”‹u”€’ˆùżńàŸś{B©ő[A©óSĂšÀ_Ö4 8 ÌM@Y€ÒîË(üHÓăÿŸÒh”€ÒëiTä€Òê*HQ }S+L!ˈ@Q }S*M ‹!Êh@Q }S*h!Ê }S* KK Kl9ˆ)$Ț+@’ŠySdA*‹ˆ(‘ xhű"€ÒKŽHD˟|ń€Ò舚, Țh@ùíȘæȘćȘ ő~’*Ë_ë‚Tï Șî Ș Ž_ ëź!ŠšˆęÓ_ëj@ù ‘Ï ‹škyiű_ńDKúG‡škތùÓèÿÿëȘ+”Ż”@č)€)!Ä*jÀTè K  iQ*} SLhQ %ÌH}S ‹ L%Ɉ}S j ‹%Éh}S h%ÉL S%Ê  * H)‘ xhű«Žh@ù ő~’h@ù*Ë_ëÎ!Ššï!‹šhŽëȘk@ù«țÿ”ïŽ@ù Ëß ëbT @ùÿ ëŁTç‹ÿëBTê @ùì@ù_ëàTè @ù ëcT ùH ù롑j@ùŠ”끑j@ùȘŽHĄ‘ @ù‰”H‘ @ù‰ŽëȘê Șűÿÿ ëăTù&€RlŽè@ù )‘ xkűÿ ëAT x+űJ”è@ùÊ È@čë**i č @ùŸëcTˆ@ùëaTŠùŠùjŽ @ù_ë#TLùé@ùÉŽ @ù?ëcTIù*ùé@ùÉŽ @ù?ëƒTIù*ù߁ńbTȋʋ @ČK‹éùh@ùàA‘ @ČiùÀ_Ö(@ČÉ@ČèùÌęCÓéùîh.űŸńTˆ‘ @č ‹È Ìê ȘżjTš*čàA‘g ùG ùê,©À_Öj @ù @ù_ëƒ Tg ùàA‘G ùê,©À_ÖÈęHÓh” €ÒëiTé€Òê*HQ }S+L!ˈ@Q }S*M ‹!Êh@Q }S*h!Ê }S* KK Kl9ˆÉ%Ț+@’ŠySiA*‹éù()‘ÿ|© @čÌ É ‹Ÿ jTˆ *čàA‘Gùêùç©À_ÖJ@ù?}ńHIË舚Ë!ȚI@ù(ő~’ë€ThęÓ ‘L ‹kùӈ@ùHŽêȘH@ù ő~’?ëÁțÿT @ù_ ëCTH @ù ëăT ùàA‘G ùè(©ÿùÀ_Ö @ùŸëĂT‡ùàA‘êùç©À_֐@ùրÒÀ_ÖJJ @čè K  iQ*} SLhQ %ÌH}S ‹ L%Ɉ}S j ‹%Éh}S h%ÉL S%Ê  * H)‘ xhűˆ@ùí Ș ő~’.ˈ@ùhŽìȘŒ@ù ވ@ù ő~’*Ë_ë­!ŒšÎ!Ššôÿÿ @ùżëĂTŻ‹żëbTȘ @ùŹ@ù_ ëàTš @ùëƒ T ùH ù«Ą‘j@ùŠ”«‘j@ùȘŽHĄ‘ @ù‰”H‘ @ù‰ŽëȘê Șűÿÿë Tù'€RlŽš@ù )‘ xkűż ëAT x+űJ”š@ùê È@čë**i č @ùŸëƒTˆ@ù ëaTŠùŠùjŽ @ù_ëCTLù©@ùÉŽ @ù?ëƒTIù*ù©@ùÉŽ @ù?ëŁTIù*ù߁ńbTÈ‹Ê ‹ @ČK‹©ùh@ù A‘ @ČiùÀ_Ö(@ČÉ@Čšùéùîi.ű@ùˆŽ ęCÓ @ù(‘ ‹è É@čë ȘÿjTè*č‹ @ù @ùëTŠ ùj ùK1©ùù A‘À_֐@ù֌JJóSŸ©ő{©óȘ4€Rhț_ˆh5tț ˆ©ÿÿ5ż;Őh4š@ù€R?Öhț_ˆh5tț ˆ©ÿÿ5ż;Őÿÿ5€Rő{A©óSšÀ_Ö(E,EüŸˆż;ŐÀ_Öțű@ùáȘ€R†R r€Ò?Ö€’ńˆšțAűÀ_ÖGGțű@ùáȘ€R†R€Ò?Ö€’ńˆšțAűÀ_ÖGGóSœ©ő[©țùÿĂŃóȘôȘôސš@ù€Òá‘àȘ?Ö Žè@ùëATè@ùëáTè#@č@qTè@ùë(TÈ@ùR€ÒàȘ?Ö`4è@ù‹”ËŽüÿ”€RÿÑț@ùő[A©óSĂšÀ_րÿÑț@ùő[A©óSĂšÀ_ÖI H$IlH*€'ÖB$äc‡1ĄeÀÒÂ$äj€ Őaäăo–±€Cž@ȘÒÄȂ&äăăDŸ5 K9 L1€&,ÒÄȂ&äăM«a#N$R:€37ÒÂ$äS·òÀœéîքȂ&äăăXœŐ@ĆŚɆÉȂ*äăăYĂm„Zź°ò»bDšdkcžVIÓ23ïjuX*t;\e1w{|OǞt>·ˆ˜Q m‹[±U曮Ś_óQĘ!cœ™/Ï”őĂ,œnzMă„@“Œy7l@??Kš@”©–lPJûFÄ$ÏĆŠ+ú"ă°[áŸ.+‹E’Ę‘ƒ&t»üXNߎB‘„HԚËF ż`u&ôê±òÊą$‹Ł.m °ë™ü”x`‡†Î\âË5]`#‹'E Ńo-4Â3Ăô'R\@C9ź7Ț휖ò7XÉ-ŻUpőĄ zńëțtꓖ 䅍#慯UpőĄ z§š—üKéŻUpőĄ z„'ĆżMPYáq›ÿ.8ëWŻUpőĄ z–~œŸQ»‚Nšwˆ:Jł.<ßÓ]ŻUpőĄ z‡„uÀu#Bš n€dȏÀ Íè2źŻUpőĄ z{­Śá=ì/JŻUpőĄ zÁ Â’lĘÖŻUpőĄ z#$dșô ‚@comp.idVuÿÿ@feat.00€ÿÿ.drectvebx€}ö.debug$S˜.bssÈmparams_gm_@.text$mnM9ăb.text$mnà Ž„?0.text$mn° «ä,.text$mnš§ ôn.text$mn0iÔœ.text$mn xĄë.text$mn PÔÿ€.text$mn 8łvÌ#.text$mn ÁJĄb.text$mn ćęsu.text$mnDąè|Ô.text$mnèˆÜ….text$mn,jև•.text$mnTčOÆ­.text$mn0ßś~.text$mnÈK‚Łő.text$mnl4KŽS.text$mn,>á’č.text$mn˜>Őlk.text$mn`Q„·á.text$mn 'ÓT.text$mn8…uÿ.text$mn4Xg™č.text$mnÄP!ï ' 8 Q f }    dlmalloc dlfree Ż»Ïâô  . : M ` p  Ž init_top ™ Ł ± œ Ç sys_trim ß í $LN20x$LN3˜$LN18@$LN9L$LL8$$LN5ˆ$LN22$LN2$LN5$LN180 $LN8H $LL7 $LN4d $LN21 $LN17( $LN7< $LL6 $LN30 $LN20 $LN62$LN59°$LN360$LN378$LN49€$LN55L$LN54<$LN57x$LN56h$LL40X$LN3š$LN5 $LN7è$LN10ü$LN16„ûŒ$LN53ä$LN12$LN14À$LN17Ô$LN18$LN20t$LN21X$LN24h$LN28Ü$LN60Ô$LN26ž$LN58Ű$LN29H$LN31 $LN33€$LN1 $LN190à$LN186à$LN130š$LN1214$LN122<$LN136„$LN167P$LN166@$LN169|$LN168l$LL125\ Ô$LN173\$LN13Ű”$LN18ä$LN20È$LN22($LN26@$LN27P$LN56ì$LN58$LN60Ź$LN28ì$LN1874$LL2$LN368$LN34$LN3($LN38t$LN153˜$LN175X$LN43$LN44”$LN50Ä$LN62D$LN64Ž$LN66€$LN91Ì$LN70˜$LN71š$LN72Ű$LN188 $LL4đ$LN80$$LN78$LN5$LN82`$LN152„$LN87|$LN88€$LN94°$LN57$LN99`$LN101@$LN105p$LN108à$LN107„$LN109$LN111l$LN113ž$LL6@$LN3”$LN6Ź$LL2$$LN14 $LN13$LN9P$LN16H$LN158$LL2,$LN13 $LN3,$LL2$LN5 $LN100 $LL2 $LN26ü $LN24 $LN4ü $LN21T $LN20D $LN16ˆ $LN23€ $LN22p $LL8` $LN3° $LN5đ $LN3Ű$LN4Ź$LN5È$LN7h$LN9˜$LN11@$LN12$LN14Ű$LN398$LN84ô$LN64 $LN19$LN20@$LN28Œ$LL2X$LN26p$LN3|$LN30È$LN79đ$LN35è$LN36ì$LN42$LN46À$LN48œ$LN52Đ$LN55@$LN54ä$LN56|$LN58Đ$LN60$LL4€$LN54$$LN380$LL37$LN29 $LN8($LL2$LN6l$LN11°$LN13$LN17À$LN200$LN19Ô$LN21p$LN23Ä$LN25đ$LL5˜$LN86œ$LN84È$LN83ô$LN80ű$LN52Đ$LN53ì$LN1ž$LN41Ž$LN31\$LN32`$LN45œ$LN34$LN40À$LN36€$LN3$LL2h$LN38$LN39°$LN5x$LL4œ$LN91T$LN89T$LN78$LN7$LN11„$LN38š$LN13°$LN20H$LN21ü$LL4È$LN19à$LN5ì$LN238$LN71`$LN28X$LN29\$LN35Œ$LN39Đ$LN41à$LN44P$LN43ô$LN45ˆ$LN47Ü$LN49,$LL6°$LL2,$LN16T$LN32<$LN48T$LN42„$LN20$LL19h$LN43ž$LL26š$LN12$LN15ˆ$LN14,$LN104$LN18Ű$LN3$LN83$LL2Ź$LL5˜$LN108Ì$LN24$$LN6È$LN76À$LN77Ä$LN26$LN27$LN35d$LL74$LN33L$LN8X$LN46$LN37€$LN98Ì$LN42Ä$LN43È$LN49ű$LN53D$LN55À$LN57œ$LN61Đ$LN64<$LN63à$LN65x$LN67È$LN69ű$LL9œ$LN43ˆ$LN56$LN3Ź$LL2x$LN8Œ$LN9ì$LN174$LL4$LN15$LN5($LN28ä$LN19t$LN53œ$LN24”$LN25˜$LN31È$LN35$LN37|$LN38`$LN41p.xdata0œm‘+.pdataE.pdatal‹ÿ^.xdataÏ/ v.pdata  — .xdata!žÙ ·!.pdata" Ô".pdata#ĘȚäđ#.xdata$ƒéì$.pdata%%.pdata&zŽòi&.pdata' $#.'.xdata(‹ŒæF(.pdata)Z).pdata*‘Ś4Jm*.pdata+†}ä ‡+.xdata,+‰x›,.pdata-ź-.xdata.„d .À..pdata/Ò/.xdata0üč^_ă0.pdata11.pdata2Ó=ùl"2.chks643˜2magic_init_mutexffi_closure_allocffi_closure_freeffi_data_to_code_pointerffi_tramp_is_presentffi_tramp_is_supportedffi_tramp_allocffi_tramp_get_addrffi_tramp_free__imp_Sleep__imp_GetSystemInfo__imp_VirtualAlloc__imp_VirtualFree__imp_VirtualQuery__imp_abortwin32mmapwin32direct_mmapwin32munmapwin32_acquire_lockwin32_release_locksegment_holdinghas_segment_linkinit_mparamsmmap_allocinit_binsprepend_allocadd_segmentsys_allocrelease_unused_segmentstmalloc_largetmalloc_small$postaction$63$erroraction$191$postaction$192$unwind$ffi_closure_alloc$pdata$ffi_closure_alloc$pdata$ffi_closure_free$unwind$ffi_data_to_code_pointer$pdata$ffi_data_to_code_pointer$unwind$ffi_tramp_is_present$pdata$ffi_tramp_is_present$pdata$dlmalloc$unwind$dlfree$pdata$dlfree$pdata$win32mmap$pdata$win32direct_mmap$unwind$win32munmap$pdata$win32munmap$pdata$win32_acquire_lock$pdata$init_mparams$unwind$mmap_alloc$pdata$mmap_alloc$unwind$sys_alloc$pdata$sys_alloc$unwind$release_unused_segments$pdata$release_unused_segments$pdata$sys_trim/66 1635407518 100666 5286 ` dȘžVzaĄ s.drectveíä .debug$SœŃ@B.text$mnìm @`.text$mnÌY% @`.text$mn|aĘ @`.text$mnxńi @`.text$mn} @`.text$mn @`.text$mnü… @`.text$mn<œù @`.text$mn8 O  @`.xdatam … @0@.pdata — @0@.pdata« ł @0@.pdataœ Ć @0@.xdataÏ ë @0@.pdataő ę @0@.chks64 /DEFAULTLIB:"MSVCRT" /DEFAULTLIB:"OLDNAMES" /EXPORT:ffi_java_raw_call /EXPORT:ffi_java_ptrarray_to_raw /EXPORT:ffi_java_raw_to_ptrarray /EXPORT:ffi_java_raw_size /EXPORT:ffi_prep_java_raw_closure /EXPORT:ffi_prep_java_raw_closure_loc ńRC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\java_raw_api.obj:<"öVuVuMicrosoft (R) Optimizing Compiler @ù €R@čh4ˆ…@ű @y* Q_1q(TÉ(IȘ8( ‹Ö)@ù(@9Hù (@ù À9*}@“Jù(@ù @y(@ù Ày*}@“Jù(@ù @č(@ù €č (@ù@œPœ (@ù @ùIùB@‘(@ù @ùIùB ‘@čk! ‘kăùÿTÀ_ÖíńúÖÚßâçêńńúööööę{Ÿ©ó ùę‘”ó‘ @čH}}Ó =‘?ëLTéß|Č/ęDӔÿs/Ë @ù €Rç‘ìȘj4š…@ű @y* Q_1qèTé(IȘ8( ‹Ö €Ò €Òƒ…ű@čkC‹kăęÿTăȘ”‘”ó @ùę{šÀ_֐@ù?Öíïïïïïïïííïïęęęę '0%˜ &° Ž @č €Ò @ù QLű7š…@ű @y* Q_1qèT)(IȘ8( ‹Ö €Ò €ÒH ‹K ‹ŒQ,țÿ6ëȘà ȘÀ_֐@ùÖóőőőőőőőóóęőęęęę` d  @ù €R@čh4ˆ…@ű @y* Q_1qèT)(IȘ8( ‹Ö €Ò €ÒA„ű@čkA‹kăęÿTÀ_֐@ùÖóőőőőőőőóóőőęęęę\ ` À_ÖÀ_Öę{Œ©óS©ő[©śùę‘”ś‘ôȘˆ@čöȘ@ùáȘőȘ €Ò QLű7š…@ű @y+ Q1qèTÉ(I«8( ‹Ö €Ò €Òh ‹j ‹ŒQ,țÿ6êȘH=‘ ëLTèß|ČęDӔÿs/ËàȘó‘âȘ”šC©âȘáȘàȘ?Öÿ‘”ś@ùő[B©óSA©ę{ÄšÀ_֐@ù?ÖȚàààààààȚȚęàęęęę'˜%ŹÈ&à ä óSŸ©ő{©óȘôȘ‘őȘäȘăȘ”@5tV©ő{A©óSšÀ_Ö##$óSŸ©ő{©ôȘ‘őȘăȘóȘ”@5tV©ő{A©óSšÀ_Ö ## 3P(áĐƒäăăăŰÿÿÿ$2`=#Q9#S?P2áŃȄȇäăăăžÿÿÿ$#lYąïvdŸ%ÚŹÜXôsXd3©»ȘV$H<Đ)„TmŰT­•A›šRÆ*㭊—F”ł\‘F”ł\‘%M KpU€:#:…€œ|\ńóž9} rnĆ~OĐŻUpőĄ zúaȚQašęcśy2FC q­|vù3ŻUpőĄ z@comp.idVuÿÿ@feat.00€ÿÿ.drectveí ^Ž.debug$Sœ.text$mnìG[Ò.text$mn̍TFđ.text$mn|Ú-ĄŰ.text$mnxôÀ‘-.text$mn%f˜&.text$mn%f˜&.text$mn ü7˜ôv.text$mn <–cä.text$mn 83ç‡  / H Z o ‰ ffi_call §ł Ê á ù __chkstk     __swtŒ$LN9p$LN12°$LN38Œ$LN22Œ$LN21,$LN20”$LN13x$LN4|$LL6L$LN42__swtÜ$LN13$LN14 $LN74$LN8D$LN9X$LN10d$LN11x$LN12„$LN17Ž$LN3Ű$LN2Ä$LN24À$LN25Œ$LL4__swth$LN74$LN10\$LN17X$LN11<$LN2@$LL4__swtl$LN78$LN11`$LN3X$LN12@$LN2D$LL4$LN40 $LN7 $LN2, $LN5 __swtì $LN9d $LN13à $LN39ì $LN23ì $LN5„ $LN14l $LN4p $LL6@ $LN22” .xdata Șü©H7 .pdata Q .pdata‹gïË j.pdataÜđD ‹.xdataBĆV °.pdata Đ_fltused.chks64ïffi_java_raw_callffi_java_ptrarray_to_rawffi_java_raw_to_ptrarrayffi_java_raw_sizeffi_prep_closure_locffi_prep_java_raw_closureffi_prep_java_raw_closure_loc__imp_abortffi_java_rvalue_to_rawffi_java_raw_to_rvalueffi_java_translate_args__GSHandlerCheck__security_pop_cookie__security_push_cookie$unwind$ffi_java_raw_call$pdata$ffi_java_raw_call$pdata$ffi_prep_java_raw_closure$pdata$ffi_prep_java_raw_closure_loc$unwind$ffi_java_translate_args$pdata$ffi_java_translate_argssrc\raw_api.obj/1635407515 100666 4674 ` dȘ›Vzaó `.drectveÏŒ .debug$S˜‹@B.text$mn<#_ @`.text$mn8}” @`.text$mn,Óÿ @`.text$mnÌ Ő @`.text$mnPę @`.text$mnxM @`.text$mnĐĆ• @`.xdataœŃ@0@.pdataÛă@0@.pdataśÿ@0@.pdata  @0@.pdata # @0@.xdata- E @0@.pdataO W @0@.chks64ˆk /DEFAULTLIB:"MSVCRT" /DEFAULTLIB:"OLDNAMES" /EXPORT:ffi_raw_call /EXPORT:ffi_ptrarray_to_raw /EXPORT:ffi_raw_to_ptrarray /EXPORT:ffi_raw_size /EXPORT:ffi_prep_raw_closure /EXPORT:ffi_prep_raw_closure_loc ń‹MC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\raw_api.obj:<"öVuVuMicrosoft (R) Optimizing CompileróSŸ©ő{©óȘôȘ‘őȘäȘăȘ”@5tV©ő{A©óSšÀ_Ö$óSŸ©ő{©ôȘ‘őȘăȘóȘ”@5tV©ő{A©óSšÀ_Ö  óSœ©ő[©ś{©śȘö@ùôȘè@čóȘ€Rh4ˆ@űh@y Q_)qÈT)(IȘ8( ‹։@ù(@9h†ű)ˆ@ù À9*}@“j†ű$ˆ@ù @yi†ű ˆ@ù Ày*}@“j†űˆ@ù @či†űˆ@ù €či†űˆ@ùh†űˆ@ù @ùi†ű b@ùàȘ@ù”È‚_ű @ù*ŃK @Čh‘ ęCÓs ‹è@č””"‘żkăűÿTś{B©ő[A©óSĂšÀ_ÖËÏÔŰĘáììćèććÜ!ę{Ÿ©ó ùę‘”ó‘ @čH}}Ó =‘?ëLTéß|Č/ęDӔÿs/Ë@ù €Rç‘ìȘJ4ȅ@ű @y?5qTh„@űˆù?=qTh„@űˆù ƒùȁ_ű @ù*ŃK @Čh‘ ęCÓc ‹@č­Œ!‘żkęÿTăȘ”‘”ó @ùę{šÀ_Ö 0ŽŒ@č €Ò @ù QÌű7©…@ű(@y5qaTk!‘(@ù Ń* @ČK ‹k‘ŒQŒțÿ6à ȘÀ_Ö @ù €R@čH4š…@ű @y?5qT(„@űHù?=qT(„@űHù Aùš_ű @ù*ŃK @Čh‘ ęCÓ! ‹@čŒB ‘ŸkęÿTÀ_Öę{Œ©óS©ő[©śùę‘”ś‘ôȘˆ@čöȘ@ùáȘőȘ €Ò QÌű7©…@ű(@y5qaTk!‘(@ù Ń* @ČK ‹k‘ŒQŒțÿ6h=‘ ëLTèß|ČęDӔÿs/ËàȘó‘âȘ”šC©âȘáȘàȘ?Öÿ‘”ś@ùő[B©óSA©ę{ÄšÀ_Ö ˆœž30áĐƒäăăăŰÿÿÿ (K!„6=#A9#C40áŃȄȇäăăăžÿÿÿZy•z|țï€B+ Îl”#:…€œ|\ńóž9}3˜ŽČ:æĘÛFF{Œ‚¶™ńÜŹń-h<^žJjaÆ^»>“éE·Ü”ĐòűŻUpőĄ z°Ű9dűČˏúaȚQašęcśy2FC q8֏CĂ0ȘŻUpőĄ z@comp.idVuÿÿ@feat.00€ÿÿ.drectveÏ©­.debug$S˜.text$mn<–cä.text$mn83ç‡.text$mn,ëb.text$mnÌ‚аÇ.text$mnP~‡Àš.text$mnxPäÓ(.text$mn ĐíûÄ  % 9 F [ p ffi_call ‰ œ __chkstk ­ Ă memcpy $LN16,$LN5°$LN7h$LN4œ$LN9|$LL6L$LN18__swt $LN7L$LN8\$LN9p$LN10€$LN11”$LN12€$LN13Ž$LN15À$LN3$LN16Đ$LN2ü$LL4($LN36$LN3t$LN5,$LN2`$LN7@$LL4$LN3H$LN5,$LN2@$LL4$LN40$LN7$LN2,$LN5$LN5t $LN7X $LN4l $LL6@ $LN14„ .xdata §âWÚ .pdata ï .pdata VÚ;œ .pdata ‹gïË .pdataÜđD:.xdata€2T— Z.pdata u.chks64ˆffi_raw_callffi_ptrarray_to_rawffi_raw_to_ptrarrayffi_raw_sizeffi_prep_closure_locffi_prep_raw_closureffi_prep_raw_closure_locffi_translate_args__GSHandlerCheck__security_pop_cookie__security_push_cookie$unwind$ffi_raw_call$pdata$ffi_raw_call$pdata$ffi_ptrarray_to_raw$pdata$ffi_prep_raw_closure$pdata$ffi_prep_raw_closure_loc$unwind$ffi_translate_args$pdata$ffi_translate_argssrc\types.obj/ 1635407511 100666 1631 ` dȘ—Vza.drectve‡Ž .debug$S˜;@B.rdata Ó@@@.chks64 ó /DEFAULTLIB:"MSVCRT" /DEFAULTLIB:"OLDNAMES" /EXPORT:ffi_type_void,DATA /EXPORT:ffi_type_uint8,DATA /EXPORT:ffi_type_sint8,DATA /EXPORT:ffi_type_uint16,DATA /EXPORT:ffi_type_sint16,DATA /EXPORT:ffi_type_uint32,DATA /EXPORT:ffi_type_sint32,DATA /EXPORT:ffi_type_uint64,DATA /EXPORT:ffi_type_sint64,DATA /EXPORT:ffi_type_pointer,DATA /EXPORT:ffi_type_float,DATA /EXPORT:ffi_type_double,DATA ń‰KC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\types.obj:<"öVuVuMicrosoft (R) Optimizing Compiler    Ą2o˜îŰ ÏT–Źw]źż^”Óé.e­@comp.idVuÿÿ@feat.00€ÿÿ.drectve‡Æ;Ń#.debug$S˜.rdata ±rô­!00H@`Px`pš€ÀŰĄđ°.chks64 Àffi_type_voidffi_type_uint8ffi_type_sint8ffi_type_uint16ffi_type_sint16ffi_type_uint32ffi_type_sint32ffi_type_uint64ffi_type_sint64ffi_type_pointerffi_type_floatffi_type_double /87 1635407508 100666 4309 ` dȘ”Vzaƒ S.drectve•l .debug$S˜@B.text$mn8™Ń @`.text$mnÛó @`.text$mntęq @`.text$mn ­M @`.text$mn‡ @`.text$mnä‘u @`.xdata@0@.pdata—@0@.xdata«@0@.pdataĂË@0@.xdataß@0@.pdataïś@0@.chks64x /DEFAULTLIB:"MSVCRT" /DEFAULTLIB:"OLDNAMES" /EXPORT:ffi_prep_closure /EXPORT:ffi_prep_cif /EXPORT:ffi_prep_cif_var /EXPORT:ffi_get_struct_offsets ńŒNC:\Users\niysai01\Workspace\libffi2\aarch64-w64-cygwin\src\prep_cif.obj:<"öVuVuMicrosoft (R) Optimizing CompilerQqéȘ(T(@y5q`T €RÀ_ÖáȘà Ș@€RÀ_Ö,æȘćȘä*ă*€RÿCŃó{©(QqóȘˆTa)f–©Š)š@ùÈ”­@ù €Ò Žżù€Rży €Ò©@ùéŽ(@ùš”€Òà Ș”€ 5š@ù @yš@ù*Ń ŃJ ȘK‘«ùŒŽ‹ù«@ùŒ!‘š@ùȘ@y @ù+ ‹«ùš@ù @y_ k(‘Ššy©@űiüÿ” ŃhŃ)Ș*‘ȘùÊ Žš@y=q€ Tgą@© €Rf@č @y?5qïŠ 4ì@ùˆ@ùè” €Ò쎎 @ùźޟù€RŸy €ÒÉ@ùéŽ(@ùš”€Òà Ș” 5È@ù @yˆ@ù*Ń ŃJ ȘK‘‹ùŽ«ù‹@ù­!‘È@ùŠ@y @ù+ ‹‹ùÈ@ù @y_ k(‘Šˆyɍ@űiüÿ” ŃhŃ)Ș*‘ŠùjŽê„@űH@y=qTH@y Q?j€TèQ  */H@ùÆQ QK 2h œÿÿ €Ró{@©ÿC‘À_ÖočàȘâ4â*á*”ó{@©ÿC‘À_֔ó{@©ÿC‘À_֐@ù?Ö@€Ró{@©ÿC‘À_Ö`L8HX\óSŸ©ő{©ó*ôȘćȘă*@ùæȘä*"€R” 5*€Rq)T ‘­ÿŸR‹Újű ëàTh@y5Q j€Th@ùëTJ_kƒțÿT€Rő{A©óSšÀ_Ö`€Rő{A©óSšÀ_ÖOO,@PDPäȘóSŸ©ő{©óȘôȘ3Žu @ùőŽù€Ry €Ò©@ùéŽ(@ùš”€Òà Ș”`5š@ù @yh@ù*Ń ŃJ ȘK‘kù”Ž‹ùk@ù”"‘š@ùj@y @ù+ ‹kùš@ù @y_ k(‘Šhy©Ž@űiüÿ” ŃhŃ)Ș*‘jùȘŽ€Rő{A©óSšÀ_Ö €Rő{A©óSšÀ_ÖD(€!%ÖB$ä!? †“šÖä7E9€26ÖB$äK›*Wô15u™â{/?~ëđÍèáRè4걓†êËț>è|ę›É<đŸdčVì‘”XndٔӎI#ȘL`­…K ?ϔŻUpőĄ z”n݉ïńZZŻUpőĄ zÈÆ‰Û?ûSŻUpőĄ z@comp.idVuÿÿ@feat.00€ÿÿ.drectve•eł±7.debug$S˜.text$mn8§gwë.text$mn?˒.text$mntH"Bj.text$mn  ÓT.text$mnLb?.text$mnäŠÆ)J  * 7 H _ t  Ÿ« $LN1„$LN3€$LN10$LN2t$LL4L$LN16$LN30$LN4$$LN84d$LN81t$LN6d$LN62à$LN52$LN17È$LN61h$LN21˜$LL16P$LN53X$LN3$$LN80Ì$LN33Ž$LN64T$LN37„$LL32<$LN65ű$LN82$LN13H$LN85$LN28Ô$LN3Ź$LN22L$LN7|$LL24.xdata 4[đ©À .pdata Ù .xdata ț™S—ń .pdata   .xdata ÿę h$ .pdataA]m.chks64x|ffi_prep_closureffi_prep_closure_locffi_prep_cifffi_prep_cif_varffi_get_struct_offsetsffi_prep_cif_machdepffi_prep_cif_machdep_varffi_prep_cif_core__imp_abortinitialize_aggregate$unwind$ffi_prep_cif_var$pdata$ffi_prep_cif_var$unwind$ffi_prep_cif_core$pdata$ffi_prep_cif_core$unwind$initialize_aggregate$pdata$initialize_aggregateffi_type_sint32ffi_type_float ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1397676 cffi-1.16.0/src/c/libffi_arm64/include/0000755000175100001770000000000000000000000020214 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_arm64/include/ffi.h0000644000175100001770000003360700000000000021142 0ustar00runnerdocker00000000000000/* -----------------------------------------------------------------*-C-*- libffi 3.4.2 - Copyright (c) 2011, 2014, 2019, 2021 Anthony Green - Copyright (c) 1996-2003, 2007, 2008 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 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. ----------------------------------------------------------------------- */ /* ------------------------------------------------------------------- Most of the API is documented in doc/libffi.texi. The raw API is designed to bypass some of the argument packing and unpacking on architectures for which it can be avoided. Routines are provided to emulate the raw API if the underlying platform doesn't allow faster implementation. More details on the raw 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. */ #ifndef ARM_WIN64 #define ARM_WIN64 #endif /* ---- System configuration information --------------------------------- */ #include #ifndef LIBFFI_ASM #if defined(_MSC_VER) && !defined(__clang__) #define __attribute__(X) #endif #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. */ #define FFI_64_BIT_MAX 9223372036854775807 #ifdef LONG_LONG_MAX # define FFI_LONG_LONG_MAX LONG_LONG_MAX #else # ifdef LLONG_MAX # define FFI_LONG_LONG_MAX LLONG_MAX # ifdef _AIX52 /* or newer has C99 LLONG_MAX */ # undef FFI_64_BIT_MAX # define FFI_64_BIT_MAX 9223372036854775807LL # endif /* _AIX52 or newer */ # else # ifdef __GNUC__ # define FFI_LONG_LONG_MAX __LONG_LONG_MAX__ # endif # ifdef _AIX /* AIX 5.1 and earlier have LONGLONG_MAX */ # ifndef __PPC64__ # if defined (__IBMC__) || defined (__IBMCPP__) # define FFI_LONG_LONG_MAX LONGLONG_MAX # endif # endif /* __PPC64__ */ # undef FFI_64_BIT_MAX # define FFI_64_BIT_MAX 9223372036854775807LL # endif # endif #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; struct _ffi_type **elements; } ffi_type; /* Need minimal decorations for DLLs to work on Windows. GCC has autoimport and autoexport. Always mark externally visible symbols as dllimport for MSVC clients, even if it means an extra indirection when using the static version of the library. Besides, as a workaround, they can define FFI_BUILDING if they *know* they are going to link with the static library. */ #if defined _MSC_VER # if defined FFI_BUILDING_DLL /* Building libffi.DLL with msvcc.sh */ # define FFI_API __declspec(dllexport) # elif !defined FFI_BUILDING /* Importing libffi.DLL */ # define FFI_API __declspec(dllimport) # else /* Building/linking static library */ # define FFI_API # endif #else # define FFI_API #endif /* The externally visible type declarations also need the MSVC DLL decorations, or they will not be exported from the object file. */ #if defined LIBFFI_HIDE_BASIC_TYPES # define FFI_EXTERN FFI_API #else # define FFI_EXTERN extern FFI_API #endif #ifndef LIBFFI_HIDE_BASIC_TYPES #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 #if LONG_MAX == 2147483647 # if FFI_LONG_LONG_MAX != FFI_64_BIT_MAX #error "no 64-bit data type supported" # endif #elif LONG_MAX != FFI_64_BIT_MAX #error "long size not supported" #endif #if LONG_MAX == 2147483647 # define ffi_type_ulong ffi_type_uint32 # define ffi_type_slong ffi_type_sint32 #elif LONG_MAX == FFI_64_BIT_MAX # define ffi_type_ulong ffi_type_uint64 # define ffi_type_slong ffi_type_sint64 #else #error "long size not supported" #endif /* These are defined in types.c. */ FFI_EXTERN ffi_type ffi_type_void; FFI_EXTERN ffi_type ffi_type_uint8; FFI_EXTERN ffi_type ffi_type_sint8; FFI_EXTERN ffi_type ffi_type_uint16; FFI_EXTERN ffi_type ffi_type_sint16; FFI_EXTERN ffi_type ffi_type_uint32; FFI_EXTERN ffi_type ffi_type_sint32; FFI_EXTERN ffi_type ffi_type_uint64; FFI_EXTERN ffi_type ffi_type_sint64; FFI_EXTERN ffi_type ffi_type_float; FFI_EXTERN ffi_type ffi_type_double; FFI_EXTERN ffi_type ffi_type_pointer; #if 0 FFI_EXTERN ffi_type ffi_type_longdouble; #else #define ffi_type_longdouble ffi_type_double #endif #ifdef FFI_TARGET_HAS_COMPLEX_TYPE FFI_EXTERN ffi_type ffi_type_complex_float; FFI_EXTERN ffi_type ffi_type_complex_double; #if 0 FFI_EXTERN ffi_type ffi_type_complex_longdouble; #else #define ffi_type_complex_longdouble ffi_type_complex_double #endif #endif #endif /* LIBFFI_HIDE_BASIC_TYPES */ typedef enum { FFI_OK = 0, FFI_BAD_TYPEDEF, FFI_BAD_ABI, FFI_BAD_ARGTYPE } ffi_status; typedef struct { ffi_abi abi; unsigned nargs; ffi_type **arg_types; ffi_type *rtype; unsigned bytes; unsigned flags; #ifdef FFI_EXTRA_CIF_FIELDS FFI_EXTRA_CIF_FIELDS; #endif } ffi_cif; /* ---- Definitions for the raw API -------------------------------------- */ #ifndef FFI_SIZEOF_ARG # if LONG_MAX == 2147483647 # define FFI_SIZEOF_ARG 4 # elif LONG_MAX == FFI_64_BIT_MAX # define FFI_SIZEOF_ARG 8 # endif #endif #ifndef FFI_SIZEOF_JAVA_RAW # define FFI_SIZEOF_JAVA_RAW FFI_SIZEOF_ARG #endif typedef union { ffi_sarg sint; ffi_arg uint; float flt; char data[FFI_SIZEOF_ARG]; void* ptr; } ffi_raw; #if FFI_SIZEOF_JAVA_RAW == 4 && FFI_SIZEOF_ARG == 8 /* This is a special case for mips64/n32 ABI (and perhaps others) where sizeof(void *) is 4 and FFI_SIZEOF_ARG is 8. */ typedef union { signed int sint; unsigned int uint; float flt; char data[FFI_SIZEOF_JAVA_RAW]; void* ptr; } ffi_java_raw; #else typedef ffi_raw ffi_java_raw; #endif FFI_API void ffi_raw_call (ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *avalue); FFI_API void ffi_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw); FFI_API void ffi_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args); FFI_API 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. */ #if !FFI_NATIVE_RAW_API FFI_API void ffi_java_raw_call (ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_java_raw *avalue) __attribute__((deprecated)); #endif FFI_API void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw) __attribute__((deprecated)); FFI_API void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args) __attribute__((deprecated)); FFI_API size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated)); /* ---- Definitions for closures ----------------------------------------- */ #if FFI_CLOSURES #ifdef _MSC_VER __declspec(align(8)) #endif typedef struct { #if 0 void *trampoline_table; void *trampoline_table_entry; #else union { char tramp[FFI_TRAMPOLINE_SIZE]; void *ftramp; }; #endif ffi_cif *cif; void (*fun)(ffi_cif*,void*,void**,void*); void *user_data; } ffi_closure #ifdef __GNUC__ __attribute__((aligned (8))) #endif ; #ifndef __GNUC__ # ifdef __sgi # pragma pack 0 # endif #endif FFI_API void *ffi_closure_alloc (size_t size, void **code); FFI_API void ffi_closure_free (void *); #if defined(PA_LINUX) || defined(PA_HPUX) #define FFI_CLOSURE_PTR(X) ((void *)((unsigned int)(X) | 2)) #define FFI_RESTORE_PTR(X) ((void *)((unsigned int)(X) & ~3)) #else #define FFI_CLOSURE_PTR(X) (X) #define FFI_RESTORE_PTR(X) (X) #endif FFI_API ffi_status ffi_prep_closure (ffi_closure*, ffi_cif *, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data) #if defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 405) __attribute__((deprecated ("use ffi_prep_closure_loc instead"))) #elif defined(__GNUC__) && __GNUC__ >= 3 __attribute__((deprecated)) #endif ; FFI_API ffi_status ffi_prep_closure_loc (ffi_closure*, ffi_cif *, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data, void*codeloc); #ifdef __sgi # pragma pack 8 #endif typedef struct { #if 0 void *trampoline_table; void *trampoline_table_entry; #else char tramp[FFI_TRAMPOLINE_SIZE]; #endif 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; typedef struct { #if 0 void *trampoline_table; void *trampoline_table_entry; #else char tramp[FFI_TRAMPOLINE_SIZE]; #endif 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 translation, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); void *this_closure; #endif void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*); void *user_data; } ffi_java_raw_closure; FFI_API ffi_status ffi_prep_raw_closure (ffi_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_raw*,void*), void *user_data); FFI_API ffi_status ffi_prep_raw_closure_loc (ffi_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_raw*,void*), void *user_data, void *codeloc); #if !FFI_NATIVE_RAW_API FFI_API ffi_status ffi_prep_java_raw_closure (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), void *user_data) __attribute__((deprecated)); FFI_API ffi_status ffi_prep_java_raw_closure_loc (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), void *user_data, void *codeloc) __attribute__((deprecated)); #endif #endif /* FFI_CLOSURES */ #if FFI_GO_CLOSURES typedef struct { void *tramp; ffi_cif *cif; void (*fun)(ffi_cif*,void*,void**,void*); } ffi_go_closure; FFI_API ffi_status ffi_prep_go_closure (ffi_go_closure*, ffi_cif *, void (*fun)(ffi_cif*,void*,void**,void*)); FFI_API void ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue, void *closure); #endif /* FFI_GO_CLOSURES */ /* ---- Public interface definition -------------------------------------- */ FFI_API ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs, ffi_type *rtype, ffi_type **atypes); FFI_API ffi_status ffi_prep_cif_var(ffi_cif *cif, ffi_abi abi, unsigned int nfixedargs, unsigned int ntotalargs, ffi_type *rtype, ffi_type **atypes); FFI_API void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue); FFI_API ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets); /* Useful for eliminating compiler warnings. */ #define FFI_FN(f) ((void (*)(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 0 #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 #define FFI_TYPE_COMPLEX 15 /* This should always refer to the last type code (for sanity checks). */ #define FFI_TYPE_LAST FFI_TYPE_COMPLEX #ifdef __cplusplus } #endif #endif ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_arm64/include/fficonfig.h0000644000175100001770000001513100000000000022320 0ustar00runnerdocker00000000000000/* fficonfig.h. Generated from fficonfig.h.in by configure. */ /* fficonfig.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ /* #undef AC_APPLE_UNIVERSAL_BUILD */ /* 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 to 1 if using `alloca.c'. */ /* #undef C_ALLOCA */ /* Define to the flags needed for the .section .eh_frame directive. */ /* #undef EH_FRAME_FLAGS */ /* Define this if you want extra debugging. */ /* #undef FFI_DEBUG */ /* Define this if you want statically defined trampolines */ /* #undef FFI_EXEC_STATIC_TRAMP */ /* Cannot use PROT_EXEC on this target, so, we revert to alternative means */ /* #undef FFI_EXEC_TRAMPOLINE_TABLE */ /* Define this if you want to enable pax emulated trampolines */ /* #undef FFI_MMAP_EXEC_EMUTRAMP_PAX */ /* Cannot use malloc on this target, so, we revert to alternative means */ /* #undef FFI_MMAP_EXEC_WRIT */ /* Define this if you do not want support for the raw API. */ /* #undef FFI_NO_RAW_API */ /* Define this if you do not want support for aggregate types. */ /* #undef FFI_NO_STRUCTS */ /* Define to 1 if you have `alloca', as a function or macro. */ #define HAVE_ALLOCA 1 /* Define to 1 if you have and it should be used (not on Ultrix). */ /* #undef HAVE_ALLOCA_H */ /* Define if your assembler supports .cfi_* directives. */ /* #undef HAVE_AS_CFI_PSEUDO_OP */ /* Define if your assembler supports .register. */ /* #undef HAVE_AS_REGISTER_PSEUDO_OP */ /* Define if the compiler uses zarch features. */ /* #undef HAVE_AS_S390_ZARCH */ /* Define if your assembler and linker support unaligned PC relative relocs. */ /* #undef HAVE_AS_SPARC_UA_PCREL */ /* Define if your assembler supports unwind section type. */ /* #undef HAVE_AS_X86_64_UNWIND_SECTION_TYPE */ /* Define if your assembler supports PC relative relocs. */ /* #undef HAVE_AS_X86_PCREL */ /* Define to 1 if you have the header file. */ /* #undef HAVE_DLFCN_H */ /* Define if __attribute__((visibility("hidden"))) is supported. */ /* #undef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE */ /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* Define if you have the long double type and it is bigger than a double */ /* #undef HAVE_LONG_DOUBLE */ /* Define if you support more than one size of the long double type */ /* #undef HAVE_LONG_DOUBLE_VARIANT */ /* Define to 1 if you have the `memcpy' function. */ /* #undef HAVE_MEMCPY */ /* Define to 1 if you have the `memfd_create' function. */ /* #undef HAVE_MEMFD_CREATE */ /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the `mkostemp' function. */ /* #undef HAVE_MKOSTEMP */ /* Define to 1 if you have the `mkstemp' function. */ /* #undef HAVE_MKSTEMP */ /* Define to 1 if you have the `mmap' function. */ /* #undef HAVE_MMAP */ /* Define if mmap with MAP_ANON(YMOUS) works. */ /* #undef HAVE_MMAP_ANON */ /* Define if mmap of /dev/zero works. */ /* #undef HAVE_MMAP_DEV_ZERO */ /* Define if read-only mmap of a plain file works. */ /* #undef HAVE_MMAP_FILE */ /* Define if your compiler supports pointer authentication. */ /* #undef HAVE_PTRAUTH */ /* Define if .eh_frame sections should be read-only. */ /* #undef HAVE_RO_EH_FRAME */ /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_STRINGS_H */ /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_MEMFD_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_MMAN_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_UNISTD_H */ /* Define to 1 if GNU symbol versioning is used for libatomic. */ /* #undef LIBFFI_GNU_SYMBOL_VERSIONING */ /* Define to the sub-directory where libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* Name of package */ #define PACKAGE "libffi" /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "http://github.com/libffi/libffi/issues" /* Define to the full name of this package. */ #define PACKAGE_NAME "libffi" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "libffi 3.4.2" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "libffi" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "3.4.2" /* The size of `double', as computed by sizeof. */ #define SIZEOF_DOUBLE 8 /* The size of `long double', as computed by sizeof. */ #define SIZEOF_LONG_DOUBLE 8 /* The size of `size_t', as computed by sizeof. */ #define SIZEOF_SIZE_T 8 /* 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 runtime. 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 to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if symbols are underscored. */ /* #undef SYMBOL_UNDERSCORE */ /* Define this if you are using Purify and want to suppress spurious messages. */ /* #undef USING_PURIFY */ /* Version number of package */ #define VERSION "3.4.2" /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN /* # undef WORDS_BIGENDIAN */ # endif #endif /* Define to `unsigned int' if does not define. */ /* #undef size_t */ #ifdef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE #ifdef LIBFFI_ASM #ifdef __APPLE__ #define FFI_HIDDEN(name) .private_extern name #else #define FFI_HIDDEN(name) .hidden name #endif #else #define FFI_HIDDEN __attribute__ ((visibility ("hidden"))) #endif #else #ifdef LIBFFI_ASM #define FFI_HIDDEN(name) #else #define FFI_HIDDEN #endif #endif ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_arm64/include/ffitarget.h0000644000175100001770000000527400000000000022350 0ustar00runnerdocker00000000000000/* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd. 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. */ #ifndef LIBFFI_TARGET_H #define LIBFFI_TARGET_H #ifndef LIBFFI_H #error "Please do not include ffitarget.h directly into your source. Use ffi.h instead." #endif #ifndef LIBFFI_ASM #ifdef __ILP32__ #define FFI_SIZEOF_ARG 8 #define FFI_SIZEOF_JAVA_RAW 4 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; #elif defined(_WIN32) #define FFI_SIZEOF_ARG 8 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; #else typedef unsigned long ffi_arg; typedef signed long ffi_sarg; #endif typedef enum ffi_abi { FFI_FIRST_ABI = 0, FFI_SYSV, FFI_WIN64, FFI_LAST_ABI, #if defined(_WIN32) FFI_DEFAULT_ABI = FFI_WIN64 #else FFI_DEFAULT_ABI = FFI_SYSV #endif } ffi_abi; #endif /* ---- Definitions for closures ----------------------------------------- */ #define FFI_CLOSURES 1 #define FFI_NATIVE_RAW_API 0 #if defined (FFI_EXEC_TRAMPOLINE_TABLE) && FFI_EXEC_TRAMPOLINE_TABLE #ifdef __MACH__ #define FFI_TRAMPOLINE_SIZE 16 #define FFI_TRAMPOLINE_CLOSURE_OFFSET 16 #else #error "No trampoline table implementation" #endif #else #define FFI_TRAMPOLINE_SIZE 24 #define FFI_TRAMPOLINE_CLOSURE_OFFSET FFI_TRAMPOLINE_SIZE #endif #ifdef _WIN32 #define FFI_EXTRA_CIF_FIELDS unsigned is_variadic #endif #define FFI_TARGET_SPECIFIC_VARIADIC /* ---- Internal ---- */ #if defined (__APPLE__) #define FFI_EXTRA_CIF_FIELDS unsigned aarch64_nfixedargs #elif !defined(_WIN32) /* iOS and Windows reserve x18 for the system. Disable Go closures until a new static chain is chosen. */ #define FFI_GO_CLOSURES 1 #endif #ifndef _WIN32 /* No complex type on Windows */ #define FFI_TARGET_HAS_COMPLEX_TYPE #endif #endif ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1397676 cffi-1.16.0/src/c/libffi_x86_x64/0000755000175100001770000000000000000000000016766 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/ffi.c0000644000175100001770000003033500000000000017702 0ustar00runnerdocker00000000000000/* ----------------------------------------------------------------------- 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 != 1 && z != 2 && z != 4 && 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 == 1 || cif->rtype->size == 2 || 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 || rtype == FFI_TYPE_LONGDOUBLE) { _asm mov eax, resp ; _asm fld QWORD PTR [eax] ; // asm ("fldl (%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 if (rtype == FFI_TYPE_STRUCT) { _asm mov eax, resp ; } #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 || rtype == FFI_TYPE_LONGDOUBLE) { asm ("fldl (%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"); } else if (rtype == FFI_TYPE_STRUCT) { asm ("movl %0,%%eax" : : "r" (resp) : "eax"); } #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. */ if (rtype == FFI_TYPE_STRUCT) return resp; 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 != 1 && z != 2 && z != 4 && 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; /* if we return a non-small struct, then the first argument is a pointer * to the return area, and all real arguments are shifted by one */ if (cif->flags == FFI_TYPE_STRUCT) mask = (mask & ~8) << 1; /* 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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/ffi.h0000644000175100001770000002212100000000000017701 0ustar00runnerdocker00000000000000/* -----------------------------------------------------------------*-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); /* AR: for cffi we need the following API, and not the _loc version */ #define ffi_prep_closure(a,b,c,d) ffi_prep_closure_loc(a,b,c,d,a) 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/ffi_common.h0000644000175100001770000000410200000000000021250 0ustar00runnerdocker00000000000000/* ----------------------------------------------------------------------- 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/fficonfig.h0000644000175100001770000000563500000000000021102 0ustar00runnerdocker00000000000000/* 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 */ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/ffitarget.h0000644000175100001770000000521300000000000021113 0ustar00runnerdocker00000000000000/* -----------------------------------------------------------------*-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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/prep_cif.c0000644000175100001770000001313000000000000020717 0ustar00runnerdocker00000000000000/* ----------------------------------------------------------------------- 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 != 1) /* MSVC returns small structs in registers */ && (cif->rtype->size != 2) && (cif->rtype->size != 4) && (cif->rtype->size != 8) #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); } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/types.c0000644000175100001770000000674400000000000020311 0ustar00runnerdocker00000000000000/* ----------------------------------------------------------------------- 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/win32.c0000644000175100001770000001115100000000000020073 0ustar00runnerdocker00000000000000/* ----------------------------------------------------------------------- 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 } } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/win64.asm0000644000175100001770000000603000000000000020436 0ustar00runnerdocker00000000000000PUBLIC 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/libffi_x86_x64/win64.obj0000644000175100001770000000223000000000000020426 0ustar00runnerdocker00000000000000d†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_AMD64././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/malloc_closure.h0000644000175100001770000001135400000000000017505 0ustar00runnerdocker00000000000000/* * 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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/minibuffer.h0000644000175100001770000003146100000000000016631 0ustar00runnerdocker00000000000000 /* 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; } } /* forward: from _cffi_backend.c */ static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only); static int mb_ass_slice(MiniBufferObj *self, Py_ssize_t left, Py_ssize_t right, PyObject *other) { Py_ssize_t count; Py_ssize_t size = self->mb_size; Py_buffer src_view; if (_fetch_as_buffer(other, &src_view, 0) < 0) return -1; if (left < 0) left = 0; if (right > size) right = size; if (left > right) left = right; count = right - left; if (count != src_view.len) { PyBuffer_Release(&src_view); PyErr_SetString(PyExc_ValueError, "right operand length must match slice length"); return -1; } memcpy(self->mb_data + left, src_view.buf, count); PyBuffer_Release(&src_view); 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; } static PyObject * mb_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t self_size, other_size; Py_buffer self_bytes, other_bytes; PyObject *res; Py_ssize_t minsize; int cmp, rc; /* Bytes can be compared to anything that supports the (binary) buffer API. Except that a comparison with Unicode is always an error, even if the comparison is for equality. */ rc = PyObject_IsInstance(self, (PyObject*)&PyUnicode_Type); if (!rc) rc = PyObject_IsInstance(other, (PyObject*)&PyUnicode_Type); if (rc < 0) return NULL; if (rc) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (PyObject_GetBuffer(self, &self_bytes, PyBUF_SIMPLE) != 0) { PyErr_Clear(); Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } self_size = self_bytes.len; if (PyObject_GetBuffer(other, &other_bytes, PyBUF_SIMPLE) != 0) { PyErr_Clear(); PyBuffer_Release(&self_bytes); Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } other_size = other_bytes.len; if (self_size != other_size && (op == Py_EQ || op == Py_NE)) { /* Shortcut: if the lengths differ, the objects differ */ cmp = (op == Py_NE); } else { minsize = self_size; if (other_size < minsize) minsize = other_size; cmp = memcmp(self_bytes.buf, other_bytes.buf, minsize); /* In ISO C, memcmp() guarantees to use unsigned bytes! */ if (cmp == 0) { if (self_size < other_size) cmp = -1; else if (self_size > other_size) cmp = 1; } switch (op) { case Py_LT: cmp = cmp < 0; break; case Py_LE: cmp = cmp <= 0; break; case Py_EQ: cmp = cmp == 0; break; case Py_NE: cmp = cmp != 0; break; case Py_GT: cmp = cmp > 0; break; case Py_GE: cmp = cmp >= 0; break; } } res = cmp ? Py_True : Py_False; PyBuffer_Release(&self_bytes); PyBuffer_Release(&other_bytes); Py_INCREF(res); return res; } #if PY_MAJOR_VERSION >= 3 /* pfffffffffffff pages of copy-paste from listobject.c */ /* pfffffffffffff#2: the PySlice_GetIndicesEx() *macro* should not be called, because C extension modules compiled with it differ on ABI between 3.6.0, 3.6.1 and 3.6.2. */ #if PY_VERSION_HEX < 0x03070000 && defined(PySlice_GetIndicesEx) && !defined(PYPY_VERSION) #undef PySlice_GetIndicesEx #endif 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 PyDoc_STRVAR(ffi_buffer_doc, "ffi.buffer(cdata[, byte_size]):\n" "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"); static PyObject * /* forward, implemented in _cffi_backend.c */ b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds); 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 */ ffi_buffer_doc, /* tp_doc */ (traverseproc)mb_traverse, /* tp_traverse */ (inquiry)mb_clear, /* tp_clear */ (richcmpfunc)mb_richcompare, /* tp_richcompare */ offsetof(MiniBufferObj, mb_weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ b_buffer_new, /* tp_new */ 0, /* tp_free */ }; 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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/misc_thread_common.h0000644000175100001770000003311300000000000020331 0ustar00runnerdocker00000000000000#ifndef WITH_THREAD # error "xxx no-thread configuration not tested, please report if you need that" #endif #include "pythread.h" struct cffi_tls_s { /* The current thread's ThreadCanaryObj. This is only non-null in case cffi builds the thread state here. It remains null if this thread had already a thread state provided by CPython. */ struct thread_canary_s *local_thread_canary; #ifndef USE__THREAD /* The saved errno. If the C compiler supports '__thread', then we use that instead. */ int saved_errno; #endif #ifdef MS_WIN32 /* The saved lasterror, on Windows. */ int saved_lasterror; #endif }; static struct cffi_tls_s *get_cffi_tls(void); /* in misc_thread_posix.h or misc_win32.h */ /* We try to keep the PyThreadState around in a thread not started by * Python but where cffi callbacks occur. If we didn't do that, then * the standard logic in PyGILState_Ensure() and PyGILState_Release() * would create a new PyThreadState and completely free it for every * single call. For some applications, this is a huge slow-down. * * As shown by issue #362, it is quite messy to do. The current * solution is to keep the PyThreadState alive by incrementing its * 'gilstate_counter'. We detect thread shut-down, and we put the * PyThreadState inside a list of zombies (we can't free it * immediately because we don't have the GIL at that point in time). * We also detect other pieces of code (notably Py_Finalize()) which * clear and free PyThreadStates under our feet, using ThreadCanaryObj. */ #define TLS_ZOM_LOCK() PyThread_acquire_lock(cffi_zombie_lock, WAIT_LOCK) #define TLS_ZOM_UNLOCK() PyThread_release_lock(cffi_zombie_lock) static PyThread_type_lock cffi_zombie_lock = NULL; /* A 'canary' object is created in a thread when there is a callback invoked, and that thread has no PyThreadState so far. It is an object of reference count equal to 1, which is stored in the PyThreadState->dict. Two things can occur then: 1. The PyThreadState can be forcefully cleared by Py_Finalize(). Then thread_canary_dealloc() is called, and we have to cancel the hacks we did to keep the PyThreadState alive. 2. The thread finishes. In that case, we put the canary in a list of zombies, and at some convenient time later when we have the GIL, we free all PyThreadStates in the zombie list. Some more fun comes from the fact that thread_canary_dealloc() can be called at a point where the canary is in the zombie list already. Also, the various pieces are freed at specific points in time, and we must make sure not to access already-freed structures: - the struct cffi_tls_s is valid until the thread shuts down, and then it is freed by cffi_thread_shutdown(). - the canary is a normal Python object, but we have a borrowed reference to it from cffi_tls_s.local_thread_canary. */ typedef struct thread_canary_s { PyObject_HEAD struct thread_canary_s *zombie_prev, *zombie_next; PyThreadState *tstate; struct cffi_tls_s *tls; } ThreadCanaryObj; static PyTypeObject ThreadCanary_Type; /* forward */ static ThreadCanaryObj cffi_zombie_head; static void _thread_canary_detach_with_lock(ThreadCanaryObj *ob) { /* must be called with both the GIL and TLS_ZOM_LOCK. */ ThreadCanaryObj *p, *n; p = ob->zombie_prev; n = ob->zombie_next; p->zombie_next = n; n->zombie_prev = p; ob->zombie_prev = NULL; ob->zombie_next = NULL; } static void thread_canary_dealloc(ThreadCanaryObj *ob) { /* this ThreadCanaryObj is being freed: if it is in the zombie chained list, remove it. Thread-safety: 'zombie_next' amd 'local_thread_canary' accesses need to be protected with the TLS_ZOM_LOCK. */ //fprintf(stderr, "entering dealloc(%p)\n", ob); TLS_ZOM_LOCK(); if (ob->zombie_next != NULL) { //fprintf(stderr, "thread_canary_dealloc(%p): ZOMBIE\n", ob); _thread_canary_detach_with_lock(ob); } else { //fprintf(stderr, "thread_canary_dealloc(%p): not a zombie\n", ob); } if (ob->tls != NULL) { //fprintf(stderr, "thread_canary_dealloc(%p): was local_thread_canary\n", ob); assert(ob->tls->local_thread_canary == ob); ob->tls->local_thread_canary = NULL; } TLS_ZOM_UNLOCK(); PyObject_Del((PyObject *)ob); } static void thread_canary_make_zombie(ThreadCanaryObj *ob) { /* This must be called without the GIL, but with the TLS_ZOM_LOCK. It must be called at most once for a given ThreadCanaryObj. */ ThreadCanaryObj *last; //fprintf(stderr, "thread_canary_make_zombie(%p)\n", ob); if (ob->zombie_next) Py_FatalError("cffi: ThreadCanaryObj is already a zombie"); last = cffi_zombie_head.zombie_prev; ob->zombie_next = &cffi_zombie_head; ob->zombie_prev = last; last->zombie_next = ob; cffi_zombie_head.zombie_prev = ob; //fprintf(stderr, "thread_canary_make_zombie(%p) DONE\n", ob); } static void thread_canary_free_zombies(void) { /* This must be called with the GIL. */ if (cffi_zombie_head.zombie_next == &cffi_zombie_head) return; /* fast path */ while (1) { ThreadCanaryObj *ob; PyThreadState *tstate = NULL; TLS_ZOM_LOCK(); ob = cffi_zombie_head.zombie_next; if (ob != &cffi_zombie_head) { tstate = ob->tstate; //fprintf(stderr, "thread_canary_free_zombie(%p) tstate=%p\n", ob, tstate); _thread_canary_detach_with_lock(ob); if (tstate == NULL) Py_FatalError("cffi: invalid ThreadCanaryObj->tstate"); } TLS_ZOM_UNLOCK(); if (tstate == NULL) break; PyThreadState_Clear(tstate); /* calls thread_canary_dealloc on 'ob', but now ob->zombie_next == NULL. */ #if PY_VERSION_HEX >= 0x030C0000 /* this might be a bug introduced in 3.12, or just me abusing the API around there. The issue is that PyThreadState_Delete() called on a random old tstate will clear the *current* thread notion of what PyGILState_GetThisThreadState() should be, at least if the internal 'bound_gilstate' flag is set in the old tstate. Workaround: clear that flag. */ tstate->_status.bound_gilstate = 0; #endif PyThreadState_Delete(tstate); //fprintf(stderr, "thread_canary_free_zombie: cleared and deleted tstate=%p\n", tstate); } //fprintf(stderr, "thread_canary_free_zombie: end\n"); } static void thread_canary_register(PyThreadState *tstate) { /* called with the GIL; 'tstate' is the current PyThreadState. */ ThreadCanaryObj *canary; PyObject *tdict; struct cffi_tls_s *tls; int err; /* first free the zombies, if any */ thread_canary_free_zombies(); tls = get_cffi_tls(); if (tls == NULL) goto ignore_error; tdict = PyThreadState_GetDict(); if (tdict == NULL) goto ignore_error; canary = PyObject_New(ThreadCanaryObj, &ThreadCanary_Type); //fprintf(stderr, "thread_canary_register(%p): tstate=%p tls=%p\n", canary, tstate, tls); if (canary == NULL) goto ignore_error; canary->zombie_prev = NULL; canary->zombie_next = NULL; canary->tstate = tstate; canary->tls = tls; err = PyDict_SetItemString(tdict, "cffi.thread.canary", (PyObject *)canary); Py_DECREF(canary); if (err < 0) goto ignore_error; /* thread-safety: we have the GIL here, and 'tstate' is the one that corresponds to our own thread. We are allocating a new 'canary' and setting it up for our own thread, both in 'tdict' (which owns the reference) and in 'tls->local_thread_canary' (which doesn't). */ assert(Py_REFCNT(canary) == 1); tls->local_thread_canary = canary; tstate->gilstate_counter++; /* ^^^ this means 'tstate' will never be automatically freed by PyGILState_Release() */ //fprintf(stderr, "CANARY: ready, tstate=%p, tls=%p, canary=%p\n", tstate, tls, canary); return; ignore_error: //fprintf(stderr, "CANARY: IGNORED ERROR\n"); PyErr_Clear(); } static PyTypeObject ThreadCanary_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.thread_canary", sizeof(ThreadCanaryObj), 0, (destructor)thread_canary_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, /* tp_flags */ }; static void init_cffi_tls_zombie(void) { cffi_zombie_head.zombie_next = &cffi_zombie_head; cffi_zombie_head.zombie_prev = &cffi_zombie_head; cffi_zombie_lock = PyThread_allocate_lock(); if (cffi_zombie_lock == NULL) PyErr_SetString(PyExc_SystemError, "can't allocate cffi_zombie_lock"); } static void cffi_thread_shutdown(void *p) { /* this function is called from misc_thread_posix or misc_win32 when a thread is about to end. */ struct cffi_tls_s *tls = (struct cffi_tls_s *)p; /* thread-safety: this field 'local_thread_canary' can be reset to NULL in parallel, protected by TLS_ZOM_LOCK. */ TLS_ZOM_LOCK(); if (tls->local_thread_canary != NULL) { tls->local_thread_canary->tls = NULL; thread_canary_make_zombie(tls->local_thread_canary); } TLS_ZOM_UNLOCK(); //fprintf(stderr, "thread_shutdown(%p)\n", tls); 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 /* MESS. We can't use PyThreadState_GET(), because that calls PyThreadState_Get() which fails an assert if the result is NULL. * in Python 2.7 and <= 3.4, the variable _PyThreadState_Current is directly available, so use that. * in Python 3.5, the variable is available too, but it might be the case that the headers don't define it (this changed in 3.5.1). In case we're compiling with 3.5.x with x >= 1, we need to manually define this variable. * in Python >= 3.6 there is _PyThreadState_UncheckedGet(). It was added in 3.5.2 but should never be used in 3.5.x because it is not available in 3.5.0 or 3.5.1. */ #if PY_VERSION_HEX >= 0x03050100 && PY_VERSION_HEX < 0x03060000 PyAPI_DATA(void *volatile) _PyThreadState_Current; #endif static PyThreadState *get_current_ts(void) { #if PY_VERSION_HEX >= 0x03060000 return _PyThreadState_UncheckedGet(); #elif defined(_Py_atomic_load_relaxed) return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current); #else return (PyThreadState*)_PyThreadState_Current; /* assume atomic read */ #endif } static PyGILState_STATE gil_ensure(void) { /* Called at the start of a callback. Replacement for PyGILState_Ensure(). */ PyGILState_STATE result; PyThreadState *ts = PyGILState_GetThisThreadState(); //fprintf(stderr, "%p: gil_ensure(), tstate=%p, tls=%p\n", get_cffi_tls(), ts, get_cffi_tls()); 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); //fprintf(stderr, "%p: gil_ensure(), tstate=%p MADE CURRENT\n", get_cffi_tls(), ts); return PyGILState_UNLOCKED; } else { //fprintf(stderr, "%p: gil_ensure(), tstate=%p ALREADY CURRENT\n", get_cffi_tls(), ts); return PyGILState_LOCKED; } } else { /* no thread state here so far. */ result = PyGILState_Ensure(); assert(result == PyGILState_UNLOCKED); ts = PyGILState_GetThisThreadState(); //fprintf(stderr, "%p: gil_ensure(), made a new tstate=%p\n", get_cffi_tls(), ts); assert(ts != NULL); assert(ts == get_current_ts()); assert(ts->gilstate_counter >= 1); /* Use the ThreadCanary mechanism to keep 'ts' alive until the thread really shuts down */ thread_canary_register(ts); assert(ts == PyGILState_GetThisThreadState()); return result; } } static void gil_release(PyGILState_STATE oldstate) { //fprintf(stderr, "%p: gil_release(%d), tls=%p\n", get_cffi_tls(), (int)oldstate, get_cffi_tls()); PyGILState_Release(oldstate); } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/misc_thread_posix.h0000644000175100001770000000260300000000000020203 0ustar00runnerdocker00000000000000/* 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/misc_win32.h0000644000175100001770000001435300000000000016461 0ustar00runnerdocker00000000000000#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); Py_DECREF(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; } 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 *)LoadLibraryA(filename); } static void *dlopenW(const wchar_t *filename) { return (void *)LoadLibraryW(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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/parse_c_type.c0000644000175100001770000006333200000000000017155 0ustar00runnerdocker00000000000000#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; if (tok->size == 8 && !memcmp(p,"_Complex",8)) tok->kind = TOK__COMPLEX; 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; if (size == 8 && !memcmp(p, "char16", 6)) return _CFFI_PRIM_CHAR16; 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; if (size == 8 && !memcmp(p, "char32", 6)) return _CFFI_PRIM_CHAR32; 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; _cffi_opcode_t t1complex; 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; } t1complex = 0; 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: case TOK__COMPLEX: 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); t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOATCOMPLEX); break; case TOK_DOUBLE: t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLE); t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLECOMPLEX); 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); } if (tok->kind == TOK__COMPLEX) { if (t1complex == 0) return parse_error(tok, "_Complex type combination unsupported"); t1 = t1complex; 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); } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/realize_c_type.c0000644000175100001770000006277200000000000017505 0ustar00runnerdocker00000000000000 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", "float _Complex", "double _Complex", "char16_t", "char32_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 { unexpected_fn_type(x); Py_DECREF(x); return NULL; } } 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_now(builder_c_t *builder, _cffi_opcode_t op, _cffi_opcode_t opcodes[], int index) { PyObject *x, *y, *z; Py_ssize_t length = -1; 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; } return x; } static int _realize_recursion_level; static PyObject * realize_c_type_or_func(builder_c_t *builder, _cffi_opcode_t opcodes[], int index) { PyObject *x; _cffi_opcode_t op = opcodes[index]; if ((((uintptr_t)op) & 1) == 0) { x = (PyObject *)op; Py_INCREF(x); return x; } if (_realize_recursion_level >= 1000) { PyErr_Format(PyExc_RuntimeError, "type-building recursion too deep or infinite. " "This is known to occur e.g. in ``struct s { void(*callable)" "(struct s); }''. Please report if you get this error and " "really need support for your case."); return NULL; } _realize_recursion_level++; x = realize_c_type_or_func_now(builder, op, opcodes, index); _realize_recursion_level--; 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 CTypeDescrObject * realize_c_func_return_type(builder_c_t *builder, _cffi_opcode_t opcodes[], int index) { PyObject *x; _cffi_opcode_t op = opcodes[index]; if ((((uintptr_t)op) & 1) == 0) { /* already built: assert that it is a function and fish for the return type */ x = (PyObject *)op; assert(PyTuple_Check(x)); /* from _CFFI_OP_FUNCTION */ x = PyTuple_GET_ITEM(x, 0); assert(CTypeDescr_Check(x)); assert(((CTypeDescrObject *)x)->ct_flags & CT_FUNCTIONPTR); x = PyTuple_GET_ITEM(((CTypeDescrObject *)x)->ct_stuff, 1); assert(CTypeDescr_Check(x)); Py_INCREF(x); return (CTypeDescrObject *)x; } else { assert(_CFFI_GETOP(op) == _CFFI_OP_FUNCTION); return realize_c_type(builder, opcodes, _CFFI_GETARG(opcodes[index])); } } 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 (ctf != NULL && 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 (ctf == NULL || 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; } } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/test_c.py0000644000175100001770000050403300000000000016165 0ustar00runnerdocker00000000000000import pytest import sys is_musl = False if sys.platform == 'linux': try: from packaging.tags import platform_tags is_musl = any(t.startswith('musllinux') for t in platform_tags()) del platform_tags except ImportError: pass def _setup_path(): import os, sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) _setup_path() from _cffi_backend import * from _cffi_backend import _get_types, _get_common_types try: from _cffi_backend import _testfunc except ImportError: def _testfunc(num): pytest.skip("_testunc() not available") from _cffi_backend import __version__ # ____________________________________________________________ import sys assert __version__ == "1.16.0", ("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 strict_compare = False 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") strict_compare = True 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) if path is None and sys.platform == 'darwin' and sys.version_info[:2] == (3, 8): pytest.xfail("find_library usually broken on MacOS Python 3.8") if path is None and name == 'c': assert sys.platform == 'win32' assert (sys.version_info >= (3,) or '__pypy__' in sys.builtin_module_names) pytest.skip("dlopen(None) cannot work on Windows " "with PyPy or Python 3") 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 = [name for name in dir(p) if not name.startswith('_')] assert got == sorted(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._CDataBase'>" % 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 True assert (x != cast(p, -66)) is False q = new_primitive_type("short") assert (x == cast(q, -66)) is True assert (x != cast(q, -66)) is False def test_sizeof_type(): pytest.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 pytest.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') pytest.raises(TypeError, float, cast(p, 42)) pytest.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)) is False # since 1.7 assert bool(cast(p, -0.0)) is False # since 1.7 assert bool(cast(p, 1e-42)) is True assert bool(cast(p, -1e-42)) is True assert bool(cast(p, INF)) assert bool(cast(p, -INF)) assert bool(cast(p, float("nan"))) 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 pytest.raises(OverflowError, int, cast(p, INF)) pytest.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 pytest.raises(TypeError, cast, p, None) def test_complex_types(): INF = 1E200 * 1E200 for name in ["float", "double"]: p = new_primitive_type(name + " _Complex") assert bool(cast(p, 0)) is False assert bool(cast(p, INF)) assert bool(cast(p, -INF)) assert bool(cast(p, 0j)) is False assert bool(cast(p, INF*1j)) assert bool(cast(p, -INF*1j)) # "can't convert complex to float", like CPython's "float(0j)" pytest.raises(TypeError, int, cast(p, -150)) pytest.raises(TypeError, long, cast(p, -150)) pytest.raises(TypeError, float, cast(p, -150)) assert complex(cast(p, 1.25)) == 1.25 assert complex(cast(p, 1.25j)) == 1.25j assert complex(cast(p, complex(0,INF))) == complex(0,INF) assert complex(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, complex(3,1E200))) == complex(3,INF) # 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' # http://bugs.python.org/issue29602 assert complex(cast(p, b'\x09')) == 9.0 + 0j assert complex(cast(p, u+'\x09')) == 9.0 + 0j assert complex(cast(p, True)) == 1.0 + 0j pytest.raises(TypeError, cast, p, None) # pytest.raises(TypeError, cast, new_primitive_type(name), 1+0j) # for basetype in ["char", "int", "uint64_t", "float", "double", "long double"]: baseobj = cast(new_primitive_type(basetype), 65) pytest.raises(TypeError, complex, baseobj) # BArray = new_array_type(new_pointer_type(p), 10) x = newp(BArray, None) x[5] = 12.34 + 56.78j assert type(x[5]) is complex assert abs(x[5] - (12.34 + 56.78j)) < 1e-5 assert (x[5] == 12.34 + 56.78j) == (name == "double") # rounding error # class Foo: def __complex__(self): return 2 + 3j assert complex(Foo()) == 2 + 3j assert complex(cast(p, Foo())) == 2 + 3j pytest.raises(TypeError, cast, new_primitive_type("int"), 1+0j) def test_character_type(): p = new_primitive_type("char") assert bool(cast(p, 'A')) is True assert bool(cast(p, '\x00')) is False # since 1.7 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") pytest.raises(TypeError, newp, BInt) pytest.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 with pytest.raises(IndexError): p[1] with pytest.raises(IndexError): p[-1] def test_reading_pointer_to_float(): BFloat = new_primitive_type("float") pytest.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 pytest.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 pytest.raises(OverflowError, newp, pp, min - 2 ** 32) pytest.raises(OverflowError, newp, pp, min - 2 ** 64) pytest.raises(OverflowError, newp, pp, max + 2 ** 32) pytest.raises(OverflowError, newp, pp, max + 2 ** 64) pytest.raises(OverflowError, newp, pp, min - 1) pytest.raises(OverflowError, newp, pp, max + 1) pytest.raises(OverflowError, newp, pp, min - 1 - 2 ** 32) pytest.raises(OverflowError, newp, pp, min - 1 - 2 ** 64) pytest.raises(OverflowError, newp, pp, max + 1) pytest.raises(OverflowError, newp, pp, max + 1 + 2 ** 32) pytest.raises(OverflowError, newp, pp, max + 1 + 2 ** 64) pytest.raises(TypeError, newp, pp, 1.0) 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 pytest.raises(OverflowError, newp, pp, -1) pytest.raises(OverflowError, newp, pp, max + 1) def test_reading_pointer_to_char(): BChar = new_primitive_type("char") pytest.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' pytest.raises(TypeError, newp, BPtr, 65) pytest.raises(TypeError, newp, BPtr, b"foo") pytest.raises(TypeError, newp, BPtr, u+"foo") c = cast(BChar, b'A') assert str(c) == repr(c) assert int(c) == ord(b'A') pytest.raises(TypeError, cast, BChar, b'foo') pytest.raises(TypeError, cast, BChar, u+'foo') e = pytest.raises(TypeError, newp, new_array_type(BPtr, None), 12.3) assert str(e.value) == ( "expected new array length or list/tuple/str, not float") 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": pytest.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') pytest.raises(AttributeError, 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') # x.close_lib() pytest.raises(ValueError, x.load_function, BVoidP, 'sqrt') x.close_lib() def test_no_len_on_nonarray(): p = new_primitive_type("int") pytest.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) with pytest.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) == "" # pytest.raises(TypeError, new_array_type, new_pointer_type(p), "foo") pytest.raises(ValueError, new_array_type, new_pointer_type(p), -42) # p1 = new_array_type(new_pointer_type(p), None) assert repr(p1) == "" pytest.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) == "" # pytest.raises(OverflowError, new_array_type, new_pointer_type(p), sys.maxsize+1) pytest.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 with pytest.raises(IndexError): a[LENGTH] with pytest.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 with pytest.raises(IndexError) as e: a[LENGTH+100] = 500 assert ('(expected %d < %d)' % (LENGTH+100, LENGTH)) in str(e.value) pytest.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) pytest.raises(TypeError, newp, p1, None) pytest.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 with pytest.raises(IndexError): a[42] with pytest.raises(IndexError): a[-1] with pytest.raises(IndexError): a[42] = 123 with pytest.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) == "" pytest.raises(ValueError, sizeof, BStruct) pytest.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, 42) with pytest.raises(AttributeError) as e: p.a1 # opaque assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: " "cannot read fields") with pytest.raises(AttributeError) as e: p.a1 = 10 # opaque assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: " "cannot write fields") 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 with pytest.raises(OverflowError): s.a1 = sys.maxsize+1 assert s.a1 == 0 with pytest.raises(AttributeError) as e: p.foobar assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" with pytest.raises(AttributeError) as e: p.foobar = 42 assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" with pytest.raises(AttributeError) as e: s.foobar assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" with pytest.raises(AttributeError) as e: s.foobar = 42 assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" j = cast(BInt, 42) with pytest.raises(AttributeError) as e: j.foobar assert str(e.value) == "cdata 'int' has no attribute 'foobar'" with pytest.raises(AttributeError) as e: j.foobar = 42 assert str(e.value) == "cdata 'int' has no attribute 'foobar'" j = cast(new_pointer_type(BInt), 42) with pytest.raises(AttributeError) as e: j.foobar assert str(e.value) == "cdata 'int *' has no attribute 'foobar'" with pytest.raises(AttributeError) as e: j.foobar = 42 assert str(e.value) == "cdata 'int *' has no attribute 'foobar'" pp = newp(new_pointer_type(BStructPtr), p) with pytest.raises(AttributeError) as e: pp.a1 assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" with pytest.raises(AttributeError) as e: pp.a1 = 42 assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" 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 pytest.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) # pytest.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 # pytest.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") pytest.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 pytest.raises(OverflowError, f, 128, 0) pytest.raises(OverflowError, f, 0, 128) def test_call_function_0_pretend_bool_result(): BSignedChar = new_primitive_type("signed char") BBool = new_primitive_type("_Bool") BFunc0 = new_function_type((BSignedChar, BSignedChar), BBool, False) f = cast(BFunc0, _testfunc(0)) assert f(40, -39) is True assert f(40, -40) is False pytest.raises(ValueError, f, 40, 2) 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 # pytest.raises(TypeError, f, 123456) pytest.raises(TypeError, f, "foo") pytest.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' pytest.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 pytest.raises(TypeError, f, None) pytest.raises(TypeError, f, 0) pytest.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_call_function_23_bool_array(): # declaring the function as int(_Bool*) BBool = new_primitive_type("_Bool") BBoolP = new_pointer_type(BBool) BInt = new_primitive_type("int") BFunc23 = new_function_type((BBoolP,), BInt, False) f = cast(BFunc23, _testfunc(23)) res = f(b"\x01\x01") assert res == 1000 pytest.raises(ValueError, f, b"\x02\x02") 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) pytest.raises(NotImplementedError, cast(BFunc, 123), cast(BStructP, 123)) BFunc2 = new_function_type((BInt,), BStruct, False) pytest.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 pytest.raises(TypeError, f, 1, 42) pytest.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_call_function_24(): BFloat = new_primitive_type("float") BFloatComplex = new_primitive_type("float _Complex") BFunc3 = new_function_type((BFloat, BFloat), BFloatComplex, False) if 0: # libffi returning nonsense silently, so logic disabled for now f = cast(BFunc3, _testfunc(24)) result = f(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact else: f = cast(BFunc3, _testfunc(9)) pytest.raises(NotImplementedError, f, 12.3, 34.5) def test_call_function_25(): BDouble = new_primitive_type("double") BDoubleComplex = new_primitive_type("double _Complex") BFunc3 = new_function_type((BDouble, BDouble), BDoubleComplex, False) if 0: # libffi returning nonsense silently, so logic disabled for now f = cast(BFunc3, _testfunc(25)) result = f(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-10) # inexact else: f = cast(BFunc3, _testfunc(9)) pytest.raises(NotImplementedError, f, 12.3, 34.5) 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 = pytest.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; or, it contains anonymous struct/unions. " "Such structs are only supported as argument if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder." "cdef()+ffibuilder.set_source() and not taking a final '...' " "argument)") 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"): pytest.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)) # ll.close_lib() pytest.raises(ValueError, ll.read_variable, BVoidP, "stderr") 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"): pytest.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("= (3, 8): ipattern = ipattern38 if sys.version_info >= (3, 11): ipattern = ipattern311 or ipattern38 str, pattern = istr, ipattern while '$' in pattern: i = pattern.index('$') assert str[:i] == pattern[:i] j = str.find(pattern[i+1], i) assert i + 1 <= j <= str.find('\n', i) str = str[j:] pattern = pattern[i+1:] assert str == pattern return True def check_value(x): if x == 10000: raise ValueError(42) def Zcb1(x): check_value(x) return x * 3 BShort = new_primitive_type("short") BFunc = new_function_type((BShort,), BShort, False) f = callback(BFunc, Zcb1, -42) # seen = [] oops_result = None def oops(*args): seen.append(args) return oops_result ff = callback(BFunc, Zcb1, -42, oops) # orig_stderr = sys.stderr orig_getline = linecache.getline try: linecache.getline = lambda *args: 'LINE' # hack: speed up PyPy tests sys.stderr = cStringIO.StringIO() if hasattr(sys, '__unraisablehook__'): # work around pytest sys.unraisablehook = sys.__unraisablehook__ # on recent CPythons assert f(100) == 300 assert sys.stderr.getvalue() == '' assert f(10000) == -42 assert matches(sys.stderr.getvalue(), """\ From cffi callback : Traceback (most recent call last): File "$", line $, in Zcb1 $ File "$", line $, in check_value $ ValueError: 42 """, """\ Exception ignored from cffi callback : 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' """, """\ Exception ignored from cffi callback , trying to convert the result back to C: Traceback (most recent call last): File "$", line $, in test_callback_exception $ 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$ """, """\ Exception ignored from cffi callback , trying to convert the result back to C: Traceback (most recent call last): File "$", line $, in test_callback_exception $ OverflowError: integer 60000 does not fit 'short' Exception ignored during handling of the above exception by 'onerror': Traceback (most recent call last): File "$", line $, in test_callback_exception $ TypeError: $integer$ """) # sys.stderr = cStringIO.StringIO() seen = "not a list" # this makes the oops() function crash assert ff(bigvalue) == -42 # the $ after the AttributeError message are for the suggestions that # will be added in Python 3.10 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$ """, """\ Exception ignored from cffi callback , trying to convert the result back to C: Traceback (most recent call last): File "$", line $, in test_callback_exception $ OverflowError: integer 60000 does not fit 'short' Exception ignored during handling of the above exception by 'onerror': Traceback (most recent call last): File "$", line $, in oops $ AttributeError: 'str' object has no attribute 'append$ """, """\ Exception ignored from cffi callback , trying to convert the result back to C: Traceback (most recent call last): File "$", line $, in test_callback_exception $ OverflowError: integer 60000 does not fit 'short' Exception ignored during handling of the above exception by 'onerror': 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' goes 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] pytest.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 = pytest.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 with pytest.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 pytest.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 with pytest.raises(OverflowError): p.a1 = 2 assert p.a1 == 0 # p.a1 = -1 p.a2 = 3 p.a3 = -4 with pytest.raises(OverflowError): p.a3 = 4 with pytest.raises(OverflowError) as e: 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 with pytest.raises(OverflowError) as e: 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" with pytest.raises(IndexError) as e: 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) with pytest.raises(TypeError): x + 1 with pytest.raises(TypeError): x - 1 def test_void_errors(): pytest.raises(ValueError, alignof, new_void_type()) pytest.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) pytest.raises(IndexError, newp, BArray, tuple(b'123456')) pytest.raises(IndexError, newp, BArray, list(b'123456')) pytest.raises(IndexError, newp, BArray, b'123456') BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, []) pytest.raises(TypeError, newp, new_pointer_type(BStruct), b'') pytest.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) pytest.raises(TypeError, newp, BArray, 12.34) BArray = new_array_type(new_pointer_type(BInt), 5) pytest.raises(TypeError, newp, BArray, 12.34) BFloat = new_primitive_type("float") pytest.raises(TypeError, cast, BFloat, newp(BArray, None)) def test_more_overflow_errors(): BUInt = new_primitive_type("unsigned int") pytest.raises(OverflowError, newp, new_pointer_type(BUInt), -1) pytest.raises(OverflowError, newp, new_pointer_type(BUInt), 2**32) def test_newp_copying(): """Test that we can do newp(, ) for most types, including same-type arrays. """ 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]) pytest.raises(TypeError, newp, BArray, a1) BArray6 = new_array_type(new_pointer_type(BInt), 6) a1 = newp(BArray6, [10, 20, 30]) a2 = newp(BArray6, a1) assert list(a2) == [10, 20, 30, 0, 0, 0] # 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" pytest.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(): for typename in ["wchar_t", "char16_t", "char32_t"]: _test_string_wchar_variant(typename) def _test_string_wchar_variant(typename): BWChar = new_primitive_type(typename) 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]) pytest.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) pytest.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) pytest.raises(NotImplementedError, cast(BFunc, 123)) pytest.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)]) pytest.raises(TypeError, newp, BStructPtr, [cast(BIntP, 0)]) pytest.raises(TypeError, newp, BStructPtr, [cast(BFunc2, 0)]) def test_wchar(): _test_wchar_variant("wchar_t") if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 # wchar_t is often signed on Linux, but not always (e.g. on ARM) assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") assert sizeof(BChar16) == 2 _test_wchar_variant("char16_t") assert int(cast(BChar16, -1)) == 0xffff # always unsigned def test_char32(): BChar32 = new_primitive_type("char32_t") assert sizeof(BChar32) == 4 _test_wchar_variant("char32_t") assert int(cast(BChar32, -1)) == 0xffffffff # always unsigned def _test_wchar_variant(typename): BWChar = new_primitive_type(typename) 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)) == "" % ( typename, mandatory_u_prefix) assert str(cast(BWChar, 0x1234)) == "" % ( typename, mandatory_u_prefix) if not _hacked_pypy_uni4(): if wchar4: x = cast(BWChar, 0x12345) assert str(x) == "" % ( typename, mandatory_u_prefix) assert int(x) == 0x12345 else: x = cast(BWChar, 0x18345) assert str(x) == "" % ( typename, mandatory_u_prefix) assert int(x) == 0x8345 # 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' with pytest.raises(TypeError): s.a1 = b'a' with pytest.raises(TypeError): s.a1 = bytechr(0xFF) s.a1 = u+'\u1234' assert s.a1 == u+'\u1234' if pyuni4: if 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: with pytest.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 with pytest.raises(IndexError): a[4] # w = cast(BWChar, 'a') assert repr(w) == "" % (typename, 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) == "" % (typename, 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) == "" % (typename, 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) == "" % ( typename, 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) == "" pytest.raises(TypeError, cast, BInt, u+'') pytest.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) pytest.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) with pytest.raises(TypeError): p[0] p = cast(BVoidP, 0) with pytest.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 # pytest.raises(TypeError, iter, cast(BInt, 5)) pytest.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) assert (p == q) is False assert (p != q) is True assert (q == p) is False assert (q != p) is True if strict_compare: with pytest.raises(TypeError): p < q with pytest.raises(TypeError): p <= q with pytest.raises(TypeError): q < p with pytest.raises(TypeError): q <= p with pytest.raises(TypeError): p > q with pytest.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" assert type(buf) is buffer 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: with pytest.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 pytest.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 pytest.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: with pytest.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")) with pytest.raises(ValueError): buf[:] = b"shorter" with pytest.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'): pytest.skip("cannot run this test on py.py (e.g. fails on Windows)") 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] pytest.raises(TypeError, cast, BStruct, s) def test_bug_float_convertion(): BDouble = new_primitive_type("double") BDoubleP = new_pointer_type(BDouble) pytest.raises(TypeError, newp, BDoubleP, "foobar") def test_bug_delitem(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) x = newp(BCharP) with pytest.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)) with pytest.raises(AttributeError): del x.a1 def test_variable_length_struct(): pytest.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) # pytest.raises(TypeError, newp, BStructP, None) x = newp(BStructP, 5) assert sizeof(x) == 6 * size_of_long() x[4] = 123 assert x[4] == 123 with pytest.raises(IndexError): x[5] assert len(x.a2) == 5 # pytest.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 pytest.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": pytest.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": pytest.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 = pytest.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": pytest.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": pytest.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) pytest.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 with pytest.raises(IndexError): d[3] with pytest.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 with pytest.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) with pytest.raises(IndexError) as e: c[:5] assert str(e.value) == "slice start must be specified" with pytest.raises(IndexError) as e: c[4:] assert str(e.value) == "slice stop must be specified" with pytest.raises(IndexError) as e: c[1:2:3] assert str(e.value) == "slice with step not supported" with pytest.raises(IndexError) as e: c[1:2:1] assert str(e.value) == "slice with step not supported" with pytest.raises(IndexError) as e: c[4:2] assert str(e.value) == "slice start > stop" with pytest.raises(IndexError) as e: 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] with pytest.raises(ValueError): cp[-1:1] = [1000] assert list(c) == [0, 100, 1000, 600, 0] with pytest.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 pytest.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 buffer_warning(cdata): import warnings buf = buffer(cdata) bytes = len(buf) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") buffer(cdata, bytes) assert len(w) == 0 buffer(cdata, bytes + 1) assert len(w) <= 1 return len(w) == 1 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") pytest.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 == -2 assert d[1][1].bitsize == -1 # p = newp(new_pointer_type(BStruct)) p.x = 42 assert p.x == 42 assert typeof(p.y) is BArray assert len(p.y) == 0 assert p.y == cast(BIntP, p) + 1 # p = newp(new_pointer_type(BStruct), [100]) assert p.x == 100 assert len(p.y) == 0 # # 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 assert len(p.y) == 3 assert len(p[0].y) == 3 assert len(buffer(p)) == sizeof(BInt) * 4 assert sizeof(p[0]) == sizeof(BInt) * 4 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) == [200, i, 400] # # the following assignment works, as it normally would, for any array field p.y = [501, 601] assert list(p.y) == [501, 601, 400] p[0].y = [500, 600] assert list(p[0].y) == [500, 600, 400] assert repr(p) == "" % ( sizeof(BStruct) + 3 * sizeof(BInt),) assert repr(p[0]) == "" % ( sizeof(BStruct) + 3 * sizeof(BInt),) assert sizeof(p[0]) == sizeof(BStruct) + 3 * sizeof(BInt) # # from a non-owning pointer, we can't get the length q = cast(new_pointer_type(BStruct), p) assert q.y[0] == 500 assert q[0].y[0] == 500 pytest.raises(TypeError, len, q.y) pytest.raises(TypeError, len, q[0].y) assert typeof(q.y) is BIntP assert typeof(q[0].y) is BIntP assert sizeof(q[0]) == sizeof(BStruct) # # error cases with pytest.raises(IndexError): p.y[4] with pytest.raises(TypeError): p.y = cast(BIntP, 0) with pytest.raises(TypeError): p.y = 15 with pytest.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 pytest.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 pytest.raises(TypeError, newp, new_pointer_type(BStruct4), [None]) pytest.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 assert buffer_warning(p) # # struct of struct of varsized array BStruct2 = new_struct_type("bar") complete_struct_or_union(BStruct2, [('head', BInt), ('tail', BStruct)]) for i in range(2): # try to detect heap overwrites p = newp(new_pointer_type(BStruct2), [100, [200, list(range(50))]]) assert p.tail.y[49] == 49 assert buffer_warning(p) assert not buffer_warning(cast(new_pointer_type(BStruct2), p)) assert not buffer_warning(cast(BIntP, p)) def test_more_buffer_warning(): BChar = new_primitive_type("unsigned char") BCharP = new_pointer_type(BChar) BArray = new_array_type(BCharP, 10) # char[10] p = newp(BArray) assert buffer_warning(p) assert not buffer_warning(cast(BCharP, p)) p = newp(BCharP) assert buffer_warning(p) assert not buffer_warning(cast(BCharP, p)) 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_struct_array_not_aligned(): # struct a { int x; char y; char z[]; }; # ends up of size 8, but 'z' is at offset 5 BChar = new_primitive_type("char") BInt = new_primitive_type("int") BCharP = new_pointer_type(BChar) BArray = new_array_type(BCharP, None) BStruct = new_struct_type("foo") complete_struct_or_union(BStruct, [('x', BInt), ('y', BChar), ('z', BArray)]) assert sizeof(BStruct) == 2 * size_of_int() def offsetof(BType, fieldname): return typeoffsetof(BType, fieldname)[1] base = offsetof(BStruct, 'z') assert base == size_of_int() + 1 # p = newp(new_pointer_type(BStruct), {'z': 3}) assert sizeof(p[0]) == base + 3 q = newp(new_pointer_type(BStruct), {'z': size_of_int()}) assert sizeof(q) == size_of_ptr() assert sizeof(q[0]) == base + size_of_int() assert len(p.z) == 3 assert len(p[0].z) == 3 assert len(q.z) == size_of_int() assert len(q[0].z) == size_of_int() 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"] with pytest.raises(TypeError): p[1:5] = u+'XYZT' with pytest.raises(TypeError): p[1:5] = [1, 2, 3, 4] # for typename in ["wchar_t", "char16_t", "char32_t"]: BUniChar = new_primitive_type(typename) 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"] with pytest.raises(TypeError): p[1:5] = b'XYZT' with pytest.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) with pytest.raises(TypeError): p - q with pytest.raises(TypeError): q - p with pytest.raises(TypeError): p + cast(new_primitive_type('int'), 42) with pytest.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") for extra_args in [(SF_PACKED,), (0, 1)]: BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BLong, -1), ('a2', BChar, -1), ('a3', BShort, -1)], None, -1, -1, *extra_args) 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 # BStruct2 = new_struct_type("struct foo") complete_struct_or_union(BStruct2, [('b1', BChar, -1), ('b2', BLong, -1)], None, -1, -1, 0, 2) d = BStruct2.fields assert len(d) == 2 assert d[0][0] == 'b1' assert d[0][1].type is BChar assert d[0][1].offset == 0 assert d[0][1].bitshift == -1 assert d[0][1].bitsize == -1 assert d[1][0] == 'b2' assert d[1][1].type is BLong assert d[1][1].offset == 2 assert d[1][1].bitshift == -1 assert d[1][1].bitsize == -1 assert sizeof(BStruct2) == 2 + sizeof(BLong) assert alignof(BStruct2) == 2 def test_packed_with_bitfields(): if sys.platform == "win32": pytest.skip("testing gcc behavior") BLong = new_primitive_type("long") BChar = new_primitive_type("char") BStruct = new_struct_type("struct foo") pytest.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(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p1 = from_buffer(BCharA, b"foo") assert p1 == from_buffer(BCharA, b"foo") import gc; gc.collect() assert p1 == from_buffer(BCharA, b"foo") pytest.raises(TypeError, from_buffer, BCharA, u+"foo") try: from __builtin__ import buffer except ImportError: pass else: # Python 2 only contents = from_buffer(BCharA, buffer(b"foo")) assert len(contents) == len(p1) for i in range(len(contents)): assert contents[i] == p1[i] p4 = buffer(u+"foo") contents = from_buffer(BCharA, buffer(u+"foo")) assert len(contents) == len(p4) for i in range(len(contents)): assert contents[i] == p4[i] try: from __builtin__ import memoryview except ImportError: pass else: contents = from_buffer(BCharA, memoryview(b"foo")) assert len(contents) == len(p1) for i in range(len(contents)): assert contents[i] == p1[i] def test_from_buffer_bytearray(): a = bytearray(b"xyz") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p = from_buffer(BCharA, a) assert typeof(p) is BCharA assert len(p) == 3 assert repr(p) == "" assert p[2] == b"z" p[2] = b"." assert a[2] == ord(".") a[2] = ord("?") assert p[2] == b"?" def test_from_buffer_more_cases(): try: from _cffi_backend import _testbuff except ImportError: pytest.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_from_buffer_require_writable(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p1 = from_buffer(BCharA, b"foo", False) assert p1 == from_buffer(BCharA, b"foo", False) pytest.raises((TypeError, BufferError), from_buffer, BCharA, b"foo", True) ba = bytearray(b"foo") p1 = from_buffer(BCharA, ba, True) p1[0] = b"g" assert ba == b"goo" def test_from_buffer_types(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BIntA = new_array_type(BIntP, None) lst = [-12345678, 87654321, 489148] bytestring = bytearray(buffer(newp(BIntA, lst))[:] + b'XYZ') lst2 = lst + [42, -999999999] bytestring2 = bytearray(buffer(newp(BIntA, lst2))[:] + b'XYZ') # p1 = from_buffer(BIntA, bytestring) # int[] assert typeof(p1) is BIntA assert len(p1) == 3 assert p1[0] == lst[0] assert p1[1] == lst[1] assert p1[2] == lst[2] with pytest.raises(IndexError): p1[3] with pytest.raises(IndexError): p1[-1] # pytest.raises(TypeError, from_buffer, BInt, bytestring) # p2 = from_buffer(BIntP, bytestring) # int * assert p2 == p1 or 'PY_DOT_PY' in globals() # note: on py.py ^^^, bytearray buffers are not emulated well enough assert typeof(p2) is BIntP assert p2[0] == lst[0] assert p2[1] == lst[1] assert p2[2] == lst[2] # hopefully does not crash, but doesn't raise an exception: p2[3] p2[-1] # not enough data even for one, but this is not enforced: from_buffer(BIntP, b"") # BIntA2 = new_array_type(BIntP, 2) p2 = from_buffer(BIntA2, bytestring) # int[2] assert typeof(p2) is BIntA2 assert len(p2) == 2 assert p2[0] == lst[0] assert p2[1] == lst[1] with pytest.raises(IndexError): p2[2] with pytest.raises(IndexError): p2[-1] assert p2 == p1 or 'PY_DOT_PY' in globals() # BIntA4 = new_array_type(BIntP, 4) # int[4]: too big pytest.raises(ValueError, from_buffer, BIntA4, bytestring) # BStruct = new_struct_type("foo") complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1)]) BStructP = new_pointer_type(BStruct) BStructA = new_array_type(BStructP, None) p1 = from_buffer(BStructA, bytestring2) # struct[] assert len(p1) == 2 assert typeof(p1) is BStructA assert p1[0].a1 == lst2[0] assert p1[0].a2 == lst2[1] assert p1[1].a1 == lst2[2] assert p1[1].a2 == lst2[3] with pytest.raises(IndexError): p1[2] with pytest.raises(IndexError): p1[-1] assert repr(p1) == "" # p2 = from_buffer(BStructP, bytestring2) # 'struct *' assert p2 == p1 or 'PY_DOT_PY' in globals() assert typeof(p2) is BStructP assert p2.a1 == lst2[0] assert p2.a2 == lst2[1] assert p2[0].a1 == lst2[0] assert p2[0].a2 == lst2[1] assert p2[1].a1 == lst2[2] assert p2[1].a2 == lst2[3] # does not crash: p2[2] p2[-1] # not enough data even for one, but this is not enforced: from_buffer(BStructP, b"") from_buffer(BStructP, b"1234567") # release(p1) assert repr(p1) == "" # BEmptyStruct = new_struct_type("empty") complete_struct_or_union(BEmptyStruct, [], Ellipsis, 0) assert sizeof(BEmptyStruct) == 0 BEmptyStructP = new_pointer_type(BEmptyStruct) BEmptyStructA = new_array_type(BEmptyStructP, None) pytest.raises(ZeroDivisionError, from_buffer, # empty[] BEmptyStructA, bytestring) # BEmptyStructA5 = new_array_type(BEmptyStructP, 5) p1 = from_buffer(BEmptyStructA5, bytestring) # struct empty[5] assert typeof(p1) is BEmptyStructA5 assert len(p1) == 5 assert (cast(BIntP, p1) == from_buffer(BIntA, bytestring) or 'PY_DOT_PY' in globals()) # BVarStruct = new_struct_type("varfoo") BVarStructP = new_pointer_type(BVarStruct) complete_struct_or_union(BVarStruct, [('a1', BInt, -1), ('va', BIntA, -1)]) with pytest.raises(TypeError): from_buffer(BVarStruct, bytestring) pv = from_buffer(BVarStructP, bytestring) # varfoo * assert pv.a1 == lst[0] assert pv.va[0] == lst[1] assert pv.va[1] == lst[2] assert sizeof(pv[0]) == 1 * size_of_int() with pytest.raises(TypeError): len(pv.va) # hopefully does not crash, but doesn't raise an exception: pv.va[2] pv.va[-1] # not enough data even for one, but this is not enforced: from_buffer(BVarStructP, b"") assert repr(pv) == "" assert repr(pv[0]).startswith("" assert repr(pv[0]).startswith("" else: assert repr(BFunc) == "" def test_get_common_types(): d = {} _get_common_types(d) assert d['bool'] == '_Bool' def test_unpack(): BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), 10) # char[10] p = newp(BArray, b"abc\x00def") p0 = p assert unpack(p, 10) == b"abc\x00def\x00\x00\x00" assert unpack(p+1, 5) == b"bc\x00de" for typename in ["wchar_t", "char16_t", "char32_t"]: BWChar = new_primitive_type(typename) BArray = new_array_type(new_pointer_type(BWChar), 10) # wchar_t[10] p = newp(BArray, u"abc\x00def") assert unpack(p, 10) == u"abc\x00def\x00\x00\x00" for typename, samples in [ ("uint8_t", [0, 2**8-1]), ("uint16_t", [0, 2**16-1]), ("uint32_t", [0, 2**32-1]), ("uint64_t", [0, 2**64-1]), ("int8_t", [-2**7, 2**7-1]), ("int16_t", [-2**15, 2**15-1]), ("int32_t", [-2**31, 2**31-1]), ("int64_t", [-2**63, 2**63-1]), ("_Bool", [False, True]), ("float", [0.0, 10.5]), ("double", [12.34, 56.78]), ]: BItem = new_primitive_type(typename) BArray = new_array_type(new_pointer_type(BItem), 10) p = newp(BArray, samples) result = unpack(p, len(samples)) assert result == samples for i in range(len(samples)): assert result[i] == p[i] and type(result[i]) is type(p[i]) assert (type(result[i]) is bool) == (type(samples[i]) is bool) # BInt = new_primitive_type("int") pytest.raises(TypeError, unpack, p) pytest.raises(TypeError, unpack, b"foobar", 6) pytest.raises(TypeError, unpack, cast(BInt, 42), 1) # BPtr = new_pointer_type(BInt) random_ptr = cast(BPtr, -424344) other_ptr = cast(BPtr, 54321) BArray = new_array_type(new_pointer_type(BPtr), None) lst = unpack(newp(BArray, [random_ptr, other_ptr]), 2) assert lst == [random_ptr, other_ptr] # BFunc = new_function_type((BInt, BInt), BInt, False) BFuncPtr = new_pointer_type(BFunc) lst = unpack(newp(new_array_type(BFuncPtr, None), 2), 2) assert len(lst) == 2 assert not lst[0] and not lst[1] assert typeof(lst[0]) is BFunc # BStruct = new_struct_type("foo") BStructPtr = new_pointer_type(BStruct) e = pytest.raises(ValueError, unpack, cast(BStructPtr, 42), 5) assert str(e.value) == "'foo *' points to items of unknown size" complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1)]) array_of_structs = newp(new_array_type(BStructPtr, None), [[4,5], [6,7]]) lst = unpack(array_of_structs, 2) assert typeof(lst[0]) is BStruct assert lst[0].a1 == 4 and lst[1].a2 == 7 # pytest.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 0) pytest.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 10) # pytest.raises(ValueError, unpack, p0, -1) pytest.raises(ValueError, unpack, p, -1) def test_cdata_dir(): BInt = new_primitive_type("int") p = cast(BInt, 42) check_dir(p, []) p = newp(new_array_type(new_pointer_type(BInt), None), 5) check_dir(p, []) BStruct = new_struct_type("foo") p = cast(new_pointer_type(BStruct), 0) check_dir(p, []) # opaque complete_struct_or_union(BStruct, [('a2', BInt, -1), ('a1', BInt, -1)]) check_dir(p, ['a1', 'a2']) # always sorted p = newp(new_pointer_type(BStruct), None) check_dir(p, ['a1', 'a2']) check_dir(p[0], ['a1', 'a2']) pp = newp(new_pointer_type(new_pointer_type(BStruct)), p) check_dir(pp, []) check_dir(pp[0], ['a1', 'a2']) check_dir(pp[0][0], ['a1', 'a2']) def test_char_pointer_conversion(): import warnings assert __version__.startswith("1."), ( "the warning will be an error if we ever release cffi 2.x") BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 newp(new_pointer_type(BVoidP), z1) # fine assert len(w) == 1 newp(new_pointer_type(BCharP), z2) # warn assert len(w) == 2 newp(new_pointer_type(BVoidP), z2) # fine assert len(w) == 2 newp(new_pointer_type(BCharP), z3) # fine assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) assert len(w) == 2 newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) assert len(w) == 2 newp(new_pointer_type(BUCharP), z3) # fine assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 def test_primitive_comparison(): def assert_eq(a, b): assert (a == b) is True assert (b == a) is True assert (a != b) is False assert (b != a) is False assert (a < b) is False assert (a <= b) is True assert (a > b) is False assert (a >= b) is True assert (b < a) is False assert (b <= a) is True assert (b > a) is False assert (b >= a) is True assert hash(a) == hash(b) def assert_lt(a, b, check_hash=True): assert (a == b) is False assert (b == a) is False assert (a != b) is True assert (b != a) is True assert (a < b) is True assert (a <= b) is True assert (a > b) is False assert (a >= b) is False assert (b < a) is False assert (b <= a) is False assert (b > a) is True assert (b >= a) is True if check_hash: assert hash(a) != hash(b) # (or at least, it is unlikely) def assert_gt(a, b, check_hash=True): assert_lt(b, a, check_hash) def assert_ne(a, b): assert (a == b) is False assert (b == a) is False assert (a != b) is True assert (b != a) is True if strict_compare: with pytest.raises(TypeError): a < b with pytest.raises(TypeError): a <= b with pytest.raises(TypeError): a > b with pytest.raises(TypeError): a >= b with pytest.raises(TypeError): b < a with pytest.raises(TypeError): b <= a with pytest.raises(TypeError): b > a with pytest.raises(TypeError): b >= a elif a < b: assert_lt(a, b) else: assert_lt(b, a) assert_eq(5, 5) assert_lt(3, 5) assert_ne('5', 5) # t1 = new_primitive_type("char") t2 = new_primitive_type("int") t3 = new_primitive_type("unsigned char") t4 = new_primitive_type("unsigned int") t5 = new_primitive_type("float") t6 = new_primitive_type("double") assert_eq(cast(t1, 65), b'A') assert_lt(cast(t1, 64), b'\x99') assert_gt(cast(t1, 200), b'A') assert_ne(cast(t1, 65), 65) assert_eq(cast(t2, -25), -25) assert_lt(cast(t2, -25), -24) assert_gt(cast(t2, -25), -26) assert_eq(cast(t3, 65), 65) assert_ne(cast(t3, 65), b'A') assert_ne(cast(t3, 65), cast(t1, 65)) assert_gt(cast(t4, -1), -1, check_hash=False) assert_gt(cast(t4, -1), cast(t2, -1), check_hash=False) assert_gt(cast(t4, -1), 99999) assert_eq(cast(t4, -1), 256 ** size_of_int() - 1) assert_eq(cast(t5, 3.0), 3) assert_eq(cast(t5, 3.5), 3.5) assert_lt(cast(t5, 3.3), 3.3) # imperfect rounding assert_eq(cast(t6, 3.3), 3.3) assert_eq(cast(t5, 3.5), cast(t6, 3.5)) assert_lt(cast(t5, 3.1), cast(t6, 3.1)) # imperfect rounding assert_eq(cast(t5, 7.0), cast(t3, 7)) assert_lt(cast(t5, 3.1), 3.101) assert_gt(cast(t5, 3.1), 3) def test_explicit_release_new(): # release() on a ffi.new() object has no effect on CPython, but # really releases memory on PyPy. We can't test that effect # though, because a released cdata is not marked. BIntP = new_pointer_type(new_primitive_type("int")) p = newp(BIntP) p[0] = 42 with pytest.raises(IndexError): p[1] release(p) # here, reading p[0] might give garbage or segfault... release(p) # no effect # BStruct = new_struct_type("struct foo") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('p', BIntP, -1)]) pstruct = newp(BStructP) assert pstruct.p == cast(BIntP, 0) release(pstruct) # here, reading pstruct.p might give garbage or segfault... release(pstruct) # no effect def test_explicit_release_new_contextmgr(): BIntP = new_pointer_type(new_primitive_type("int")) with newp(BIntP) as p: p[0] = 42 assert p[0] == 42 # here, reading p[0] might give garbage or segfault... release(p) # no effect def test_explicit_release_badtype(): BIntP = new_pointer_type(new_primitive_type("int")) p = cast(BIntP, 12345) pytest.raises(ValueError, release, p) pytest.raises(ValueError, release, p) BStruct = new_struct_type("struct foo") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('p', BIntP, -1)]) pstruct = newp(BStructP) pytest.raises(ValueError, release, pstruct[0]) def test_explicit_release_badtype_contextmgr(): BIntP = new_pointer_type(new_primitive_type("int")) p = cast(BIntP, 12345) with pytest.raises(ValueError): with p: pass with pytest.raises(ValueError): with p: pass def test_explicit_release_gc(): BIntP = new_pointer_type(new_primitive_type("int")) seen = [] intp1 = newp(BIntP, 12345) p1 = cast(BIntP, intp1) p = gcp(p1, seen.append) assert seen == [] release(p) assert seen == [p1] assert p1[0] == 12345 assert p[0] == 12345 # true so far, but might change to raise RuntimeError release(p) # no effect def test_explicit_release_gc_contextmgr(): BIntP = new_pointer_type(new_primitive_type("int")) seen = [] intp1 = newp(BIntP, 12345) p1 = cast(BIntP, intp1) p = gcp(p1, seen.append) with p: assert p[0] == 12345 assert seen == [] assert seen == [p1] assert p1[0] == 12345 assert p[0] == 12345 # true so far, but might change to raise RuntimeError release(p) # no effect def test_explicit_release_from_buffer(): a = bytearray(b"xyz") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p = from_buffer(BCharA, a) assert p[2] == b"z" assert repr(p) == "" release(p) assert p[2] == b"z" # true so far, but might change to raise RuntimeError assert repr(p) == "" release(p) # no effect def test_explicit_release_from_buffer_contextmgr(): a = bytearray(b"xyz") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p = from_buffer(BCharA, a) with p: assert p[2] == b"z" assert p[2] == b"z" # true so far, but might change to raise RuntimeError assert repr(p) == "" release(p) # no effect def test_explicit_release_bytearray_on_cpython(): if '__pypy__' in sys.builtin_module_names: pytest.skip("pypy's bytearray are never locked") a = bytearray(b"xyz") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) a += b't' * 10 p = from_buffer(BCharA, a) with pytest.raises(BufferError): a += b'u' * 100 release(p) a += b'v' * 100 release(p) # no effect a += b'w' * 1000 assert a == bytearray(b"xyz" + b't' * 10 + b'v' * 100 + b'w' * 1000) def test_int_doesnt_give_bool(): BBool = new_primitive_type("_Bool") x = int(cast(BBool, 42)) assert type(x) is int and x == 1 x = long(cast(BBool, 42)) assert type(x) is long and x == 1 with pytest.raises(TypeError): float(cast(BBool, 42)) with pytest.raises(TypeError): complex(cast(BBool, 42)) def test_cannot_call_null_function_pointer(): BInt = new_primitive_type("int") BFunc = new_function_type((BInt, BInt), BInt, False) f = cast(BFunc, 0) with pytest.raises(RuntimeError): f(40, 2) def test_huge_structure(): BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), sys.maxsize) assert sizeof(BArray) == sys.maxsize BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BArray, -1)]) assert sizeof(BStruct) == sys.maxsize def test_get_types(): import _cffi_backend CData, CType = _get_types() assert CData is _cffi_backend._CDataBase assert CType is _cffi_backend.CType def test_type_available_with_correct_names(): import _cffi_backend check_names = [ 'CType', 'CField', 'CLibrary', '_CDataBase', 'FFI', 'Lib', 'buffer', ] if '__pypy__' in sys.builtin_module_names: check_names += [ '__CData_iterator', '__FFIGlobSupport', '__FFIAllocator', '__FFIFunctionWrapper', ] else: check_names += [ '__CDataOwn', '__CDataOwnGC', '__CDataFromBuf', '__CDataGCP', '__CData_iterator', '__FFIGlobSupport', ] for name in check_names: tp = getattr(_cffi_backend, name) assert isinstance(tp, type) assert (tp.__module__, tp.__name__) == ('_cffi_backend', name) def test_unaligned_types(): BByteArray = new_array_type( new_pointer_type(new_primitive_type("unsigned char")), None) pbuf = newp(BByteArray, 40) buf = buffer(pbuf) # for name in ['short', 'int', 'long', 'long long', 'float', 'double', 'float _Complex', 'double _Complex']: p = new_primitive_type(name) if name.endswith(' _Complex'): num = cast(p, 1.23 - 4.56j) else: num = cast(p, 0x0123456789abcdef) size = sizeof(p) buf[0:40] = b"\x00" * 40 pbuf1 = cast(new_pointer_type(p), pbuf + 1) pbuf1[0] = num assert pbuf1[0] == num assert buf[0] == b'\x00' assert buf[1 + size] == b'\x00' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/c/wchar_helper.h0000644000175100001770000001511700000000000017146 0ustar00runnerdocker00000000000000/* * wchar_t helpers */ typedef uint16_t cffi_char16_t; typedef uint32_t cffi_char32_t; #if Py_UNICODE_SIZE == 2 /* 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. From cffi 1.11 we can't use it anyway, because we need a version with char32_t input types. */ static PyObject * _my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) { PyObject *unicode; register Py_ssize_t i; Py_ssize_t alloc; const cffi_char32_t *orig_w; 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 (*w > 0xFFFF) { cffi_char32_t ordinal; if (*w > 0x10FFFF) { PyErr_Format(PyExc_ValueError, "char32_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; } static PyObject * _my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) { return PyUnicode_FromUnicode((const Py_UNICODE *)w, size); } #else /* Py_UNICODE_SIZE == 4 */ static PyObject * _my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) { return PyUnicode_FromUnicode((const Py_UNICODE *)w, size); } static PyObject * _my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) { /* 'size' is the length of the 'w' array */ PyObject *result = PyUnicode_FromUnicode(NULL, size); if (result != NULL) { Py_UNICODE *u_base = PyUnicode_AS_UNICODE(result); Py_UNICODE *u = u_base; if (size == 1) { /* performance only */ *u = (cffi_char32_t)*w; } else { while (size > 0) { cffi_char32_t ch = *w++; size--; if (0xD800 <= ch && ch <= 0xDBFF && size > 0) { cffi_char32_t ch2 = *w; if (0xDC00 <= ch2 && ch2 <= 0xDFFF) { ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; w++; size--; } } *u++ = ch; } if (PyUnicode_Resize(&result, u - u_base) < 0) { Py_DECREF(result); return NULL; } } } return result; } #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_AsSingleChar16(PyObject *unicode, cffi_char16_t *result, char *err_got) { Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); if (PyUnicode_GET_SIZE(unicode) != 1) { sprintf(err_got, "unicode string of length %zd", PyUnicode_GET_SIZE(unicode)); return -1; } #if Py_UNICODE_SIZE == 4 if (((unsigned int)u[0]) > 0xFFFF) { sprintf(err_got, "larger-than-0xFFFF character"); return -1; } #endif *result = (cffi_char16_t)u[0]; return 0; } static int _my_PyUnicode_AsSingleChar32(PyObject *unicode, cffi_char32_t *result, char *err_got) { Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); if (PyUnicode_GET_SIZE(unicode) == 1) { *result = (cffi_char32_t)u[0]; return 0; } #if Py_UNICODE_SIZE == 2 if (PyUnicode_GET_SIZE(unicode) == 2 && IS_SURROGATE(u)) { *result = AS_SURROGATE(u); return 0; } #endif sprintf(err_got, "unicode string of length %zd", PyUnicode_GET_SIZE(unicode)); return -1; } static Py_ssize_t _my_PyUnicode_SizeAsChar16(PyObject *unicode) { Py_ssize_t length = PyUnicode_GET_SIZE(unicode); Py_ssize_t result = length; #if Py_UNICODE_SIZE == 4 Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); Py_ssize_t i; for (i=0; i 0xFFFF) result++; } #endif return result; } static Py_ssize_t _my_PyUnicode_SizeAsChar32(PyObject *unicode) { Py_ssize_t length = PyUnicode_GET_SIZE(unicode); Py_ssize_t result = length; #if Py_UNICODE_SIZE == 2 Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); Py_ssize_t i; for (i=0; i 0xFFFF) { if (ordinal > 0x10FFFF) { PyErr_Format(PyExc_ValueError, "unicode character out of range for " "conversion to char16_t: 0x%x", (int)ordinal); return -1; } ordinal -= 0x10000; *result++ = 0xD800 | (ordinal >> 10); *result++ = 0xDC00 | (ordinal & 0x3FF); continue; } #endif *result++ = ordinal; } return 0; } static int _my_PyUnicode_AsChar32(PyObject *unicode, cffi_char32_t *result, Py_ssize_t resultlen) { Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); Py_ssize_t i; for (i=0; i= 3.3. * * CPython 3.3 added support for sys.maxunicode == 0x10FFFF on all * platforms, even ones with wchar_t limited to 2 bytes. As such, * this code here works from the outside like wchar_helper.h in the * case Py_UNICODE_SIZE == 4, but the implementation is very different. */ typedef uint16_t cffi_char16_t; typedef uint32_t cffi_char32_t; static PyObject * _my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) { return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, w, size); } static PyObject * _my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) { /* are there any surrogate pairs, and if so, how many? */ Py_ssize_t i, count_surrogates = 0; for (i = 0; i < size - 1; i++) { if (0xD800 <= w[i] && w[i] <= 0xDBFF && 0xDC00 <= w[i+1] && w[i+1] <= 0xDFFF) count_surrogates++; } if (count_surrogates == 0) { /* no, fast path */ return PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, w, size); } else { PyObject *result = PyUnicode_New(size - count_surrogates, 0x10FFFF); Py_UCS4 *data; assert(PyUnicode_KIND(result) == PyUnicode_4BYTE_KIND); data = PyUnicode_4BYTE_DATA(result); for (i = 0; i < size; i++) { cffi_char32_t ch = w[i]; if (0xD800 <= ch && ch <= 0xDBFF && i < size - 1) { cffi_char32_t ch2 = w[i + 1]; if (0xDC00 <= ch2 && ch2 <= 0xDFFF) { ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; i++; } } *data++ = ch; } return result; } } static int _my_PyUnicode_AsSingleChar16(PyObject *unicode, cffi_char16_t *result, char *err_got) { cffi_char32_t ch; if (PyUnicode_GET_LENGTH(unicode) != 1) { sprintf(err_got, "unicode string of length %zd", PyUnicode_GET_LENGTH(unicode)); return -1; } ch = PyUnicode_READ_CHAR(unicode, 0); if (ch > 0xFFFF) { sprintf(err_got, "larger-than-0xFFFF character"); return -1; } *result = (cffi_char16_t)ch; return 0; } static int _my_PyUnicode_AsSingleChar32(PyObject *unicode, cffi_char32_t *result, char *err_got) { if (PyUnicode_GET_LENGTH(unicode) != 1) { sprintf(err_got, "unicode string of length %zd", PyUnicode_GET_LENGTH(unicode)); return -1; } *result = PyUnicode_READ_CHAR(unicode, 0); return 0; } static Py_ssize_t _my_PyUnicode_SizeAsChar16(PyObject *unicode) { Py_ssize_t length = PyUnicode_GET_LENGTH(unicode); Py_ssize_t result = length; unsigned int kind = PyUnicode_KIND(unicode); if (kind == PyUnicode_4BYTE_KIND) { Py_UCS4 *data = PyUnicode_4BYTE_DATA(unicode); Py_ssize_t i; for (i = 0; i < length; i++) { if (data[i] > 0xFFFF) result++; } } return result; } static Py_ssize_t _my_PyUnicode_SizeAsChar32(PyObject *unicode) { return PyUnicode_GET_LENGTH(unicode); } static int _my_PyUnicode_AsChar16(PyObject *unicode, cffi_char16_t *result, Py_ssize_t resultlen) { Py_ssize_t len = PyUnicode_GET_LENGTH(unicode); unsigned int kind = PyUnicode_KIND(unicode); void *data = PyUnicode_DATA(unicode); Py_ssize_t i; for (i = 0; i < len; i++) { cffi_char32_t ordinal = PyUnicode_READ(kind, data, i); if (ordinal > 0xFFFF) { if (ordinal > 0x10FFFF) { PyErr_Format(PyExc_ValueError, "unicode character out of range for " "conversion to char16_t: 0x%x", (int)ordinal); return -1; } ordinal -= 0x10000; *result++ = 0xD800 | (ordinal >> 10); *result++ = 0xDC00 | (ordinal & 0x3FF); } else *result++ = ordinal; } return 0; } static int _my_PyUnicode_AsChar32(PyObject *unicode, cffi_char32_t *result, Py_ssize_t resultlen) { if (PyUnicode_AsUCS4(unicode, (Py_UCS4 *)result, resultlen, 0) == NULL) return -1; return 0; } ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1477678 cffi-1.16.0/src/cffi/0000755000175100001770000000000000000000000015012 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/__init__.py0000644000175100001770000000100100000000000017113 0ustar00runnerdocker00000000000000__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', 'FFIError'] from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError __version__ = "1.16.0" __version_info__ = (1, 16, 0) # 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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/_cffi_errors.h0000644000175100001770000000750400000000000017633 0ustar00runnerdocker00000000000000#ifndef CFFI_MESSAGEBOX # ifdef _MSC_VER # define CFFI_MESSAGEBOX 1 # else # define CFFI_MESSAGEBOX 0 # endif #endif #if CFFI_MESSAGEBOX /* Windows only: logic to take the Python-CFFI embedding logic initialization errors and display them in a background thread with MessageBox. The idea is that if the whole program closes as a result of this problem, then likely it is already a console program and you can read the stderr output in the console too. If it is not a console program, then it will likely show its own dialog to complain, or generally not abruptly close, and for this case the background thread should stay alive. */ static void *volatile _cffi_bootstrap_text; static PyObject *_cffi_start_error_capture(void) { PyObject *result = NULL; PyObject *x, *m, *bi; if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text, (void *)1, NULL) != NULL) return (PyObject *)1; m = PyImport_AddModule("_cffi_error_capture"); if (m == NULL) goto error; result = PyModule_GetDict(m); if (result == NULL) goto error; #if PY_MAJOR_VERSION >= 3 bi = PyImport_ImportModule("builtins"); #else bi = PyImport_ImportModule("__builtin__"); #endif if (bi == NULL) goto error; PyDict_SetItemString(result, "__builtins__", bi); Py_DECREF(bi); x = PyRun_String( "import sys\n" "class FileLike:\n" " def write(self, x):\n" " try:\n" " of.write(x)\n" " except: pass\n" " self.buf += x\n" " def flush(self):\n" " pass\n" "fl = FileLike()\n" "fl.buf = ''\n" "of = sys.stderr\n" "sys.stderr = fl\n" "def done():\n" " sys.stderr = of\n" " return fl.buf\n", /* make sure the returned value stays alive */ Py_file_input, result, result); Py_XDECREF(x); error: if (PyErr_Occurred()) { PyErr_WriteUnraisable(Py_None); PyErr_Clear(); } return result; } #pragma comment(lib, "user32.lib") static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored) { Sleep(666); /* may be interrupted if the whole process is closing */ #if PY_MAJOR_VERSION >= 3 MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text, L"Python-CFFI error", MB_OK | MB_ICONERROR); #else MessageBoxA(NULL, (char *)_cffi_bootstrap_text, "Python-CFFI error", MB_OK | MB_ICONERROR); #endif _cffi_bootstrap_text = NULL; return 0; } static void _cffi_stop_error_capture(PyObject *ecap) { PyObject *s; void *text; if (ecap == (PyObject *)1) return; if (ecap == NULL) goto error; s = PyRun_String("done()", Py_eval_input, ecap, ecap); if (s == NULL) goto error; /* Show a dialog box, but in a background thread, and never show multiple dialog boxes at once. */ #if PY_MAJOR_VERSION >= 3 text = PyUnicode_AsWideCharString(s, NULL); #else text = PyString_AsString(s); #endif _cffi_bootstrap_text = text; if (text != NULL) { HANDLE h; h = CreateThread(NULL, 0, _cffi_bootstrap_dialog, NULL, 0, NULL); if (h != NULL) CloseHandle(h); } /* decref the string, but it should stay alive as 'fl.buf' in the small module above. It will really be freed only if we later get another similar error. So it's a leak of at most one copy of the small module. That's fine for this situation which is usually a "fatal error" anyway. */ Py_DECREF(s); PyErr_Clear(); return; error: _cffi_bootstrap_text = NULL; PyErr_Clear(); } #else static PyObject *_cffi_start_error_capture(void) { return NULL; } static void _cffi_stop_error_capture(PyObject *ecap) { } #endif ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/_cffi_include.h0000644000175100001770000003472000000000000017742 0ustar00runnerdocker00000000000000#define _CFFI_ /* We try to define Py_LIMITED_API before including Python.h. Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and Py_REF_DEBUG are not defined. This is a best-effort approximation: we can learn about Py_DEBUG from pyconfig.h, but it is unclear if the same works for the other two macros. Py_DEBUG implies them, but not the other way around. The implementation is messy (issue #350): on Windows, with _MSC_VER, we have to define Py_LIMITED_API even before including pyconfig.h. In that case, we guess what pyconfig.h will do to the macros above, and check our guess after the #include. Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv version >= 16.0.0. With older versions of either, you don't get a copy of PYTHON3.DLL in the virtualenv. We can't check the version of CPython *before* we even include pyconfig.h. ffi.set_source() puts a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is running on Windows < 3.5, as an attempt at fixing it, but that's arguably wrong because it may not be the target version of Python. Still better than nothing I guess. As another workaround, you can remove the definition of Py_LIMITED_API here. See also 'py_limited_api' in cffi/setuptools_ext.py. */ #if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) # ifdef _MSC_VER # if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) # define Py_LIMITED_API # endif # include /* sanity-check: Py_LIMITED_API will cause crashes if any of these are also defined. Normally, the Python file PC/pyconfig.h does not cause any of these to be defined, with the exception that _DEBUG causes Py_DEBUG. Double-check that. */ # ifdef Py_LIMITED_API # if defined(Py_DEBUG) # error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" # endif # if defined(Py_TRACE_REFS) # error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" # endif # if defined(Py_REF_DEBUG) # error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" # endif # endif # else # include # if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) # define Py_LIMITED_API # endif # endif #endif #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 */ # ifndef __cplusplus typedef unsigned char _Bool; # endif # 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 #ifdef __cplusplus # ifndef _Bool typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */ # endif #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_from_c__Bool PyBool_FromLong #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 *, struct _cffi_ctypedescr *))_cffi_exports[10]) #define _cffi_to_c_pointer \ ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_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 *, struct _cffi_ctypedescr *))_cffi_exports[16]) #define _cffi_to_c \ ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17]) #define _cffi_from_c_struct \ ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18]) #define _cffi_to_c_wchar_t \ ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19]) #define _cffi_from_c_wchar_t \ ((PyObject *(*)(_cffi_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(*)(struct _cffi_ctypedescr *, \ PyObject *, char **))_cffi_exports[23]) #define _cffi_convert_array_from_object \ ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24]) #define _CFFI_CPIDX 25 #define _cffi_call_python \ ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) #define _cffi_to_c_wchar3216_t \ ((int(*)(PyObject *))_cffi_exports[26]) #define _cffi_from_c_wchar3216_t \ ((PyObject *(*)(int))_cffi_exports[27]) #define _CFFI_NUM_EXPORTS 28 struct _cffi_ctypedescr; static void *_cffi_exports[_CFFI_NUM_EXPORTS]; #define _cffi_type(index) ( \ assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ (struct _cffi_ctypedescr *)_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; } #ifdef HAVE_WCHAR_H typedef wchar_t _cffi_wchar_t; #else typedef uint16_t _cffi_wchar_t; /* same random pick as _cffi_backend.c */ #endif _CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o) { if (sizeof(_cffi_wchar_t) == 2) return (uint16_t)_cffi_to_c_wchar_t(o); else return (uint16_t)_cffi_to_c_wchar3216_t(o); } _CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) { if (sizeof(_cffi_wchar_t) == 2) return _cffi_from_c_wchar_t((_cffi_wchar_t)x); else return _cffi_from_c_wchar3216_t((int)x); } _CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) { if (sizeof(_cffi_wchar_t) == 4) return (int)_cffi_to_c_wchar_t(o); else return (int)_cffi_to_c_wchar3216_t(o); } _CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x) { if (sizeof(_cffi_wchar_t) == 4) return _cffi_from_c_wchar_t((_cffi_wchar_t)x); else return _cffi_from_c_wchar3216_t((int)x); } union _cffi_union_alignment_u { 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; }; struct _cffi_freeme_s { struct _cffi_freeme_s *next; union _cffi_union_alignment_u alignment; }; _CFFI_UNUSED_FN static int _cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg, char **output_data, Py_ssize_t datasize, struct _cffi_freeme_s **freeme) { char *p; if (datasize < 0) return -1; p = *output_data; if (p == NULL) { struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); if (fp == NULL) return -1; fp->next = *freeme; *freeme = fp; p = *output_data = (char *)&fp->alignment; } memset((void *)p, 0, (size_t)datasize); return _cffi_convert_array_from_object(p, ctptr, arg); } _CFFI_UNUSED_FN static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme) { do { void *p = (void *)freeme; freeme = freeme->next; PyObject_Free(p); } while (freeme != NULL); } /********** 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/_embedding.h0000644000175100001770000004454300000000000017252 0ustar00runnerdocker00000000000000 /***** Support code for embedding *****/ #ifdef __cplusplus extern "C" { #endif #if defined(_WIN32) # 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. If _cffi_start_python() fails, then this is set to NULL; otherwise, it should never be NULL. 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 #include "_cffi_errors.h" #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; PyObject *builtins; 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; builtins = PyEval_GetBuiltins(); if (builtins == NULL) goto error; if (PyDict_SetItemString(global_dict, "__builtins__", 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 *ecap; PyObject *exception, *v, *tb, *f, *modules, *mod; PyErr_Fetch(&exception, &v, &tb); ecap = _cffi_start_error_capture(); f = PySys_GetObject((char *)"stderr"); if (f != NULL && f != Py_None) { PyFile_WriteString( "Failed to initialize the Python-CFFI embedding logic:\n\n", f); } if (exception != NULL) { PyErr_NormalizeException(&exception, &v, &tb); PyErr_Display(exception, v, tb); } Py_XDECREF(exception); Py_XDECREF(v); Py_XDECREF(tb); if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME "\ncompiled with cffi version: 1.16.0" "\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); } _cffi_stop_error_capture(ecap); } result = -1; goto done; } #if PY_VERSION_HEX < 0x03080000 PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ #endif 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 used to be completely different in Python 2 and Python 3, with the Python 2 solution avoiding the spin-lock around the Py_InitializeEx() call. However, after recent changes to CPython 2.7 (issue #358) it no longer works. So we use the Python 3 solution everywhere.) This initializes Python by calling Py_InitializeEx(). Important: this 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. In Python < 3.8, 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.) In Python >= 3.8, this string array is no longer writable, so instead we pick PyCapsuleType.tp_version_tag. We can't change Python < 3.8 because someone might use a mixture of cffi embedded modules, some of which were compiled before this file changed. In Python >= 3.12, this stopped working because that particular tp_version_tag gets modified during interpreter startup. It's arguably a bad idea before 3.12 too, but again we can't change that because someone might use a mixture of cffi embedded modules, and no-one reported a bug so far. In Python >= 3.12 we go instead for PyCapsuleType.tp_as_buffer, which is supposed to always be NULL. We write to it temporarily a pointer to a struct full of NULLs, which is semantically the same. */ #ifdef WITH_THREAD # if PY_VERSION_HEX < 0x03080000 char *volatile *lock = (char *volatile *)_PyParser_TokenNames; char *old_value, *locked_value; while (1) { /* spin loop */ old_value = *lock; locked_value = old_value + 1; if (old_value[0] == 'E') { assert(old_value[1] == 'N'); if (cffi_compare_and_swap(lock, old_value, locked_value)) 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. */ } } # else # if PY_VERSION_HEX < 0x030C0000 int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag; int old_value, locked_value = -42; assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG)); # else static struct ebp_s { PyBufferProcs buf; int mark; } empty_buffer_procs; empty_buffer_procs.mark = -42; PyBufferProcs *volatile *lock = (PyBufferProcs *volatile *) &PyCapsule_Type.tp_as_buffer; PyBufferProcs *old_value, *locked_value = &empty_buffer_procs.buf; # endif while (1) { /* spin loop */ old_value = *lock; if (old_value == 0) { if (cffi_compare_and_swap(lock, old_value, locked_value)) break; } else { # if PY_VERSION_HEX < 0x030C0000 assert(old_value == locked_value); # else /* The pointer should point to a possibly different empty_buffer_procs from another C extension module */ assert(((struct ebp_s *)old_value)->mark == -42); # endif /* 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 #endif /* call Py_InitializeEx() */ if (!Py_IsInitialized()) { _cffi_py_initialize(); #if PY_VERSION_HEX < 0x03070000 PyEval_InitThreads(); #endif PyEval_SaveThread(); /* release the GIL */ /* the returned tstate must be the one that has been stored into the autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */ } else { #if PY_VERSION_HEX < 0x03070000 /* PyEval_InitThreads() is always a no-op from CPython 3.7 */ PyGILState_STATE state = PyGILState_Ensure(); PyEval_InitThreads(); PyGILState_Release(state); #endif } #ifdef WITH_THREAD /* release the lock */ while (!cffi_compare_and_swap(lock, locked_value, 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; /* function pointer */ 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 #ifdef __cplusplus } #endif ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/_imp_emulation.py0000644000175100001770000000562000000000000020370 0ustar00runnerdocker00000000000000 try: # this works on Python < 3.12 from imp import * except ImportError: # this is a limited emulation for Python >= 3.12. # Note that this is used only for tests or for the old ffi.verify(). # This is copied from the source code of Python 3.11. from _imp import (acquire_lock, release_lock, is_builtin, is_frozen) from importlib._bootstrap import _load from importlib import machinery import os import sys import tokenize SEARCH_ERROR = 0 PY_SOURCE = 1 PY_COMPILED = 2 C_EXTENSION = 3 PY_RESOURCE = 4 PKG_DIRECTORY = 5 C_BUILTIN = 6 PY_FROZEN = 7 PY_CODERESOURCE = 8 IMP_HOOK = 9 def get_suffixes(): extensions = [(s, 'rb', C_EXTENSION) for s in machinery.EXTENSION_SUFFIXES] source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES] bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES] return extensions + source + bytecode def find_module(name, path=None): if not isinstance(name, str): raise TypeError("'name' must be a str, not {}".format(type(name))) elif not isinstance(path, (type(None), list)): # Backwards-compatibility raise RuntimeError("'path' must be None or a list, " "not {}".format(type(path))) if path is None: if is_builtin(name): return None, None, ('', '', C_BUILTIN) elif is_frozen(name): return None, None, ('', '', PY_FROZEN) else: path = sys.path for entry in path: package_directory = os.path.join(entry, name) for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]: package_file_name = '__init__' + suffix file_path = os.path.join(package_directory, package_file_name) if os.path.isfile(file_path): return None, package_directory, ('', '', PKG_DIRECTORY) for suffix, mode, type_ in get_suffixes(): file_name = name + suffix file_path = os.path.join(entry, file_name) if os.path.isfile(file_path): break else: continue break # Break out of outer loop when breaking out of inner loop. else: raise ImportError(name, name=name) encoding = None if 'b' not in mode: with open(file_path, 'rb') as file: encoding = tokenize.detect_encoding(file.readline)[0] file = open(file_path, mode, encoding=encoding) return file, file_path, (suffix, mode, type_) def load_dynamic(name, path, file=None): loader = machinery.ExtensionFileLoader(name, path) spec = machinery.ModuleSpec(name=name, loader=loader, origin=path) return _load(spec) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/_shimmed_dist_utils.py0000644000175100001770000000372700000000000021425 0ustar00runnerdocker00000000000000""" Temporary shim module to indirect the bits of distutils we need from setuptools/distutils while providing useful error messages beyond `No module named 'distutils' on Python >= 3.12, or when setuptools' vendored distutils is broken. This is a compromise to avoid a hard-dep on setuptools for Python >= 3.12, since many users don't need runtime compilation support from CFFI. """ import sys try: # import setuptools first; this is the most robust way to ensure its embedded distutils is available # (the .pth shim should usually work, but this is even more robust) import setuptools except Exception as ex: if sys.version_info >= (3, 12): # Python 3.12 has no built-in distutils to fall back on, so any import problem is fatal raise Exception("This CFFI feature requires setuptools on Python >= 3.12. The setuptools module is missing or non-functional.") from ex # silently ignore on older Pythons (support fallback to stdlib distutils where available) else: del setuptools try: # bring in just the bits of distutils we need, whether they really came from setuptools or stdlib-embedded distutils from distutils import log, sysconfig from distutils.ccompiler import CCompiler from distutils.command.build_ext import build_ext from distutils.core import Distribution, Extension from distutils.dir_util import mkpath from distutils.errors import DistutilsSetupError, CompileError, LinkError from distutils.log import set_threshold, set_verbosity if sys.platform == 'win32': from distutils.msvc9compiler import MSVCCompiler except Exception as ex: if sys.version_info >= (3, 12): raise Exception("This CFFI feature requires setuptools on Python >= 3.12. Please install the setuptools package.") from ex # anything older, just let the underlying distutils import error fly raise Exception("This CFFI feature requires distutils. Please install the distutils or setuptools package.") from ex del sys ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/api.py0000644000175100001770000012214500000000000016142 0ustar00runnerdocker00000000000000import sys, types from .lock import allocate_lock from .error import CDefError from . import model 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 _unspecified = object() 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. """ 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__ if backend.__version__ != __version__: # bad version! Try to be as explicit as possible. if hasattr(backend, '__file__'): # CPython raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r. The two versions should be equal; check your installation." % ( __version__, __file__, backend.__version__, backend.__file__)) else: # PyPy raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. This interpreter comes with a built-in '_cffi_backend' module, which is version %s. The two versions should be equal; check your installation." % ( __version__, __file__, backend.__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.) from . import cparser 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 self._typecache = model.get_typecache(backend) if hasattr(backend, 'set_ffi'): backend.set_ffi(self) for name in list(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() self.buffer = backend.buffer def cdef(self, csource, override=False, packed=False, pack=None): """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. Alternatively, 'pack' can be a small integer, and requests for alignment greater than that are ignored (pack=1 is equivalent to packed=True). """ self._cdef(csource, override=override, packed=packed, pack=pack) def embedding_api(self, csource, packed=False, pack=None): self._cdef(csource, packed=packed, pack=pack, 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. """ if not (isinstance(name, basestring) or name is None or isinstance(name, self.CData)): raise TypeError("dlopen(name): name must be a file name, None, " "or an already-opened 'void *' handle") 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 dlclose(self, lib): """Close a library obtained with ffi.dlopen(). After this call, access to functions or variables from the library will fail (possibly with a segmentation fault). """ type(lib).__cffi_close__(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 unpack(self, cdata, length): """Unpack an array of C data of the given length, returning a Python string/unicode/list. If 'cdata' is a pointer to 'char', returns a byte string. It does not stop at the first null. This is equivalent to: ffi.buffer(cdata, length)[:] If 'cdata' is a pointer to 'wchar_t', returns a unicode string. 'length' is measured in wchar_t's; it is not the size in bytes. If 'cdata' is a pointer to anything else, returns a list of 'length' items. This is a faster equivalent to: [cdata[i] for i in range(length)] """ return self._backend.unpack(cdata, length) #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 # """ # note that 'buffer' is a type, set on this instance by __init__ def from_buffer(self, cdecl, python_buffer=_unspecified, require_writable=False): """Return a cdata of the given type pointing 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 or unicode (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. The first argument is optional and default to 'char[]'. """ if python_buffer is _unspecified: cdecl, python_buffer = self.BCharA, cdecl elif isinstance(cdecl, basestring): cdecl = self._typeof(cdecl) return self._backend.from_buffer(cdecl, python_buffer, require_writable) 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, size=0): """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. The optional 'size' gives an estimate of the size, used to trigger the garbage collection more eagerly. So far only used on PyPy. It tells the GC that the returned object keeps alive roughly 'size' bytes of external memory. """ return self._backend.gcp(cdata, destructor, size) 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): 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. """ try: ctype = self._backend.typeof(cdata) except TypeError: if '__addressof__' in type(cdata).__dict__: return type(cdata).__addressof__(cdata, *fields_or_indexes) raise 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 release(self, x): self._backend.release(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: import os if sys.platform == "win32": # we need 'libpypy-c.lib'. Current distributions of # pypy (>= 4.1) contain it as 'libs/python27.lib'. pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) if hasattr(sys, 'prefix'): ensure('library_dirs', os.path.join(sys.prefix, 'libs')) else: # we need 'libpypy-c.{so,dylib}', which should be by # default located in 'sys.prefix/bin' for installed # systems. if sys.version_info < (3,): pythonlib = "pypy-c" else: pythonlib = "pypy3-c" if hasattr(sys, 'prefix'): ensure('library_dirs', os.path.join(sys.prefix, 'bin')) # On uninstalled pypy's, the libpypy-c is typically found in # .../pypy/goal/. if hasattr(sys, 'prefix'): ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal')) else: if sys.platform == "win32": template = "python%d%d" if hasattr(sys, 'gettotalrefcount'): template += '_d' else: try: import sysconfig except ImportError: # 2.6 from cffi._shimmed_dist_utils 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): import os 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") if os.sep in module_name or (os.altsep and os.altsep in module_name): raise ValueError("'module_name' must not contain '/': use a dotted " "name to make a 'package.module' location") self._assigned_source = (str(module_name), source, source_extension, kwds) def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, source_extension='.c', **kwds): from . import pkgconfig if not isinstance(pkgconfig_libs, list): raise TypeError("the pkgconfig_libs argument must be a list " "of package names") kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) pkgconfig.merge_flags(kwds, kwds2) self.set_source(module_name, source, source_extension, **kwds) def distutils_extension(self, tmpdir='build', verbose=True): from cffi._shimmed_dist_utils 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, debug=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, debug=debug, **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 list_types(self): """Returns the user type names known to this FFI instance. This returns a tuple containing three lists of names: (typedef_names, names_of_structs, names_of_unions) """ typedefs = [] structs = [] unions = [] for key in self._parser._declarations: if key.startswith('typedef '): typedefs.append(key[8:]) elif key.startswith('struct '): structs.append(key[7:]) elif key.startswith('union '): unions.append(key[6:]) typedefs.sort() structs.sort() unions.sort() return (typedefs, structs, unions) def _load_backend_lib(backend, name, flags): import os if not isinstance(name, basestring): if sys.platform != "win32" or name is not None: return backend.load_library(name, flags) name = "c" # Windows: load_library(None) fails, but this works # on Python 2 (backward compatibility hack only) first_error = None if '.' in name or '/' in name or os.sep in name: try: return backend.load_library(name, flags) except OSError as e: first_error = e import ctypes.util path = ctypes.util.find_library(name) if path is None: if name == "c" and sys.platform == "win32" and sys.version_info >= (3,): raise OSError("dlopen(None) cannot work on Windows for Python 3 " "(see http://bugs.python.org/issue23606)") msg = ("ctypes.util.find_library() did not manage " "to locate a library called %r" % (name,)) if first_error is not None: msg = "%s. Additionally, %s" % (first_error, msg) raise OSError(msg) return backend.load_library(path, flags) def _make_ffi_library(ffi, libname, flags): 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) value = backendlib.load_function(BType, name) 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 addressof_var(name): try: return addr_variables[name] except KeyError: with ffi._lock: if name not in addr_variables: key = 'variable ' + name tp, _ = ffi._parser._declarations[key] BType = ffi._get_cached_btype(tp) if BType.kind != 'array': BType = model.pointer_cache(ffi, BType) p = backendlib.load_function(BType, name) addr_variables[name] = p return addr_variables[name] # 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] addr_variables = {} # def update_accessors(): if accessors_version[0] is ffi._cdef_version: return # 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() def __addressof__(self, name): if name in library.__dict__: return library.__dict__[name] if name in FFILibrary.__dict__: return addressof_var(name) make_accessor(name) if name in library.__dict__: return library.__dict__[name] if name in FFILibrary.__dict__: return addressof_var(name) raise AttributeError("cffi library has no function or " "global variable named '%s'" % (name,)) def __cffi_close__(self): backendlib.close_lib() self.__dict__.clear() # if isinstance(libname, basestring): 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/backend_ctypes.py0000644000175100001770000012272600000000000020354 0ustar00runnerdocker00000000000000import 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): v_is_ptr = not isinstance(self, CTypesGenericPrimitive) w_is_ptr = (isinstance(other, CTypesData) and not isinstance(other, CTypesGenericPrimitive)) if v_is_ptr and w_is_ptr: return cmpfunc(self._convert_to_address(None), other._convert_to_address(None)) elif v_is_ptr or w_is_ptr: return NotImplemented else: if isinstance(self, CTypesGenericPrimitive): self = self._value if isinstance(other, CTypesGenericPrimitive): other = other._value return cmpfunc(self, other) 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(self._convert_to_address(None)) def _to_string(self, maxlen): raise TypeError("string(): %r" % (self,)) class CTypesGenericPrimitive(CTypesData): __slots__ = [] def __hash__(self): return hash(self._value) 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) __bool__ = __nonzero__ @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 int(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__) def __nonzero__(self): return ord(self._value) != 0 else: def __nonzero__(self): return self._value != 0 __bool__ = __nonzero__ 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: if isinstance(init, CTypesGenericArray): if (len(init) != len(blob) or not isinstance(init, CTypesArray)): raise TypeError("length/type mismatch: %s" % (init,)) 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, pack=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 elif pack: struct_or_union._pack_ = pack 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) _weakref_cache_ref = None def gcp(self, cdata, destructor, size=0): if self._weakref_cache_ref is None: import weakref class MyRef(weakref.ref): def __eq__(self, other): myref = self() return self is other or ( myref is not None and myref is other()) def __ne__(self, other): return not (self == other) def __hash__(self): try: return self._hash except AttributeError: self._hash = hash(self()) return self._hash self._weakref_cache_ref = {}, MyRef weak_cache, MyRef = self._weakref_cache_ref if destructor is None: try: del weak_cache[MyRef(cdata)] except KeyError: raise TypeError("Can remove destructor only on a object " "previously returned by ffi.gc()") return None def remove(k): cdata, destructor = weak_cache.pop(k, (None, None)) if destructor is not None: destructor(cdata) new_cdata = self.cast(self.typeof(cdata), cdata) assert new_cdata is not cdata weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) return new_cdata 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)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/cffi_opcode.py0000644000175100001770000001313400000000000017626 0ustar00runnerdocker00000000000000from .error import VerificationError 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): 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 PRIM_FLOATCOMPLEX = 48 PRIM_DOUBLECOMPLEX = 49 PRIM_CHAR16 = 50 PRIM_CHAR32 = 51 _NUM_PRIM = 52 _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, 'float _Complex': PRIM_FLOATCOMPLEX, 'double _Complex': PRIM_DOUBLECOMPLEX, '_Bool': PRIM_BOOL, 'wchar_t': PRIM_WCHAR, 'char16_t': PRIM_CHAR16, 'char32_t': PRIM_CHAR32, '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:] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/commontypes.py0000644000175100001770000000520100000000000017737 0ustar00runnerdocker00000000000000import sys from . import model from .error import FFIError 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 FFIError("The Windows type %r is only available after " "you call ffi.set_unicode()" % (commontype,)) else: if commontype == cdecl: raise FFIError( "Unsupported type: %r. Please look at " "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations " "and file an issue if you think this type should really " "be supported." % (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()) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/cparser.py0000644000175100001770000012630700000000000017034 0ustar00runnerdocker00000000000000from . import model from .commontypes import COMMON_TYPES, resolve_common_type from .error import FFIError, CDefError 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 def _workaround_for_static_import_finders(): # Issue #392: packaging tools like cx_Freeze can not find these # because pycparser uses exec dynamic import. This is an obscure # workaround. This function is never called. import pycparser.yacctab import pycparser.lextab CDEF_SOURCE_STRING = "" _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_line_directive = re.compile(r"^[ \t]*#[ \t]*(?:line|\d+)\b.*$", 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*"' r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.') _r_star_const_space = re.compile( # matches "* const " r"[*]\s*((const|volatile|restrict)\b\s*)+") _r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+" r"\.\.\.") _r_float_dotdotdot = re.compile(r"\b(double|float)\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; # # input: `extern "Python+C" int foo(int);` # output: # void __cffi_extern_python_plus_c_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()]) if 'C' in match.group(1): parts.append('void __cffi_extern_python_plus_c_start; ') else: parts.append('void __cffi_extern_python_start; ') if csource[endpos] == '{': # grouping variant closing = csource.find('}', endpos) if closing < 0: raise 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 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 _warn_for_string_literal(csource): if '"' not in csource: return for line in csource.splitlines(): if '"' in line and not line.lstrip().startswith('#'): import warnings warnings.warn("String literal found in cdef() or type source. " "String literals are ignored here, but you should " "remove them anyway because some character sequences " "confuse pre-parsing.") break def _warn_for_non_extern_non_static_global_variable(decl): if not decl.storage: import warnings warnings.warn("Global variable '%s' in cdef(): for consistency " "with C it should have a storage class specifier " "(usually 'extern')" % (decl.name,)) def _remove_line_directives(csource): # _r_line_directive matches whole lines, without the final \n, if they # start with '#line' with some spacing allowed, or '#NUMBER'. This # function stores them away and replaces them with exactly the string # '#line@N', where N is the index in the list 'line_directives'. line_directives = [] def replace(m): i = len(line_directives) line_directives.append(m.group()) return '#line@%d' % i csource = _r_line_directive.sub(replace, csource) return csource, line_directives def _put_back_line_directives(csource, line_directives): def replace(m): s = m.group() if not s.startswith('#line@'): raise AssertionError("unexpected #line directive " "(should have been processed and removed") return line_directives[int(s[6:])] return _r_line_directive.sub(replace, csource) def _preprocess(csource): # First, remove the lines of the form '#line N "filename"' because # the "filename" part could confuse the rest csource, line_directives = _remove_line_directives(csource) # Remove comments. NOTE: this only work because the cdef() section # should not contain any string literals (except in line directives)! def replace_keeping_newlines(m): return ' ' + m.group().count('\n') * '\n' csource = _r_comment.sub(replace_keeping_newlines, 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) # # Now there should not be any string literal left; warn if we get one _warn_for_string_literal(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 "int ..." or "unsigned long int..." with "__dotdotdotint__" csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource) # Replace "float ..." or "double..." with "__dotdotdotfloat__" csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource) # Replace all remaining "..." with the same name, "__dotdotdot__", # which is declared with a typedef for the purpose of C parsing. csource = csource.replace('...', ' __dotdotdot__ ') # Finally, put back the line directives csource = _put_back_line_directives(csource, line_directives) return csource, 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 = [] csourcelines.append('# 1 ""') for typename in typenames: csourcelines.append('typedef int %s;' % typename) csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,' ' __dotdotdot__;') # this forces pycparser to consider the following in the file # called from line 1 csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,)) csourcelines.append(csource) fullcsource = '\n'.join(csourcelines) if lock is not None: lock.acquire() # pycparser is not thread-safe... try: ast = _get_parser().parse(fullcsource) 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 interpret that as a line number. This will not work if # the user gives explicit ``# NUM "FILE"`` directives. line = None msg = str(e) match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg) if match: linenum = int(match.group(1), 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 CDefError(msg) def parse(self, csource, override=False, packed=False, pack=None, dllexport=False): if packed: if packed != True: raise ValueError("'packed' should be False or True; use " "'pack' to give another value") if pack: raise ValueError("cannot give both 'pack' and 'packed'") pack = 1 elif pack: if pack & (pack - 1): raise ValueError("'pack' must be a power of two, not %r" % (pack,)) else: pack = 0 prev_options = self._options try: self._options = {'override': override, 'packed': pack, '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 else: assert 0 current_decl = None # try: self._inside_extern_python = '__cffi_extern_python_stop' for decl in iterator: current_decl = decl if isinstance(decl, pycparser.c_ast.Decl): self._parse_decl(decl) elif isinstance(decl, pycparser.c_ast.Typedef): if not decl.name: raise 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].startswith('__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[-1].startswith('__dotdotdot')): realtype = self._get_unknown_ptr_type(decl) else: realtype, quals = self._get_type_and_quals( decl.type, name=decl.name, partial_length_ok=True, typedef_example="*(%s *)0" % (decl.name,)) self._declare('typedef ' + decl.name, realtype, quals=quals) elif decl.__class__.__name__ == 'Pragma': pass # skip pragma, only in pycparser 2.15 else: raise CDefError("unexpected <%s>: this construct is valid " "C but not valid in cdef()" % decl.__class__.__name__, decl) except CDefError as e: if len(e.args) == 1: e.args = e.args + (current_decl,) raise except 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 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 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 == '__cffi_extern_python_start': tag = 'extern_python ' elif self._inside_extern_python == '__cffi_extern_python_plus_c_start': tag = 'extern_python_plus_c ' 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 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 = decl.name else: if self._inside_extern_python !='__cffi_extern_python_stop': raise 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: _warn_for_non_extern_non_static_global_variable(decl) 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 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 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, typedef_example=None): # 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) # a hack: in 'typedef int foo_t[...][...];', don't use '...' as # the length but use directly the C expression that would be # generated by recompiler.py. This lets the typedef be used in # many more places within recompiler.py if typedef_example is not None: if length == '...': length = '_cffi_array_len(%s)' % (typedef_example,) typedef_example = "*" + typedef_example # tp, quals = self._get_type_and_quals(typenode.type, partial_length_ok=partial_length_ok, typedef_example=typedef_example) 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 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 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 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 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 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 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 '0' <= s[0] <= '9': s = s.rstrip('uUlL') try: if s.startswith('0'): return int(s, 8) else: return int(s, 10) except ValueError: if len(s) > 1: if s.lower()[0:2] == '0x': return int(s, 16) elif s.lower()[0:2] == '0b': return int(s, 2) raise CDefError("invalid constant %r" % (s,)) elif s[0] == "'" and s[-1] == "'" and ( len(s) == 3 or (len(s) == 4 and s[1] == "\\")): return ord(s[-2]) else: raise 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 (isinstance(exprnode, pycparser.c_ast.ID) and exprnode.name == '__dotdotdotarray__'): if partial_length_ok: self._partial_length = True return '...' raise FFIError(":%d: unsupported '[...]' here, cannot derive " "the actual array length in this context" % exprnode.coord.line) # if isinstance(exprnode, pycparser.c_ast.BinaryOp): left = self._parse_constant(exprnode.left) right = self._parse_constant(exprnode.right) if exprnode.op == '+': return left + right elif exprnode.op == '-': return left - right elif exprnode.op == '*': return left * right elif exprnode.op == '/': return self._c_div(left, right) elif exprnode.op == '%': return left - self._c_div(left, right) * right elif exprnode.op == '<<': return left << right elif exprnode.op == '>>': return left >> right elif exprnode.op == '&': return left & right elif exprnode.op == '|': return left | right elif exprnode.op == '^': return left ^ right # raise FFIError(":%d: unsupported expression: expected a " "simple numeric constant" % exprnode.coord.line) def _c_div(self, a, b): result = a // b if ((a < 0) ^ (b < 0)) and (a % b) != 0: result += 1 return result 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 if typenames == ['__dotdotdot__']: return model.unknown_type(decl.name) if typenames == ['__dotdotdotint__']: if self._uses_new_feature is None: self._uses_new_feature = "'typedef int... %s'" % decl.name return model.UnknownIntegerType(decl.name) if typenames == ['__dotdotdotfloat__']: # note: not for 'long double' so far if self._uses_new_feature is None: self._uses_new_feature = "'typedef float... %s'" % decl.name return model.UnknownFloatType(decl.name) raise FFIError(':%d: unsupported usage of "..." in typedef' % decl.coord.line) def _get_unknown_ptr_type(self, decl): if decl.type.type.type.names == ['__dotdotdot__']: return model.unknown_ptr_type(decl.name) raise FFIError(':%d: unsupported usage of "..." in typedef' % decl.coord.line) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/error.py0000644000175100001770000000155500000000000016523 0ustar00runnerdocker00000000000000 class FFIError(Exception): __module__ = 'cffi' class CDefError(Exception): __module__ = 'cffi' def __str__(self): try: current_decl = self.args[1] filename = current_decl.coord.file linenum = current_decl.coord.line prefix = '%s:%d: ' % (filename, linenum) except (AttributeError, TypeError, IndexError): prefix = '' return '%s%s' % (prefix, self.args[0]) class VerificationError(Exception): """ An error raised when verification fails """ __module__ = 'cffi' class VerificationMissing(Exception): """ An error raised when incomplete structures are passed into cdef, but no verification has been done """ __module__ = 'cffi' class PkgConfigError(Exception): """ An error raised for missing modules in pkg-config """ __module__ = 'cffi' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/ffiplatform.py0000644000175100001770000000700000000000000017672 0ustar00runnerdocker00000000000000import sys, os from .error import VerificationError LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', 'extra_objects', 'depends'] def get_extension(srcfilename, modname, sources=(), **kwds): from cffi._shimmed_dist_utils 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, debug=None): """Compile a C extension module using distutils.""" saved_environ = os.environ.copy() try: outputfilename = _build(tmpdir, ext, compiler_verbose, debug) 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, debug=None): # XXX compact but horrible :-( from cffi._shimmed_dist_utils import Distribution, CompileError, LinkError, set_threshold, set_verbosity dist = Distribution({'ext_modules': [ext]}) dist.parse_config_files() options = dist.get_option_dict('build_ext') if debug is None: debug = sys.flags.debug options['debug'] = ('ffiplatform', debug) options['force'] = ('ffiplatform', True) options['build_lib'] = ('ffiplatform', tmpdir) options['build_temp'] = ('ffiplatform', tmpdir) # try: old_level = set_threshold(0) or 0 try: set_verbosity(compiler_verbose) dist.run_command('build_ext') cmd_obj = dist.get_command_obj('build_ext') [soname] = cmd_obj.get_outputs() finally: set_threshold(old_level) except (CompileError, 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/lock.py0000644000175100001770000000135300000000000016316 0ustar00runnerdocker00000000000000import 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/model.py0000644000175100001770000005243600000000000016476 0ustar00runnerdocker00000000000000import types import weakref from .lock import allocate_lock from .error import CDefError, VerificationError, VerificationMissing # 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: 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): def is_complex_type(self): return False 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', 'float _Complex': 'j', 'double _Complex': 'j', '_Bool': 'i', # the following types are not primitive in the C sense 'wchar_t': 'c', 'char16_t': 'c', 'char32_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 is_complex_type(self): return self.ALL_PRIMITIVE_TYPES[self.name] == 'j' 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): raise 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 = " *&" if totype.is_array_type: extra = "(%s)" % (extra.lstrip(),) extra = qualify(quals, extra) 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 length_is_unknown(self): return isinstance(self.length, str) def resolve_length(self, newlength): return ArrayType(self.item, newlength) def build_backend_type(self, ffi, finishlist): if self.length_is_unknown(): raise 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 = 0 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 anonymous_struct_fields(self): if self.fldtypes is not None: for name, type in zip(self.fldnames, self.fldtypes): if name == '' and isinstance(type, StructOrUnion): yield type def enumfields(self, expand_anonymous_struct_union=True): 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) and expand_anonymous_struct_union): # 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)) extra_flags = () if self.packed: if self.packed == 1: extra_flags = (8,) # SF_PACKED else: extra_flags = (0, self.packed) ffi._backend.complete_struct_or_union(BType, lst, self, -1, -1, *extra_flags) # 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_is_unknown(): # 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): raise VerificationError(msg) def check_not_partial(self): if self.partial and self.fixedlayout is None: raise 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: raise VerificationMissing(self._get_c_name()) def build_backend_type(self, ffi, finishlist): self.check_not_partial() base_btype = self.build_baseinttype(ffi, finishlist) return global_cache(self, ffi, 'new_enum_type', self.get_official_name(), self.enumerators, self.enumvalues, base_btype, key=self) def build_baseinttype(self, ffi, finishlist): if self.baseinttype is not None: return self.baseinttype.get_cached_btype(ffi, finishlist) # if self.enumvalues: smallest_value = min(self.enumvalues) largest_value = max(self.enumvalues) else: import warnings try: # XXX! The goal is to ensure that the warnings.warn() # will not suppress the warning. We want to get it # several times if we reach this point several times. __warningregistry__.clear() except NameError: pass warnings.warn("%r has no values explicitly defined; " "guessing that it is equivalent to 'unsigned int'" % 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 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() _typecache_cffi_backend = weakref.WeakValueDictionary() def get_typecache(backend): # returns _typecache_cffi_backend if backend is the _cffi_backend # module, or type(backend).__typecache if backend is an instance of # CTypesBackend (or some FakeBackend class during tests) if isinstance(backend, types.ModuleType): return _typecache_cffi_backend with global_lock: if not hasattr(type(backend), '__typecache'): type(backend).__typecache = weakref.WeakValueDictionary() return type(backend).__typecache def global_cache(srctype, ffi, funcname, *args, **kwds): key = kwds.pop('key', (funcname, args)) assert not kwds try: return ffi._typecache[key] except KeyError: pass 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._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:] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/parse_c_type.h0000644000175100001770000001353000000000000017642 0ustar00runnerdocker00000000000000 /* 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_PRIM_FLOATCOMPLEX 48 #define _CFFI_PRIM_DOUBLECOMPLEX 49 #define _CFFI_PRIM_CHAR16 50 #define _CFFI_PRIM_CHAR32 51 #define _CFFI__NUM_PRIM 52 #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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/pkgconfig.py0000644000175100001770000001042600000000000017336 0ustar00runnerdocker00000000000000# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi import sys, os, subprocess from .error import PkgConfigError def merge_flags(cfg1, cfg2): """Merge values from cffi config flags cfg2 to cf1 Example: merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) {"libraries": ["one", "two"]} """ for key, value in cfg2.items(): if key not in cfg1: cfg1[key] = value else: if not isinstance(cfg1[key], list): raise TypeError("cfg1[%r] should be a list of strings" % (key,)) if not isinstance(value, list): raise TypeError("cfg2[%r] should be a list of strings" % (key,)) cfg1[key].extend(value) return cfg1 def call(libname, flag, encoding=sys.getfilesystemencoding()): """Calls pkg-config and returns the output if found """ a = ["pkg-config", "--print-errors"] a.append(flag) a.append(libname) try: pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except EnvironmentError as e: raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) bout, berr = pc.communicate() if pc.returncode != 0: try: berr = berr.decode(encoding) except Exception: pass raise PkgConfigError(berr.strip()) if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x try: bout = bout.decode(encoding) except UnicodeDecodeError: raise PkgConfigError("pkg-config %s %s returned bytes that cannot " "be decoded with encoding %r:\n%r" % (flag, libname, encoding, bout)) if os.altsep != '\\' and '\\' in bout: raise PkgConfigError("pkg-config %s %s returned an unsupported " "backslash-escaped output:\n%r" % (flag, libname, bout)) return bout def flags_from_pkgconfig(libs): r"""Return compiler line flags for FFI.set_source based on pkg-config output Usage ... ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) If pkg-config is installed on build machine, then arguments include_dirs, library_dirs, libraries, define_macros, extra_compile_args and extra_link_args are extended with an output of pkg-config for libfoo and libbar. Raises PkgConfigError in case the pkg-config call fails. """ def get_include_dirs(string): return [x[2:] for x in string.split() if x.startswith("-I")] def get_library_dirs(string): return [x[2:] for x in string.split() if x.startswith("-L")] def get_libraries(string): return [x[2:] for x in string.split() if x.startswith("-l")] # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils def get_macros(string): def _macro(x): x = x[2:] # drop "-D" if '=' in x: return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") else: return (x, None) # "-Dfoo" => ("foo", None) return [_macro(x) for x in string.split() if x.startswith("-D")] def get_other_cflags(string): return [x for x in string.split() if not x.startswith("-I") and not x.startswith("-D")] def get_other_libs(string): return [x for x in string.split() if not x.startswith("-L") and not x.startswith("-l")] # return kwargs for given libname def kwargs(libname): fse = sys.getfilesystemencoding() all_cflags = call(libname, "--cflags") all_libs = call(libname, "--libs") return { "include_dirs": get_include_dirs(all_cflags), "library_dirs": get_library_dirs(all_libs), "libraries": get_libraries(all_libs), "define_macros": get_macros(all_cflags), "extra_compile_args": get_other_cflags(all_cflags), "extra_link_args": get_other_libs(all_libs), } # merge all arguments together ret = {} for libname in libs: lib_flags = kwargs(libname) merge_flags(ret, lib_flags) return ret ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/recompiler.py0000644000175100001770000017613100000000000017536 0ustar00runnerdocker00000000000000import os, sys, io from . import ffiplatform, model from .error import VerificationError from .cffi_opcode import * VERSION_BASE = 0x2601 VERSION_EMBEDDED = 0x2701 VERSION_CHAR16CHAR32 = 0x2801 USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or sys.version_info >= (3, 5)) 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 self._version = VERSION_BASE def needs_version(self, ver): self._version = max(self._version, ver) 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 _enum_fields(self, tp): # When producing C, expand all anonymous struct/union fields. # That's necessary to have C code checking the offsets of the # individual fields contained in them. When producing Python, # don't do it and instead write it like it is, with the # corresponding fields having an empty name. Empty names are # recognized at runtime when we import the generated Python # file. expand_anonymous_struct_union = not self.target_is_python return tp.enumfields(expand_anonymous_struct_union) 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 self._enum_fields(tp): 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 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 if self.ffi._embedding is not None: prnt('#define _CFFI_USE_EMBEDDING') if not USE_LIMITED_API: prnt('#define _CFFI_NO_LIMITED_API') # # 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('static const char _CFFI_PYTHON_STARTUP_CODE[] = {') self._print_string_literal_in_array(self.ffi._embedding) prnt('0 };') 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') i = lines.index('#include "_cffi_errors.h"\n') lines[i:i+1] = self._rel_readlines('_cffi_errors.h') prnt(''.join(lines)) self.needs_version(VERSION_EMBEDDED) # # 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 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 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 > 0 or self.ffi._embedding is not None: flags |= 1 # set to mean that we use extern "Python" prnt(' %d, /* flags */' % flags) prnt('};') prnt() # # the init function prnt('#ifdef __GNUC__') prnt('# pragma GCC visibility push(default) /* for -fvisibility= */') prnt('#endif') prnt() prnt('#ifdef PYPY_VERSION') prnt('PyMODINIT_FUNC') prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) prnt('{') if flags & 1: 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 *)0x%x;' % self._version) prnt(' p[1] = &_cffi_type_context;') prnt('#if PY_MAJOR_VERSION >= 3') prnt(' return NULL;') prnt('#endif') 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", 0x%x, &_cffi_type_context);' % ( self.module_name, self._version)) prnt('}') prnt('#else') prnt('PyMODINIT_FUNC') prnt('init%s(void)' % (base_module_name,)) prnt('{') prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( self.module_name, self._version)) prnt('}') prnt('#endif') prnt() prnt('#ifdef __GNUC__') prnt('# pragma GCC visibility pop') prnt('#endif') self._version = None 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 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 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 = 0x%x," % (self._version,)) self._version = None # # 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) and not tp.is_complex_type(): 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: cname = tp.get_c_name('') converter = '(%s)_cffi_to_c_%s' % (cname, tp.name.replace(' ', '_')) if cname in ('char16_t', 'char32_t'): self.needs_version(VERSION_CHAR16CHAR32) errvalue = '-1' # elif isinstance(tp, model.PointerType): self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, tovar, errcode) return # elif (isinstance(tp, model.StructOrUnionOrEnum) or isinstance(tp, model.BasePrimitiveType)): # a struct (not a struct pointer) as a function argument; # or, a complex (the same code works) 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, freelines): if isinstance(tp, model.PointerType): localvars.add('Py_ssize_t datasize') localvars.add('struct _cffi_freeme_s *large_args_free = NULL') freelines.add('if (large_args_free != NULL)' ' _cffi_free_array_arguments(large_args_free);') 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(' %s = ((size_t)datasize) <= 640 ? ' '(%s)alloca((size_t)datasize) : NULL;' % ( tovar, tp.get_c_name(''))) self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) self._prnt(' datasize, &large_args_free) < 0)') 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() and tp.name != '_Bool': 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' and not tp.is_complex_type(): cname = tp.name.replace(' ', '_') if cname in ('char16_t', 'char32_t'): self.needs_version(VERSION_CHAR16CHAR32) return '_cffi_from_c_%s(%s)' % (cname, 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.StructOrUnion): 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 _typedef_type(self, tp, name): return self._global_type(tp, "(*(%s *)0)" % (name,)) def _generate_cpy_typedef_collecttype(self, tp, name): self._do_collect_type(self._typedef_type(tp, name)) 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): tp = self._typedef_type(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() freelines = set() for type in tp.args: self._extra_local_variables(type, localvars, freelines) for decl in sorted(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) prnt(' PyObject *pyresult;') 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() prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( name, len(rng), len(rng), ', '.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();') 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(' pyresult = %s;' % self._convert_expr_from_c(tp.result, 'result', 'result type')) for freeline in freelines: prnt(' ' + freeline) prnt(' return pyresult;') else: for freeline in freelines: prnt(' ' + freeline) 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. We also do that for # complex args and return type. def need_indirection(type): return (isinstance(type, model.StructOrUnion) or (isinstance(type, model.PrimitiveType) and type.is_complex_type())) difference = False arguments = [] call_arguments = [] context = 'argument of %s' % name for i, type in enumerate(tp.args): indirection = '' if need_indirection(type): 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 need_indirection(tp_result): 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) if self.target_is_python: # also requires nested anon struct/unions in ABI mode, recursively for fldtype in tp.anonymous_struct_fields(): self._struct_collecttype(fldtype) 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 self._enum_fields(tp): try: if ftype.is_integer_type() or fbitsize >= 0: # accept all integers, but complain on float or double if fname != '': prnt(" (void)((p->%s) | 0); /* 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 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 any(tp.anonymous_struct_fields()): pass # field layout obtained silently from the C compiler else: flags.append("_CFFI_F_CHECK_FIELDS") if tp.packed: if tp.packed > 1: raise NotImplementedError( "%r is declared with 'pack=%r'; only 0 or 1 are " "supported in API mode (try to use \"...;\", which " "does not require a 'pack' declaration)" % (tp, 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(self._enum_fields(tp)) for fldname, fldtype, fbitsize, fqual in enumfields: fldtype = self._field_type(tp, fldname, fldtype) self._check_not_opaque(fldtype, "field '%s.%s'" % (tp.name, fldname)) # 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 _check_not_opaque(self, tp, location): while isinstance(tp, model.ArrayType): tp = tp.item if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: raise TypeError( "%s is of an opaque type (not declared in cdef())" % location) 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 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 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) _generate_cpy_dllexport_python_collecttype = \ _generate_cpy_extern_python_plus_c_collecttype = \ _generate_cpy_extern_python_collecttype def _extern_python_decl(self, tp, name, tag_and_space): 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", %s, 0, 0 };' % ( self.module_name, 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) prnt('%s%s' % (tag_and_space, 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_extern_python_decl(self, tp, name): self._extern_python_decl(tp, name, 'static ') def _generate_cpy_dllexport_python_decl(self, tp, name): self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') def _generate_cpy_extern_python_plus_c_decl(self, tp, name): self._extern_python_decl(tp, name, '') def _generate_cpy_extern_python_ctx(self, tp, name): if self.target_is_python: raise 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)) _generate_cpy_dllexport_python_ctx = \ _generate_cpy_extern_python_plus_c_ctx = \ _generate_cpy_extern_python_ctx def _print_string_literal_in_array(self, s): prnt = self._prnt prnt('// # NB. this is not a string because of a size limit in MSVC') if not isinstance(s, bytes): # unicode s = s.encode('utf-8') # -> bytes else: s.decode('utf-8') # got bytes, check for valid utf-8 try: s.decode('ascii') except UnicodeDecodeError: s = b'# -*- encoding: utf8 -*-\n' + s for line in s.splitlines(True): comment = line if type('//') is bytes: # python2 line = map(ord, line) # make a list of integers else: # python3 # type(line) is bytes, which enumerates like a list of integers comment = ascii(comment)[1:-1] prnt(('// ' + comment).rstrip()) printed_line = '' for c in line: if len(printed_line) >= 76: prnt(printed_line) printed_line = '' printed_line += '%d,' % (c,) prnt(printed_line) # ---------- # 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 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, verbose): if verbose: print("generating %s" % (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 if verbose: print("(already up-to-date)") 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, verbose=False): assert preamble is not None return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, verbose) def make_py_source(ffi, module_name, target_py_file, verbose=False): return _make_c_or_py_source(ffi, module_name, None, target_py_file, verbose) 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 cffi._shimmed_dist_utils 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 cffi._shimmed_dist_utils 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 cffi._shimmed_dist_utils 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, debug=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, verbose=compiler_verbose) if call_c_compiler: patchlist = [] cwd = os.getcwd() try: if embedding: _patch_for_embedding(patchlist) if target != '*': _patch_for_target(patchlist, target) if compiler_verbose: if tmpdir == '.': msg = 'the current directory is' else: msg = 'setting the current directory to' print('%s %r' % (msg, os.path.abspath(tmpdir))) os.chdir(tmpdir) outputfilename = ffiplatform.compile('.', ext, compiler_verbose, debug) 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, verbose=compiler_verbose) if call_c_compiler: return c_file else: return None, updated ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/setuptools_ext.py0000644000175100001770000002124700000000000020473 0ustar00runnerdocker00000000000000import os import sys try: basestring except NameError: # Python 3.x basestring = str def error(msg): from cffi._shimmed_dist_utils 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 _set_py_limited_api(Extension, kwds): """ Add py_limited_api to kwds if setuptools >= 26 is in use. Do not alter the setting if it already exists. Setuptools takes care of ignoring the flag on Python 2 and PyPy. CPython itself should ignore the flag in a debugging version (by not listing .abi3.so in the extensions it supports), but it doesn't so far, creating troubles. That's why we check for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) On Windows, with CPython <= 3.4, it's better not to use py_limited_api because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. Recently (2020) we started shipping only >= 3.5 wheels, though. So we'll give it another try and set py_limited_api on Windows >= 3.5. """ from cffi import recompiler if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') and recompiler.USE_LIMITED_API): import setuptools try: setuptools_major_version = int(setuptools.__version__.partition('.')[0]) if setuptools_major_version >= 26: kwds['py_limited_api'] = True except ValueError: # certain development versions of setuptools # If we don't know the version number of setuptools, we # try to set 'py_limited_api' anyway. At worst, we get a # warning. kwds['py_limited_api'] = True return kwds def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): # We are a setuptools extension. Need this build_ext for py_limited_api. from setuptools.command.build_ext import build_ext from cffi._shimmed_dist_utils import Extension, log, mkpath from cffi import recompiler allsources = ['$PLACEHOLDER'] allsources.extend(kwds.pop('sources', [])) kwds = _set_py_limited_api(Extension, kwds) 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 setuptools.command.build_py import build_py from setuptools.command.build_ext import build_ext from cffi._shimmed_dist_utils import log, mkpath 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)) def get_source_files(self): # This is called from 'setup.py sdist' only. Exclude # the generate .py module in this case. saved_py_modules = self.py_modules try: if saved_py_modules: self.py_modules = [m for m in saved_py_modules if m != module_name] return base_class.get_source_files(self) finally: self.py_modules = saved_py_modules dist.cmdclass['build_py'] = build_py_make_mod # distutils and setuptools have no notion I could find of a # generated python module. If we don't add module_name to # dist.py_modules, then things mostly work but there are some # combination of options (--root and --record) that will miss # the module. So we add it here, which gives a few apparently # harmless warnings about not finding the file outside the # build directory. # Then we need to hack more in get_source_files(); see above. if dist.py_modules is None: dist.py_modules = [] dist.py_modules.append(module_name) # 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/vengine_cpy.py0000644000175100001770000012452700000000000017705 0ustar00runnerdocker00000000000000# # DEPRECATED: implementation for ffi.verify() # import sys from . import model from .error import VerificationError from . import _imp_emulation as imp 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 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, 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 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, freelines): if isinstance(tp, model.PointerType): localvars.add('Py_ssize_t datasize') localvars.add('struct _cffi_freeme_s *large_args_free = NULL') freelines.add('if (large_args_free != NULL)' ' _cffi_free_array_arguments(large_args_free);') 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(' %s = ((size_t)datasize) <= 640 ? ' 'alloca((size_t)datasize) : NULL;' % (tovar,)) self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) self._prnt(' datasize, &large_args_free) < 0)') 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() and tp.name != '_Bool': 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.StructOrUnion): 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() freelines = set() for type in tp.args: self._extra_local_variables(type, localvars, freelines) for decl in sorted(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)) prnt(' PyObject *pyresult;') 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(' pyresult = %s;' % self._convert_expr_from_c(tp.result, 'result', 'result type')) for freeline in freelines: prnt(' ' + freeline) prnt(' return pyresult;') else: for freeline in freelines: prnt(' ' + freeline) 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 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 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_is_unknown()) 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_is_unknown(): 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 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 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 */ # ifndef __cplusplus typedef unsigned char _Bool; # endif # endif #else # include # if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) # 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_from_c__Bool PyBool_FromLong #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); } union _cffi_union_alignment_u { 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; }; struct _cffi_freeme_s { struct _cffi_freeme_s *next; union _cffi_union_alignment_u alignment; }; #ifdef __GNUC__ __attribute__((unused)) #endif static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg, char **output_data, Py_ssize_t datasize, struct _cffi_freeme_s **freeme) { char *p; if (datasize < 0) return -1; p = *output_data; if (p == NULL) { struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); if (fp == NULL) return -1; fp->next = *freeme; *freeme = fp; p = *output_data = (char *)&fp->alignment; } memset((void *)p, 0, (size_t)datasize); return _cffi_convert_array_from_object(p, ctptr, arg); } #ifdef __GNUC__ __attribute__((unused)) #endif static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme) { do { void *p = (void *)freeme; freeme = freeme->next; PyObject_Free(p); } while (freeme != NULL); } 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)) /**********/ ''' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/vengine_gen.py0000644000175100001770000006407400000000000017663 0ustar00runnerdocker00000000000000# # DEPRECATED: implementation for ffi.verify() # import sys, os import types from . import model from .error import VerificationError 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 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 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 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 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_is_unknown(): 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_is_unknown(): 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 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 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 */ # ifndef __cplusplus typedef unsigned char _Bool; # endif # endif #else # include # if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) # include # endif #endif ''' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/src/cffi/verifier.py0000644000175100001770000002565600000000000017215 0ustar00runnerdocker00000000000000# # DEPRECATED: implementation for ffi.verify() # import sys, os, binascii, shutil, io from . import __version_verifier_modules__ from . import ffiplatform from .error import VerificationError 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) 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 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(['%d.%d' % sys.version_info[:2], __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 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 VerificationError("module already compiled") if not self._has_source: self._write_source() self._compile_module() def load_library(self): """Get a C module from this Verifier instance. Returns an instance of a FFILibrary class that behaves like the objects returned by ffi.dlopen(), but that delegates all operations to the C module. If necessary, the C code is written and compiled first. """ with self.ffi._lock: if not self._has_module: self._locate_module() if not self._has_module: if not self._has_source: self._write_source() self._compile_module() return self._load_library() def get_module_name(self): basename = os.path.basename(self.modulefilename) # kill both the .so extension and the other .'s, as introduced # by Python 3: 'basename.cpython-33m.so' basename = basename.split('.', 1)[0] # and the _d added in Python 2 debug builds --- but try to be # conservative and not kill a legitimate _d if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): basename = basename[:-2] return basename def get_extension(self): if not self._has_source: with self.ffi._lock: if not self._has_source: self._write_source() sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) modname = self.get_module_name() return ffiplatform.get_extension(sourcename, modname, **self.kwds) def generates_python_module(self): return self._vengine._gen_python_module def 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): dirname = os.path.dirname(filename) if dirname and not os.path.isdir(dirname): os.makedirs(dirname) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1477678 cffi-1.16.0/src/cffi.egg-info/0000755000175100001770000000000000000000000016504 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922997.0 cffi-1.16.0/src/cffi.egg-info/PKG-INFO0000644000175100001770000000271000000000000017601 0ustar00runnerdocker00000000000000Metadata-Version: 2.1 Name: cffi Version: 1.16.0 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 Project-URL: Documentation, http://cffi.readthedocs.org/ Project-URL: Source Code, https://github.com/python-cffi/cffi Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues Project-URL: Changelog, https://cffi.readthedocs.io/en/latest/whatsnew.html Project-URL: Downloads, https://github.com/python-cffi/cffi/releases Project-URL: Contact, https://groups.google.com/forum/#!forum/python-cffi Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: License :: OSI Approved :: MIT License Requires-Python: >=3.8 License-File: LICENSE Requires-Dist: pycparser CFFI ==== Foreign Function Interface for Python calling C code. Please see the `Documentation `_. Contact ------- `Mailing list `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922997.0 cffi-1.16.0/src/cffi.egg-info/SOURCES.txt0000644000175100001770000001263200000000000020374 0ustar00runnerdocker00000000000000AUTHORS LICENSE MANIFEST.in README.md pyproject.toml setup.cfg setup.py setup_base.py 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_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/goals.rst doc/source/index.rst doc/source/installation.rst doc/source/overview.rst doc/source/ref.rst doc/source/using.rst doc/source/whatsnew.rst src/c/_cffi_backend.c src/c/call_python.c src/c/cdlopen.c src/c/cffi1_module.c src/c/cglob.c src/c/commontypes.c src/c/ffi_obj.c src/c/file_emulator.h src/c/lib_obj.c src/c/malloc_closure.h src/c/minibuffer.h src/c/misc_thread_common.h src/c/misc_thread_posix.h src/c/misc_win32.h src/c/parse_c_type.c src/c/realize_c_type.c src/c/test_c.py src/c/wchar_helper.h src/c/wchar_helper_3.h src/c/libffi_arm64/ffi.lib src/c/libffi_arm64/include/ffi.h src/c/libffi_arm64/include/fficonfig.h src/c/libffi_arm64/include/ffitarget.h src/c/libffi_x86_x64/ffi.c src/c/libffi_x86_x64/ffi.h src/c/libffi_x86_x64/ffi_common.h src/c/libffi_x86_x64/fficonfig.h src/c/libffi_x86_x64/ffitarget.h src/c/libffi_x86_x64/prep_cif.c src/c/libffi_x86_x64/types.c src/c/libffi_x86_x64/win32.c src/c/libffi_x86_x64/win64.asm src/c/libffi_x86_x64/win64.obj src/cffi/__init__.py src/cffi/_cffi_errors.h src/cffi/_cffi_include.h src/cffi/_embedding.h src/cffi/_imp_emulation.py src/cffi/_shimmed_dist_utils.py src/cffi/api.py src/cffi/backend_ctypes.py src/cffi/cffi_opcode.py src/cffi/commontypes.py src/cffi/cparser.py src/cffi/error.py src/cffi/ffiplatform.py src/cffi/lock.py src/cffi/model.py src/cffi/parse_c_type.h src/cffi/pkgconfig.py src/cffi/recompiler.py src/cffi/setuptools_ext.py src/cffi/vengine_cpy.py src/cffi/vengine_gen.py src/cffi/verifier.py src/cffi.egg-info/PKG-INFO src/cffi.egg-info/SOURCES.txt src/cffi.egg-info/dependency_links.txt src/cffi.egg-info/entry_points.txt src/cffi.egg-info/not-zip-safe src/cffi.egg-info/requires.txt src/cffi.egg-info/top_level.txt testing/__init__.py testing/conftest.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_function_args.py testing/cffi1/test_new_ffi_1.py testing/cffi1/test_parse_c_type.py testing/cffi1/test_pkgconfig.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/empty-test.c testing/embedding/empty.py testing/embedding/initerror.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.py testing/embedding/withunicode.py././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922997.0 cffi-1.16.0/src/cffi.egg-info/dependency_links.txt0000644000175100001770000000000100000000000022552 0ustar00runnerdocker00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922997.0 cffi-1.16.0/src/cffi.egg-info/entry_points.txt0000644000175100001770000000011300000000000021775 0ustar00runnerdocker00000000000000[distutils.setup_keywords] cffi_modules = cffi.setuptools_ext:cffi_modules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922996.0 cffi-1.16.0/src/cffi.egg-info/not-zip-safe0000644000175100001770000000000100000000000020732 0ustar00runnerdocker00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922997.0 cffi-1.16.0/src/cffi.egg-info/requires.txt0000644000175100001770000000001200000000000021075 0ustar00runnerdocker00000000000000pycparser ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922997.0 cffi-1.16.0/src/cffi.egg-info/top_level.txt0000644000175100001770000000002300000000000021231 0ustar00runnerdocker00000000000000_cffi_backend cffi ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1477678 cffi-1.16.0/testing/0000755000175100001770000000000000000000000014771 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/__init__.py0000644000175100001770000000000000000000000017070 0ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1517677 cffi-1.16.0/testing/cffi0/0000755000175100001770000000000000000000000015760 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/__init__.py0000644000175100001770000000000000000000000020057 0ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/backend_tests.py0000644000175100001770000022000300000000000021140 0ustar00runnerdocker00000000000000import pytest import platform import sys, ctypes, ctypes.util 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) def needs_dlopen_none(): if sys.platform == 'win32' and not ctypes.util.find_library('c'): pytest.skip("dlopen(None) cannot work on Windows with this runtime") 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 assert hash(p) == hash(min) assert bool(p) is bool(min) 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) c_decl_ptr = '%s *' % c_decl pytest.raises(OverflowError, ffi.new, c_decl_ptr, min - 1) pytest.raises(OverflowError, ffi.new, c_decl_ptr, max + 1) pytest.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1)) pytest.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 = pytest.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 with pytest.raises(IndexError): p[10] with pytest.raises(IndexError): p[10] = 44 with pytest.raises(IndexError): p[-1] with pytest.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 with pytest.raises(IndexError): p[10] # pytest.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 with pytest.raises(IndexError): p[2] assert repr(p) == "" % (2*SIZE_OF_INT) # p = ffi.new("int[]", 0) with pytest.raises(IndexError): p[0] pytest.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 = pytest.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: pytest.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 not bool(ffi.cast("wchar_t", 0)) assert bool(ffi.cast("wchar_t", 1)) assert bool(ffi.cast("wchar_t", 65535)) if SIZE_OF_WCHAR > 2: assert bool(ffi.cast("wchar_t", 65536)) pytest.raises(TypeError, ffi.new, "wchar_t*", 32) pytest.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 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'] pytest.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 with pytest.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 pytest.raises(TypeError, int, p) pytest.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 with pytest.raises(OverflowError): s.b = 32768 # s = ffi.new("struct foo*", [-2, -3]) assert s.a == -2 assert s.b == -3 assert s.c == 0 with pytest.raises((AttributeError, TypeError)): del s.a assert repr(s) == "" % ( SIZE_OF_INT + 2 * SIZE_OF_SHORT) # pytest.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 pytest.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 with pytest.raises(OverflowError): s[0].b = -32769 with pytest.raises(IndexError): s[1] def test_struct_opaque(self): ffi = FFI(backend=self.Backend()) pytest.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 with pytest.raises(OverflowError): u.b = 32768 # u = ffi.new("union foo*", [-2]) assert u.a == -2 with pytest.raises((AttributeError, TypeError)): del u.a assert repr(u) == "" % SIZE_OF_INT def test_union_opaque(self): ffi = FFI(backend=self.Backend()) pytest.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; };") pytest.raises(TypeError, ffi.new, "union foo*", b'A') pytest.raises(TypeError, ffi.new, "union foo*", 5) pytest.raises(ValueError, ffi.new, "union foo*", [b'A', 5]) u = ffi.new("union foo*", [b'A']) assert u.a == b'A' pytest.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"" pytest.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" with pytest.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" with pytest.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" with pytest.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()) pytest.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] with pytest.raises(TypeError): vp[0] pytest.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 with pytest.raises(TypeError): s.r = a # fails b = ffi.cast("int *", a) s.p = b # works s.q = b # works with pytest.raises(TypeError): s.r = b # fails def test_functionptr_simple(self): ffi = FFI(backend=self.Backend()) pytest.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) with pytest.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 with pytest.raises(TypeError): s.e = 'B' with pytest.raises(TypeError): s.e = '2' with pytest.raises(TypeError): s.e = '#2' with pytest.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 };") needs_dlopen_none() lib = ffi.dlopen(None) assert lib.B == 0 pytest.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]") with pytest.raises(AttributeError): s.b with pytest.raises(AttributeError): s.b = 412 s[0].b = 412 assert s[0].b == 412 with pytest.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"] # pytest.raises(TypeError, iter, ffi.cast("char *", a)) pytest.raises(TypeError, list, ffi.cast("char *", a)) pytest.raises(TypeError, iter, ffi.new("int *")) pytest.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 pytest.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 with pytest.raises(OverflowError): s.a = 512 with pytest.raises(OverflowError): s[0].a = 512 assert s.a == 511 s.a = -512 with pytest.raises(OverflowError): s.a = -513 with pytest.raises(OverflowError): s[0].a = -513 assert s.a == -512 s.c = 3 assert s.c == 3 with pytest.raises(OverflowError): s.c = 4 with pytest.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_integer_comparison(self): ffi = FFI(backend=self.Backend()) x = ffi.cast("int", 123) y = ffi.cast("int", 456) assert x < y # z = ffi.cast("double", 78.9) assert x > z assert y > z 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: pytest.skip(str(e)) assert type(b) is ffi.buffer 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: pytest.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: pytest.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: pytest.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: pytest.skip(str(e)) f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) f.seek(0) assert f.read() == arraytostring(array.array('i', range(1000))) 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: pytest.skip(str(e)) f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) f.seek(0) assert f.read() == arraytostring(array.array('i', range(1000))) 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_ffi_buffer_comparisons(self): ffi = FFI(backend=self.Backend()) ba = bytearray(range(100, 110)) if sys.version_info >= (2, 7): assert ba == memoryview(ba) # justification for the following a = ffi.new("uint8_t[]", list(ba)) c = ffi.new("uint8_t[]", [99] + list(ba)) try: b_full = ffi.buffer(a) b_short = ffi.buffer(a, 3) b_mid = ffi.buffer(a, 6) b_other = ffi.buffer(c, 6) except NotImplementedError as e: pytest.skip(str(e)) else: content = b_full[:] assert content == b_full == ba assert b_other < b_short < b_mid < b_full assert ba > b_mid > ba[0:2] assert b_short != ba[1:4] 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') ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1197677 cffi-1.16.0/testing/cffi0/snippets/0000755000175100001770000000000000000000000017625 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1517677 cffi-1.16.0/testing/cffi0/snippets/distutils_module/0000755000175100001770000000000000000000000023216 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/distutils_module/setup.py0000644000175100001770000000025300000000000024730 0ustar00runnerdocker00000000000000 from distutils.core import setup import snip_basic_verify setup( py_modules=['snip_basic_verify'], ext_modules=[snip_basic_verify.ffi.verifier.get_extension()]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/distutils_module/snip_basic_verify.py0000644000175100001770000000065400000000000027273 0ustar00runnerdocker00000000000000 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_')) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1517677 cffi-1.16.0/testing/cffi0/snippets/distutils_package_1/0000755000175100001770000000000000000000000023544 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/distutils_package_1/setup.py0000644000175100001770000000025400000000000025257 0ustar00runnerdocker00000000000000 from distutils.core import setup import snip_basic_verify1 setup( packages=['snip_basic_verify1'], ext_modules=[snip_basic_verify1.ffi.verifier.get_extension()]) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1517677 cffi-1.16.0/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/0000755000175100001770000000000000000000000027323 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py0000644000175100001770000000065400000000000031441 0ustar00runnerdocker00000000000000 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_')) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1557677 cffi-1.16.0/testing/cffi0/snippets/distutils_package_2/0000755000175100001770000000000000000000000023545 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/distutils_package_2/setup.py0000644000175100001770000000032200000000000025254 0ustar00runnerdocker00000000000000 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()]) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1557677 cffi-1.16.0/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/0000755000175100001770000000000000000000000027325 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py0000644000175100001770000000072300000000000031440 0ustar00runnerdocker00000000000000 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_')) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1557677 cffi-1.16.0/testing/cffi0/snippets/infrastructure/0000755000175100001770000000000000000000000022705 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/infrastructure/setup.py0000644000175100001770000000014400000000000024416 0ustar00runnerdocker00000000000000 from distutils.core import setup setup(packages=['snip_infrastructure'], requires=['cffi']) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1557677 cffi-1.16.0/testing/cffi0/snippets/infrastructure/snip_infrastructure/0000755000175100001770000000000000000000000027016 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py0000644000175100001770000000003300000000000031123 0ustar00runnerdocker00000000000000 def func(): return 42 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1557677 cffi-1.16.0/testing/cffi0/snippets/setuptools_module/0000755000175100001770000000000000000000000023413 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/setuptools_module/setup.py0000644000175100001770000000031200000000000025121 0ustar00runnerdocker00000000000000 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()]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py0000644000175100001770000000065400000000000030630 0ustar00runnerdocker00000000000000 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_')) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1557677 cffi-1.16.0/testing/cffi0/snippets/setuptools_package_1/0000755000175100001770000000000000000000000023741 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/setuptools_package_1/setup.py0000644000175100001770000000031300000000000025450 0ustar00runnerdocker00000000000000 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()]) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1557677 cffi-1.16.0/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/0000755000175100001770000000000000000000000030660 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py0000644000175100001770000000065400000000000032776 0ustar00runnerdocker00000000000000 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_')) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1557677 cffi-1.16.0/testing/cffi0/snippets/setuptools_package_2/0000755000175100001770000000000000000000000023742 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/setuptools_package_2/setup.py0000644000175100001770000000036600000000000025461 0ustar00runnerdocker00000000000000 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()]) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1557677 cffi-1.16.0/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/0000755000175100001770000000000000000000000030662 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py0000644000175100001770000000073000000000000032773 0ustar00runnerdocker00000000000000 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_')) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_cdata.py0000644000175100001770000000165700000000000020456 0ustar00runnerdocker00000000000000from 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" buffer = "buffer type" 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' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_ctypes.py0000644000175100001770000000304500000000000020702 0ustar00runnerdocker00000000000000import sys import pytest 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): pytest.skip("ctypes backend: not supported: " "initializers for function pointers") def test_structptr_argument(self): pytest.skip("ctypes backend: not supported: passing a list " "for a pointer argument") def test_array_argument_as_list(self): pytest.skip("ctypes backend: not supported: passing a list " "for a pointer argument") def test_cast_to_array_type(self): pytest.skip("ctypes backend: not supported: casting to array") def test_nested_anonymous_struct(self): pytest.skip("ctypes backend: not supported: nested anonymous struct") def test_nested_field_offset_align(self): pytest.skip("ctypes backend: not supported: nested anonymous struct") def test_nested_anonymous_union(self): pytest.skip("ctypes backend: not supported: nested anonymous union") def test_nested_anonymous_struct_2(self): pytest.skip("ctypes backend: not supported: nested anonymous union") def test_CData_CType_2(self): if sys.version_info >= (3,): pytest.skip("ctypes backend: not supported in Python 3: CType") backend_tests.BackendTests.test_CData_CType_2(self) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_ffi_backend.py0000644000175100001770000006024100000000000021607 0ustar00runnerdocker00000000000000import sys, platform import pytest from testing.cffi0 import backend_tests, test_function, test_ownlib from testing.support import u 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 = pytest.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 pytest.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()) pytest.raises(TypeError, ffi.new_allocator, free=lambda x: None) # def myalloc2(size): raise LookupError alloc2 = ffi.new_allocator(myalloc2) pytest.raises(LookupError, alloc2, "int[5]") # def myalloc3(size): return 42 alloc3 = ffi.new_allocator(myalloc3) e = pytest.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 = pytest.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) pytest.raises(MemoryError, alloc5, "int[5]") def test_new_struct_containing_struct_containing_array_varsize(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" struct foo_s { int len[100]; short data[]; }; struct bar_s { int abc[100]; struct foo_s tail; }; """) # loop to try to detect heap overwrites, if the size allocated # is too small for i in range(1, 501, 100): p = ffi.new("struct bar_s *", [[10], [[20], [3,4,5,6,7,8,9] * i]]) assert p.abc[0] == 10 assert p.tail.len[0] == 20 assert p.tail.data[0] == 3 assert p.tail.data[6] == 9 assert p.tail.data[7 * i - 1] == 9 def test_bogus_struct_containing_struct_containing_array_varsize(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" struct foo_s { signed char len; signed char data[]; }; struct bar_s { struct foo_s foo; int bcd; }; """) p = ffi.new("struct bar_s *", [[123, [45, 56, 67, 78]], 9999999]) assert p.foo.len == 123 assert p.foo.data[0] == 45 assert p.foo.data[1] == 56 assert p.foo.data[2] == 67 assert p.bcd == 9999999 assert p.foo.data[3] != 78 # has been overwritten with 9999999 def test_issue553(self): import gc, warnings ffi = FFI(backend=self.Backend()) p = ffi.new("int *", 123) with warnings.catch_warnings(record=True) as w: ffi.gc(p, lambda x: None) gc.collect() assert w == [] def test_issue553_from_buffer(self): import gc, warnings ffi = FFI(backend=self.Backend()) buf = b"123" with warnings.catch_warnings(record=True) as w: ffi.from_buffer(buf) gc.collect() assert w == [] 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(""" #include 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)[:] buff1 = ffi.buffer(s) t = lib.try_with_value(fnames.index(name), value) raw2 = ffi.buffer(t, len(raw1))[:] assert raw1 == raw2 buff2 = ffi.buffer(t, len(buff1)) assert buff1 == buff2 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( "not (sys.platform == 'darwin' and platform.machine() == 'arm64')" " and " "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( "(sys.platform == 'darwin' and platform.machine() == 'arm64')" " or " "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( "not (sys.platform == 'darwin' and platform.machine() == 'arm64')" " and " "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( "(sys.platform == 'darwin' and platform.machine() == 'arm64')" " or " "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() ffi.cdef("struct s1 { float x:1; };") with pytest.raises(TypeError): ffi.new("struct s1 *") ffi.cdef("struct s2 { char x:0; };") with pytest.raises(TypeError): ffi.new("struct s2 *") ffi.cdef("struct s3 { char x:9; };") with pytest.raises(TypeError): 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("" % (cb,) res = fptr(b"Hello") assert res == 42 # if not sys.platform.startswith('linux'): pytest.skip("probably no symbol 'stderr' in the lib") ffi.cdef(""" int fputs(const char *, void *); extern void *stderr; """) needs_dlopen_none() 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': pytest.skip("Windows-only test") if self.Backend is CTypesBackend: pytest.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': pytest.skip("Windows-only test") if self.Backend is CTypesBackend: pytest.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: pytest.raises(TypeError, ffi.new, "fnc_t[]", [fns]) pytest.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); extern double myvar; const double myconst; #define MYFOO 42 """) m = ffi.dlopen(lib_m) assert dir(m) == ['MYE1', 'MYE2', 'MYFOO', 'myconst', 'myfunc', 'myvar'] def test_dlclose(self): if self.Backend is CTypesBackend: pytest.skip("not with the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef("int foobar(void); extern int foobaz;") lib = ffi.dlopen(lib_m) ffi.dlclose(lib) e = pytest.raises(ValueError, getattr, lib, 'foobar') assert str(e.value).startswith("library '") assert str(e.value).endswith("' has already been closed") e = pytest.raises(ValueError, getattr, lib, 'foobaz') assert str(e.value).startswith("library '") assert str(e.value).endswith("' has already been closed") e = pytest.raises(ValueError, setattr, lib, 'foobaz', 42) assert str(e.value).startswith("library '") assert str(e.value).endswith("' has already been closed") ffi.dlclose(lib) # does not raise def test_passing_large_list(self): if self.Backend is CTypesBackend: pytest.skip("the ctypes backend doesn't support this") ffi = FFI(backend=self.Backend()) ffi.cdef(""" void getenv(char *); """) needs_dlopen_none() m = ffi.dlopen(None) arg = [b"F", b"O", b"O"] + [b"\x00"] * 20000000 x = m.getenv(arg) assert x is None ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_model.py0000644000175100001770000001174400000000000020500 0ustar00runnerdocker00000000000000from 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]" ptr_type = ConstPointerType(ArrayType( ConstPointerType(PrimitiveType("int")), 5)) assert ptr_type.get_c_name("x") == "int const * const (* x)[5]" ptr_type = PointerType(ArrayType( ConstPointerType(PrimitiveType("int")), 5)) assert ptr_type.get_c_name("x") == "int const *(* x)[5]" ptr_type = ConstPointerType(ArrayType( PointerType(PrimitiveType("int")), 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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_ownlib.py0000644000175100001770000003261600000000000020673 0ustar00runnerdocker00000000000000import sys, os import subprocess, weakref import pytest from cffi import FFI from cffi.backend_ctypes import CTypesBackend from testing.support import u, is_musl 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; typedef struct { unsigned char a, b, c; } THREEBYTES; 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}; EXPORT unsigned short foo_2bytes(unsigned short a) { return (unsigned short)(a + 42); } EXPORT unsigned int foo_4bytes(unsigned int a) { return (unsigned int)(a + 42); } EXPORT void modify_struct_value(RECT r) { r.left = r.right = r.top = r.bottom = 500; } EXPORT THREEBYTES return_three_bytes(void) { THREEBYTES result; result.a = 12; result.b = 34; result.c = 56; return result; } """ 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': # did we already build it? if cls.Backend is CTypesBackend: dll_path = str(udir) + '\\testownlib1.dll' # only ascii for the ctypes backend else: dll_path = str(udir) + '\\' + (u+'testownlib\u03be.dll') # non-ascii char if os.path.exists(dll_path): cls.module = dll_path 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) os.rename(str(udir) + '\\testownlib.dll', dll_path) cls.module = dll_path else: encoded = None if cls.Backend is not CTypesBackend: try: unicode_name = u+'testownlibcaf\xe9' encoded = unicode_name.encode(sys.getfilesystemencoding()) if sys.version_info >= (3,): encoded = str(unicode_name) except UnicodeEncodeError: pass if encoded is None: unicode_name = u+'testownlib' encoded = str(unicode_name) subprocess.check_call( "cc testownlib.c -shared -fPIC -o '%s.so'" % (encoded,), cwd=str(udir), shell=True) cls.module = os.path.join(str(udir), unicode_name + (u+'.so')) print(repr(cls.module)) def test_getting_errno(self): if self.module is None: pytest.skip("fix the auto-generation of the tiny test lib") if sys.platform == 'win32': pytest.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: pytest.skip("fix the auto-generation of the tiny test lib") if sys.platform == 'win32': pytest.skip("fails, errno at multiple addresses") if self.Backend is CTypesBackend and '__pypy__' in sys.modules: pytest.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: pytest.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" extern 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: pytest.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: pytest.skip("fix the auto-generation of the tiny test lib") if self.Backend is CTypesBackend: pytest.skip("not supported by the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef(""" extern int my_array[]; """) ownlib = ffi.dlopen(self.module) for i in range(7): assert ownlib.my_array[i] == i pytest.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: pytest.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: pytest.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: pytest.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; extern 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 def test_addressof_lib(self): if self.module is None: pytest.skip("fix the auto-generation of the tiny test lib") if self.Backend is CTypesBackend: pytest.skip("not implemented with the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef("extern long left; int test_getting_errno(void);") lib = ffi.dlopen(self.module) lib.left = 123456 p = ffi.addressof(lib, "left") assert ffi.typeof(p) == ffi.typeof("long *") assert p[0] == 123456 p[0] += 1 assert lib.left == 123457 pfn = ffi.addressof(lib, "test_getting_errno") assert ffi.typeof(pfn) == ffi.typeof("int(*)(void)") assert pfn == lib.test_getting_errno def test_char16_char32_t(self): if self.module is None: pytest.skip("fix the auto-generation of the tiny test lib") if self.Backend is CTypesBackend: pytest.skip("not implemented with the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef(""" char16_t foo_2bytes(char16_t); char32_t foo_4bytes(char32_t); """) lib = ffi.dlopen(self.module) assert lib.foo_2bytes(u+'\u1234') == u+'\u125e' assert lib.foo_4bytes(u+'\u1234') == u+'\u125e' assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f' def test_modify_struct_value(self): if self.module is None: pytest.skip("fix the auto-generation of the tiny test lib") if self.Backend is CTypesBackend: pytest.skip("fails with the ctypes backend on some architectures") ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { long left; long top; long right; long bottom; } RECT; void modify_struct_value(RECT r); """) lib = ffi.dlopen(self.module) s = ffi.new("RECT *", [11, 22, 33, 44]) lib.modify_struct_value(s[0]) assert s.left == 11 assert s.top == 22 assert s.right == 33 assert s.bottom == 44 def test_dlopen_handle(self): if self.module is None: pytest.skip("fix the auto-generation of the tiny test lib") if sys.platform == 'win32' or is_musl: pytest.skip("uses 'dl' explicitly") if self.__class__.Backend is CTypesBackend: pytest.skip("not for the ctypes backend") backend = self.Backend() ffi1 = FFI(backend=backend) ffi1.cdef("""void *dlopen(const char *filename, int flags); int dlclose(void *handle);""") lib1 = ffi1.dlopen('dl') handle = lib1.dlopen(self.module.encode(sys.getfilesystemencoding()), backend.RTLD_LAZY) assert ffi1.typeof(handle) == ffi1.typeof("void *") assert handle ffi = FFI(backend=backend) ffi.cdef("""unsigned short foo_2bytes(unsigned short a);""") lib = ffi.dlopen(handle) x = lib.foo_2bytes(1000) assert x == 1042 err = lib1.dlclose(handle) assert err == 0 def test_return_three_bytes(self): if self.module is None: pytest.skip("fix the auto-generation of the tiny test lib") if self.__class__.Backend is CTypesBackend: pytest.skip("not working on win32 on the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { unsigned char a, b, c; } THREEBYTES; THREEBYTES return_three_bytes(void); """) lib = ffi.dlopen(self.module) tb = lib.return_three_bytes() assert tb.a == 12 assert tb.b == 34 assert tb.c == 56 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_parsing.py0000644000175100001770000004702600000000000021045 0ustar00runnerdocker00000000000000import sys, re import pytest from cffi import FFI, FFIError, CDefError, VerificationError from .backend_tests import needs_dlopen_none from testing.support import is_musl 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" buffer = "buffer type" 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' elif is_musl: lib_m = 'c' 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]);") needs_dlopen_none() 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, ...);") needs_dlopen_none() 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); """) needs_dlopen_none() 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); """) needs_dlopen_none() 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[]); """) needs_dlopen_none() 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 // blah\\\\ y(void); double // blah\\ \ etc z(void); """) m = ffi.dlopen(lib_m) m.x m.y m.z def test_dont_remove_comment_in_line_directives(): ffi = FFI(backend=FakeBackend()) e = pytest.raises(CDefError, ffi.cdef, """ \t # \t line \t 8 \t "baz.c" \t some syntax error here """) assert str(e.value) == "parse error\nbaz.c:9:14: before: syntax" # e = pytest.raises(CDefError, ffi.cdef, """ #line 7 "foo//bar.c" some syntax error here """) # assert str(e.value) == "parse error\nfoo//bar.c:8:14: before: syntax" ffi = FFI(backend=FakeBackend()) e = pytest.raises(CDefError, ffi.cdef, """ \t # \t 8 \t "baz.c" \t some syntax error here """) assert str(e.value) == "parse error\nbaz.c:9:14: before: syntax" # e = pytest.raises(CDefError, ffi.cdef, """ # 7 "foo//bar.c" some syntax error here """) assert str(e.value) == "parse error\nfoo//bar.c:8:14: before: syntax" def test_multiple_line_directives(): ffi = FFI(backend=FakeBackend()) e = pytest.raises(CDefError, ffi.cdef, """ #line 5 "foo.c" extern int xx; #line 6 "bar.c" extern int yy; #line 7 "baz.c" some syntax error here #line 8 "yadda.c" extern int zz; """) assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax" # e = pytest.raises(CDefError, ffi.cdef, """ # 5 "foo.c" extern int xx; # 6 "bar.c" extern int yy; # 7 "baz.c" some syntax error here # 8 "yadda.c" extern int zz; """) assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax" def test_commented_line_directive(): ffi = FFI(backend=FakeBackend()) e = pytest.raises(CDefError, ffi.cdef, """ /* #line 5 "foo.c" */ void xx(void); #line 6 "bar.c" /* #line 35 "foo.c" */ some syntax error """) # assert str(e.value) == "parse error\nbar.c:9:14: before: syntax" e = pytest.raises(CDefError, ffi.cdef, """ /* # 5 "foo.c" */ void xx(void); # 6 "bar.c" /* # 35 "foo.c" */ some syntax error """) assert str(e.value) == "parse error\nbar.c:9:14: before: syntax" 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 = pytest.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) == "" pytest.raises(VerificationError, type_bar.get_c_name) assert type_foo.get_c_name() == "foo_t" def test_override(): ffi = FFI(backend=FakeBackend()) needs_dlopen_none() C = ffi.dlopen(None) ffi.cdef("int foo(void);") pytest.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 = pytest.raises(CDefError, ffi.cdef, "int foo(...);") assert str(e.value) == ( ":1: foo: a function with only '(...)' " "as argument is not correct C") def test_parse_error(): ffi = FFI() e = pytest.raises(CDefError, ffi.cdef, " x y z ") assert str(e.value).startswith( 'cannot parse "x y z"\n:1:') e = pytest.raises(CDefError, ffi.cdef, "\n\n\n x y z ") assert str(e.value).startswith( 'cannot parse "x y z"\n:4:') def test_error_custom_lineno(): ffi = FFI() e = pytest.raises(CDefError, ffi.cdef, """ # 42 "foobar" a b c d """) assert str(e.value).startswith('parse error\nfoobar:43:') def test_cannot_declare_enum_later(): ffi = FFI() e = pytest.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 = pytest.raises(CDefError, ffi.cast, "foobarbazunknown", 0) assert str(e.value) == "unknown identifier 'foobarbazunknown'" e = pytest.raises(CDefError, ffi.cast, "foobarbazunknown*", 0) assert str(e.value).startswith('cannot parse "foobarbazunknown*"') e = pytest.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 = pytest.raises(CDefError, ffi.cdef, "void f(foobarbazzz);") assert str(e.value) == (":1: 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': pytest.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(): import warnings 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() with warnings.catch_warnings(record=True) as log: warnings.simplefilter("always") ffi.cdef(input) declarations = ffi._parser._declarations assert ('constant a' in declarations) == expected_output assert ('variable a' in declarations) == (not expected_output) assert len(log) == (1 - 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("extern " + 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, ADDSUB = (POS+TWO)-1, DIVMULINT = (3 * 3) / 2, SHIFT = (1 << 3) >> 1, BINOPS = (0x7 & 0x1) | 0x8, XOR = 0xf ^ 0xa }; """) needs_dlopen_none() C = ffi.dlopen(None) assert C.POS == 1 assert C.TWO == 2 assert C.NIL == 0 assert C.NEG == -1 assert C.ADDSUB == 2 assert C.DIVMULINT == 4 assert C.SHIFT == 4 assert C.BINOPS == 0b1001 assert C.XOR == 0b0101 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']) def test_error_invalid_syntax_for_cdef(): ffi = FFI() e = pytest.raises(CDefError, ffi.cdef, 'void foo(void) {}') assert str(e.value) == (':1: unexpected : ' 'this construct is valid C but not valid in cdef()') def test_unsigned_int_suffix_for_constant(): ffi = FFI() ffi.cdef("""enum e { bin_0=0b10, bin_1=0b10u, bin_2=0b10U, bin_3=0b10l, bin_4=0b10L, bin_5=0b10ll, bin_6=0b10LL, oct_0=010, oct_1=010u, oct_2=010U, oct_3=010l, oct_4=010L, oct_5=010ll, oct_6=010LL, dec_0=10, dec_1=10u, dec_2=10U, dec_3=10l, dec_4=10L, dec_5=10ll, dec_6=10LL, hex_0=0x10, hex_1=0x10u, hex_2=0x10U, hex_3=0x10l, hex_4=0x10L, hex_5=0x10ll, hex_6=0x10LL,};""") needs_dlopen_none() C = ffi.dlopen(None) for base, expected_result in (('bin', 2), ('oct', 8), ('dec', 10), ('hex', 16)): for index in range(7): assert getattr(C, '{base}_{index}'.format(base=base, index=index)) == expected_result ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_platform.py0000644000175100001770000000153200000000000021216 0ustar00runnerdocker00000000000000import 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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_unicode_literals.py0000644000175100001770000000430500000000000022720 0ustar00runnerdocker00000000000000# # ---------------------------------------------- # WARNING, ALL LITERALS IN THIS FILE ARE UNICODE # ---------------------------------------------- # from __future__ import unicode_literals # # # import sys, math from cffi import FFI from testing.support import is_musl 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' elif is_musl: lib_m = 'c' 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_verify.py0000644000175100001770000025532100000000000020705 0ustar00runnerdocker00000000000000import re import pytest import sys, os, math, weakref from cffi import FFI, VerificationError, VerificationMissing, model, FFIError from testing.support import * from testing.support import extra_compile_args, is_musl # eliminate warning noise from common test modules that are repeatedly re-imported pytestmark = [ pytest.mark.filterwarnings("ignore:reimporting:UserWarning"), #pytest.mark.filterwarnings("ignore:Deprecated:_DeprecatedConfig") ] 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: 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'): pytest.skip("needs GCC") if '-Wno-error=sign-conversion' in extra_compile_args: pytest.skip("gcc 9.2.0 compiler bug exposed by Python 3.12+ prevents compilation with sign-conversion warnings-as-errors") ffi = FFI() ffi.cdef(cdef) pytest.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: pytest.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() X = tp.is_complex_type() I = tp.is_integer_type() assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t')) assert F == (typename in ('float', 'double', 'long double')) assert X == (typename in ('float _Complex', 'double _Complex')) assert I + F + C + X == 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 all_primitive_types[typename] == 'j' or # complex 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 pytest.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: pytest.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(["static %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 pytest.raises(OverflowError, setattr, lib, varname, max+1) pytest.raises(OverflowError, setattr, lib, varname, min-1) def test_var_unsigned_integer_types(): ffi = FFI() lst = all_unsigned_integer_types(ffi) csource = "\n".join(["static %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 pytest.raises(OverflowError, setattr, lib, varname, max+1) pytest.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 pytest.raises(OverflowError, fn, max + 1) pytest.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 pytest.raises(OverflowError, fn, max + 1) pytest.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" pytest.raises(TypeError, lib.foo, b"bar") pytest.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_char16_char32_type(): pytest.skip("XXX test or fully prevent char16_t and char32_t from " "working in ffi.verify() mode") 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; }") pytest.raises(TypeError, lib.foo, ffi.new("short *", 42)) def test_verify_typedefs(): pytest.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: pytest.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 pytest.raises(VerificationError, ffi.verify, "struct foo_s { char x; int y; int *z; };") # pytest.raises(VerificationError, ffi.verify, "struct foo_s { int y; long *z; };") # e = pytest.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 = pytest.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 = pytest.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; ...; }; """) pytest.raises(VerificationMissing, ffi.sizeof, 'struct foo_s') pytest.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x') pytest.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': pytest.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[]') # implicit max length assert len(s.a) == 18 # max length, computed from the size and start offset 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') with pytest.raises(IndexError): s.a[17] def test_struct_array_c99_1(): if sys.platform == 'win32': pytest.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(ffi.typeof(s[0])) == 1 * ffi.sizeof('int') assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') # ^^^ explanation: if you write in C: "char x[5];", then # "sizeof(x)" will evaluate to 5. The behavior above is # a generalization of that to "struct foo_s[len(a)=5] x;" # if you could do that in C. assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) assert ffi.sizeof(s[0]) == 5 * 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': pytest.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]) == 6 * ffi.sizeof('int') assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) assert ffi.sizeof(s[0]) == 6 * 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 with pytest.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() pytest.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 };") pytest.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 };") pytest.raises(VerificationError, ffi.verify, "enum ee { EE1, EE2 };") e = pytest.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 };") pytest.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... };") pytest.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("static int foo(void);\n" "static 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("static 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" "static 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': pytest.skip("XXX fixme: only gives warnings") ffi = FFI() ffi.cdef("int foo(int x, ...);") pytest.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" "static 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; } static int (*foo)(struct foo_s s) = &foo1; """) e = pytest.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; or, it contains anonymous struct/unions. " "Such structs are only supported as argument " "if the function is 'API mode' and non-variadic (i.e. declared " "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking " "a final '...' argument)") 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 pytest.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;") pytest.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 pytest.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'): # pytest.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') #elif platform.machine() == 'mips64' and sys.maxsize > 2**32: # pytest.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 = pytest.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': pytest.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': pytest.skip("nested anonymous struct/union") ffi = FFI() ffi.cdef(""" struct foo_s { struct { int a; char b; }; union { char c, d; }; }; """) pytest.raises(VerificationError, ffi.verify, """ struct foo_s { struct { int a; short b; }; union { char c, d; }; }; """) pytest.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': pytest.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': pytest.skip("_Bool not in MSVC") ffi = FFI() ffi.cdef("struct foo_s { _Bool x; };" "_Bool foo(_Bool); static _Bool (*foop)(_Bool);") lib = ffi.verify(""" struct foo_s { _Bool x; }; int foo(int arg) { return !arg; } _Bool _foofunc(_Bool x) { return !x; } static _Bool (*foop)(_Bool) = _foofunc; """) p = ffi.new("struct foo_s *") p.x = 1 assert p.x is True with pytest.raises(OverflowError): p.x = -1 with pytest.raises(TypeError): p.x = 0.0 assert lib.foop(1) is False assert lib.foop(True) is False assert lib.foop(0) is True pytest.raises(OverflowError, lib.foop, 42) pytest.raises(TypeError, lib.foop, 0.0) assert lib.foo(1) is False assert lib.foo(True) is False assert lib.foo(0) is True pytest.raises(OverflowError, lib.foo, 42) pytest.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 # pytest.raises(TypeError, ffi.cast, "_Bool", []) def test_bool_on_long_double(): if sys.platform == 'win32': pytest.skip("_Bool not in MSVC") f = 1E-250 if f == 0.0 or f*f != 0.0: pytest.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): pytest.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 pytest.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 *") with pytest.raises(TypeError): p.x = 0.0 assert lib.foo(42) == 0 assert lib.foo(0) == 1 pytest.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 pytest.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 pytest.raises(TypeError, lib.sum_coord, res2) def test_callback_in_thread(): if sys.platform == 'win32': pytest.skip("pthread only") import os, subprocess from cffi import _imp_emulation as 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') or is_musl: pytest.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); extern 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("extern 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 = pytest.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(""" static 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("""extern 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("""extern const int a[];""") lib = ffi.verify("""const int a[5];""") assert repr(ffi.typeof(lib.a)) == "" def _test_various_calls(force_libffi): cdef_source = """ extern int xvalue; extern long long ivalue, rvalue; extern float fvalue; extern double dvalue; extern 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(): # NOTE: on some platforms, to work correctly, this test needs to be # compiled with -pthread. Otherwise, the accesses to errno done from f() # are compiled by assuming this small library won't be used from multiple # threads, which is wrong. If you see failures _and_ if you pass your # own CFLAGS environment variable, please make sure "-pthread" is in it. 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': pytest.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("extern 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("extern 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 = pytest.raises(NotImplementedError, fooptr, ffi.new("Data *")) assert str(e.value) == ( "ctype 'Data' not supported as argument by libffi. Unions are only " "supported as argument if the function is 'API mode' and " "non-variadic (i.e. declared inside ffibuilder.cdef()+" "ffibuilder.set_source() and not taking a final '...' argument)") e = pytest.raises(NotImplementedError, bazptr) assert str(e.value) == ( "ctype 'Data' not supported as return value by libffi. Unions are " "only supported as return value if the function is 'API mode' and " "non-variadic (i.e. declared inside ffibuilder.cdef()+" "ffibuilder.set_source() and not taking a final '...' argument)") e = pytest.raises(NotImplementedError, barptr) assert str(e.value) == ( "ctype 'MyStr' not supported as return value. It is a struct with " "bit fields, which libffi does not support. Such structs are only " "supported as return value if the function is 'API mode' and non-" "variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." "set_source() and not taking a final '...' argument)") 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': pytest.skip("win32-only test") ffi = FFI() e = pytest.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 = pytest.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 = pytest.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 = pytest.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 double ... t1;") e = pytest.raises(VerificationError, ffi.verify, "") assert str(e.value) == ("feature not supported with ffi.verify(), but only " "with ffi.set_source(): 'typedef float... 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)) pytest.raises(TypeError, lib.call1, cb2) pytest.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: pytest.raises(TypeError, lib.call1, lib.cb2) pytest.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) def _only_test_on_linux_intel(): if not sys.platform.startswith('linux'): pytest.skip('only running the memory-intensive test on Linux') import platform machine = platform.machine() if 'x86' not in machine and 'x64' not in machine: pytest.skip('only running the memory-intensive test on x86/x64') def test_ffi_gc_size_arg(): # with PyPy's GC, these calls to ffi.gc() would rapidly consume # 40 GB of RAM without the third argument _only_test_on_linux_intel() ffi = FFI() ffi.cdef("void *malloc(size_t); void free(void *);") lib = ffi.verify(r""" #include """) for i in range(2000): p = lib.malloc(20*1024*1024) # 20 MB p1 = ffi.cast("char *", p) for j in range(0, 20*1024*1024, 4096): p1[j] = b'!' p = ffi.gc(p, lib.free, 20*1024*1024) del p def test_ffi_gc_size_arg_2(): # a variant of the above: this "attack" works on cpython's cyclic gc too # and I found no obvious way to prevent that. So for now, this test # is skipped on CPython, where it eats all the memory. if '__pypy__' not in sys.builtin_module_names: pytest.skip("find a way to tweak the cyclic GC of CPython") _only_test_on_linux_intel() ffi = FFI() ffi.cdef("void *malloc(size_t); void free(void *);") lib = ffi.verify(r""" #include """) class X(object): pass for i in range(2000): p = lib.malloc(50*1024*1024) # 50 MB p1 = ffi.cast("char *", p) for j in range(0, 50*1024*1024, 4096): p1[j] = b'!' p = ffi.gc(p, lib.free, 50*1024*1024) x = X() x.p = p x.cyclic = x del p, x def test_ffi_new_with_cycles(): # still another variant, with ffi.new() if '__pypy__' not in sys.builtin_module_names: pytest.skip("find a way to tweak the cyclic GC of CPython") ffi = FFI() ffi.cdef("") lib = ffi.verify("") class X(object): pass for i in range(2000): p = ffi.new("char[]", 50*1024*1024) # 50 MB for j in range(0, 50*1024*1024, 4096): p[j] = b'!' x = X() x.p = p x.cyclic = x del p, x def test_arithmetic_in_cdef(): for a in [0, 11, 15]: ffi = FFI() ffi.cdef(""" enum FOO { DIVNN = ((-?) / (-3)), DIVNP = ((-?) / (+3)), DIVPN = ((+?) / (-3)), MODNN = ((-?) % (-3)), MODNP = ((-?) % (+3)), MODPN = ((+?) % (-3)), }; """.replace('?', str(a))) lib = ffi.verify(""" enum FOO { DIVNN = ((-?) / (-3)), DIVNP = ((-?) / (+3)), DIVPN = ((+?) / (-3)), MODNN = ((-?) % (-3)), MODNP = ((-?) % (+3)), MODPN = ((+?) % (-3)), }; """.replace('?', str(a))) # the verify() crashes if the values in the enum are different from # the values we computed ourselves from the cdef() def test_passing_large_list(): ffi = FFI() ffi.cdef("""void passing_large_list(long[]);""") lib = ffi.verify(""" static void passing_large_list(long a[]) { } """) arg = list(range(20000000)) lib.passing_large_list(arg) # assert did not segfault ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_verify2.py0000644000175100001770000000075000000000000020761 0ustar00runnerdocker00000000000000import pytest from .test_verify import * # eliminate warning noise from common test modules that are repeatedly re-imported pytestmark = pytest.mark.filterwarnings("ignore:reimporting:UserWarning") # 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_version.py0000644000175100001770000000352300000000000021061 0ustar00runnerdocker00000000000000import os, sys import pytest import cffi, _cffi_backend from pathlib import Path def setup_module(mod): if '_cffi_backend' in sys.builtin_module_names: pytest.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', '+') version_info = version_info.replace('.rc', 'rc') assert v == version_info #v = BACKEND_VERSIONS.get(v, v) assert v == _cffi_backend.__version__ def test_doc_version(): cffi_root = Path(os.path.dirname(__file__)).parent.parent p = cffi_root / 'doc/source/conf.py' content = open(p).read() # v = cffi.__version__ assert ("version = '%s'\n" % v[:4]) in content assert ("release = '%s'\n" % v) in content def test_setup_version(): cffi_root = Path(os.path.dirname(__file__)).parent.parent p = cffi_root / 'setup.py' content = open(p).read() # v = cffi.__version__.replace('+', '') assert ("version='%s'" % v) in content def test_c_version(): cffi_root = Path(os.path.dirname(__file__)).parent.parent v = cffi.__version__ p = cffi_root / 'src/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(): cffi_root = Path(os.path.dirname(__file__)).parent.parent v = cffi.__version__ p = cffi_root / 'src/cffi/_embedding.h' content = open(p).read() assert ('cffi version: %s"' % (v,)) in content ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_vgen.py0000644000175100001770000000051000000000000020324 0ustar00runnerdocker00000000000000import 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_vgen2.py0000644000175100001770000000065100000000000020414 0ustar00runnerdocker00000000000000import 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_zdistutils.py0000644000175100001770000002703300000000000021614 0ustar00runnerdocker00000000000000import sys, os, math, shutil import pytest 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 from testing.support import load_dynamic 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 = 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__[:10] + '_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 = 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'): pytest.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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi0/test_zintegration.py0000644000175100001770000001704100000000000022111 0ustar00runnerdocker00000000000000import py, os, sys, shutil import subprocess import textwrap from testing.udir import udir import pytest if sys.platform == 'win32': pytestmark = pytest.mark.skip('snippets do not run on win32') if sys.version_info < (2, 7): pytestmark = pytest.mark.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: # FUTURE: we should probably update this to use venv for at least more modern Pythons, and # install setuptools/pip/etc explicitly for the tests that require them (as venv has stopped including # setuptools and wheel by default for newer versions). subprocess.check_call(['virtualenv', #'--never-download', <= could be added, but causes failures # in random cases on random machines '-p', os.path.abspath(sys.executable), str(tmpdir)]) # Python 3.12 venv/virtualenv no longer include setuptools and wheel by default, which # breaks a number of these tests; ensure it's always present for 3.12+ if sys.version_info >= (3, 12): subprocess.check_call([ os.path.join(tmpdir, 'bin/python'), '-m', 'pip', 'install', 'setuptools', 'wheel', '--upgrade' ]) except OSError as e: pytest.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(textwrap.dedent(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) # there's a setuptools/easy_install bug that causes this to fail when the build/install occur together and # we're in the same directory with the build (it tries to look up dependencies for itself on PyPI); # subsequent runs will succeed because this test doesn't properly clean up the build- use pip for now. subprocess.check_call((vp, '-m', 'pip', '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" ''') def test_set_py_limited_api(self): from cffi.setuptools_ext import _set_py_limited_api try: import setuptools except ImportError as e: pytest.skip(str(e)) orig_version = setuptools.__version__ expecting_limited_api = not hasattr(sys, 'gettotalrefcount') try: setuptools.__version__ = '26.0.0' from setuptools import Extension kwds = _set_py_limited_api(Extension, {}) assert kwds.get('py_limited_api', False) == expecting_limited_api setuptools.__version__ = '25.0' kwds = _set_py_limited_api(Extension, {}) assert kwds.get('py_limited_api', False) == False setuptools.__version__ = 'development' kwds = _set_py_limited_api(Extension, {}) assert kwds.get('py_limited_api', False) == expecting_limited_api finally: setuptools.__version__ = orig_version ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1597679 cffi-1.16.0/testing/cffi1/0000755000175100001770000000000000000000000015761 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/__init__.py0000644000175100001770000000000000000000000020060 0ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_cffi_binary.py0000644000175100001770000000203300000000000021643 0ustar00runnerdocker00000000000000import sys, os import pytest import _cffi_backend from testing.support import is_musl def test_no_unknown_exported_symbols(): if not hasattr(_cffi_backend, '__file__'): pytest.skip("_cffi_backend module is built-in") if not sys.platform.startswith('linux') or is_musl: pytest.skip("linux-only") g = os.popen("objdump -T '%s'" % _cffi_backend.__file__, 'r') for line in g: if not line.startswith('0'): continue if line[line.find(' ') + 1] == 'l': continue if '*UND*' in line: continue name = line.split()[-1] if name.startswith('_') or name.startswith('.'): continue # a statically-linked libffi will always appear here without header hackage, ignore it if it's internal if name.startswith('ffi_') and 'Base' in line: continue if name not in ('init_cffi_backend', 'PyInit__cffi_backend', 'cffistatic_ffi_call'): raise Exception("Unexpected exported name %r" % (name,)) g.close() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_commontypes.py0000644000175100001770000000163700000000000021756 0ustar00runnerdocker00000000000000import os, cffi, re import pytest import _cffi_backend def getlines(): try: f = open(os.path.join(os.path.dirname(cffi.__file__), '..', 'c', 'commontypes.c')) except IOError: pytest.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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_dlopen.py0000644000175100001770000001574100000000000020663 0ustar00runnerdocker00000000000000import pytest 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; extern 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 = pytest.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 = pytest.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') pytest.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') pytest.raises(OverflowError, make_py_source, ffi, 'test_array_overflow', str(target)) def test_global_var(): ffi = FFI() ffi.cdef("extern 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'),), ) """ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_dlopen_unicode_literals.py0000644000175100001770000000032600000000000024261 0ustar00runnerdocker00000000000000import 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(compile(s, filename='test_dlopen.py', mode='exec')) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_ffi_obj.py0000644000175100001770000004200200000000000020766 0ustar00runnerdocker00000000000000import sys import pytest 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(): pytest.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 pytest.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() with pytest.raises(AttributeError): ffi.no_such_name with pytest.raises(AttributeError): ffi.no_such_name = 42 with pytest.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 pytest.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" # pytest.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 pytest.raises(ffi.error, ffi.cast, "int(int)", 0) def test_ffi_invalid_type(): ffi = _cffi1_backend.FFI() e = pytest.raises(ffi.error, ffi.cast, "", 0) assert str(e.value) == ("identifier expected\n" "\n" "^") e = pytest.raises(ffi.error, ffi.cast, "struct struct", 0) assert str(e.value) == ("struct or union name expected\n" "struct struct\n" " ^") e = pytest.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 = pytest.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 = pytest.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' assert type(ffi.buffer(a)) is ffi.buffer def test_ffi_from_buffer(): import array ffi = _cffi1_backend.FFI() a = array.array('H', [10000, 20000, 30000, 40000]) c = ffi.from_buffer(a) assert ffi.typeof(c) is ffi.typeof("char[]") assert len(c) == 8 ffi.cast("unsigned short *", c)[1] += 500 assert list(a) == [10000, 20500, 30000, 40000] pytest.raises(TypeError, ffi.from_buffer, a, True) assert c == ffi.from_buffer("char[]", a, True) assert c == ffi.from_buffer(a, require_writable=True) # c = ffi.from_buffer("unsigned short[]", a) assert len(c) == 4 assert c[1] == 20500 # c = ffi.from_buffer("unsigned short[2][2]", a) assert len(c) == 2 assert len(c[0]) == 2 assert c[0][1] == 20500 # p = ffi.from_buffer(b"abcd") assert p[2] == b"c" # assert p == ffi.from_buffer(b"abcd", require_writable=False) pytest.raises((TypeError, BufferError), ffi.from_buffer, "char[]", b"abcd", True) pytest.raises((TypeError, BufferError), ffi.from_buffer, b"abcd", require_writable=True) 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] pytest.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": pytest.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(400): p1 = alloc1("int[10]") p2 = alloc2("int[]", 10 + retry * 13) combination = 0 for i in range(10): assert p1[i] == 0 combination |= p2[i] p1[i] = -42 p2[i] = -43 if combination != 0: break 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] or seen == [40, 40, raw2, raw1]) 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() pytest.raises(TypeError, ffi.new_allocator, free=lambda x: None) # def myalloc2(size): raise LookupError alloc2 = ffi.new_allocator(myalloc2) pytest.raises(LookupError, alloc2, "int[5]") # def myalloc3(size): return 42 alloc3 = ffi.new_allocator(myalloc3) e = pytest.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 = pytest.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) pytest.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): pytest.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(): pytest.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 def test_unpack(): ffi = _cffi1_backend.FFI() p = ffi.new("char[]", b"abc\x00def") assert ffi.unpack(p+1, 7) == b"bc\x00def\x00" p = ffi.new("int[]", [-123456789]) assert ffi.unpack(p, 1) == [-123456789] def test_negative_array_size(): ffi = _cffi1_backend.FFI() pytest.raises(ffi.error, ffi.cast, "int[-5]", 0) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_function_args.py0000644000175100001770000001701100000000000022233 0ustar00runnerdocker00000000000000import pytest, sys try: # comment out the following line to run this test. # the latest on x86-64 linux: https://github.com/libffi/libffi/issues/574 if sys.platform != 'win32': raise ImportError("this test is skipped because it keeps finding " "failures in libffi, instead of cffi") from hypothesis import given, settings, example from hypothesis import strategies as st except ImportError as e: e1 = e def test_types(): pytest.skip(str(e1)) else: from cffi import FFI import sys, random from .test_recompiler import verify ALL_PRIMITIVES = [ 'unsigned char', 'short', 'int', 'long', 'long long', 'float', 'double', #'long double', --- on x86 it can give libffi crashes ] def _make_struct(s): return st.lists(s, min_size=1) types = st.one_of(st.sampled_from(ALL_PRIMITIVES), st.lists(st.sampled_from(ALL_PRIMITIVES), min_size=1)) # NB. 'types' could be st.recursive instead, but it doesn't # really seem useful def draw_primitive(ffi, typename): value = random.random() * 2**40 if typename != 'long double': return ffi.cast(typename, value) else: return value TEST_RUN_COUNTER = 0 @given(st.lists(types), types) @settings(max_examples=100, deadline=5000) # 5000ms def test_types(tp_args, tp_result): global TEST_RUN_COUNTER print(tp_args, tp_result) cdefs = [] structs = {} def build_type(tp): if type(tp) is list: field_types = [build_type(tp1) for tp1 in tp] fields = ['%s f%d;' % (ftp, j) for (j, ftp) in enumerate(field_types)] fields = '\n '.join(fields) name = 's%d' % len(cdefs) cdefs.append("typedef struct {\n %s\n} %s;" % (fields, name)) structs[name] = field_types return name else: return tp args = [build_type(tp) for tp in tp_args] result = build_type(tp_result) TEST_RUN_COUNTER += 1 signature = "%s testfargs(%s)" % (result, ', '.join(['%s a%d' % (arg, i) for (i, arg) in enumerate(args)]) or 'void') source = list(cdefs) cdefs.append("%s;" % signature) cdefs.append("extern %s testfargs_result;" % result) for i, arg in enumerate(args): cdefs.append("extern %s testfargs_arg%d;" % (arg, i)) source.append("%s testfargs_result;" % result) for i, arg in enumerate(args): source.append("%s testfargs_arg%d;" % (arg, i)) source.append(signature) source.append("{") for i, arg in enumerate(args): source.append(" testfargs_arg%d = a%d;" % (i, i)) source.append(" return testfargs_result;") source.append("}") typedef_line = "typedef %s;" % (signature.replace('testfargs', '(*mycallback_t)'),) assert signature.endswith(')') sig_callback = "%s testfcallback(mycallback_t callback)" % result cdefs.append(typedef_line) cdefs.append("%s;" % sig_callback) source.append(typedef_line) source.append(sig_callback) source.append("{") source.append(" return callback(%s);" % ', '.join(["testfargs_arg%d" % i for i in range(len(args))])) source.append("}") ffi = FFI() ffi.cdef("\n".join(cdefs)) lib = verify(ffi, 'test_function_args_%d' % TEST_RUN_COUNTER, "\n".join(source), no_cpp=True) # when getting segfaults, enable this: if False: from testing.udir import udir import subprocess f = open(str(udir.join('run1.py')), 'w') f.write('import sys; sys.path = %r\n' % (sys.path,)) f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % TEST_RUN_COUNTER) for i in range(len(args)): f.write('a%d = ffi.new("%s *")\n' % (i, args[i])) aliststr = ', '.join(['a%d[0]' % i for i in range(len(args))]) f.write('lib.testfargs(%s)\n' % aliststr) f.write('ffi.addressof(lib, "testfargs")(%s)\n' % aliststr) f.close() print("checking for segfault for direct call...") rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) assert rc == 0, rc def make_arg(tp): if tp in structs: return [make_arg(tp1) for tp1 in structs[tp]] else: return draw_primitive(ffi, tp) passed_args = [make_arg(arg) for arg in args] returned_value = make_arg(result) def write(p, v): if type(v) is list: for i, v1 in enumerate(v): write(ffi.addressof(p, 'f%d' % i), v1) else: p[0] = v write(ffi.addressof(lib, 'testfargs_result'), returned_value) ## CALL forcing libffi print("CALL forcing libffi") received_return = ffi.addressof(lib, 'testfargs')(*passed_args) ## _tp_long_double = ffi.typeof("long double") def check(p, v): if type(v) is list: for i, v1 in enumerate(v): check(ffi.addressof(p, 'f%d' % i), v1) else: if ffi.typeof(p).item is _tp_long_double: assert ffi.cast("double", p[0]) == v else: assert p[0] == v for i, arg in enumerate(passed_args): check(ffi.addressof(lib, 'testfargs_arg%d' % i), arg) ret = ffi.new(result + "*", received_return) check(ret, returned_value) ## CALLBACK def expand(value): if isinstance(value, ffi.CData): t = ffi.typeof(value) if t is _tp_long_double: return float(ffi.cast("double", value)) return [expand(getattr(value, 'f%d' % i)) for i in range(len(t.fields))] else: return value # when getting segfaults, enable this: if False: from testing.udir import udir import subprocess f = open(str(udir.join('run1.py')), 'w') f.write('import sys; sys.path = %r\n' % (sys.path,)) f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % TEST_RUN_COUNTER) f.write('def callback(*args): return ffi.new("%s *")[0]\n' % result) f.write('fptr = ffi.callback("%s(%s)", callback)\n' % (result, ','.join(args))) f.write('print(lib.testfcallback(fptr))\n') f.close() print("checking for segfault for callback...") rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) assert rc == 0, rc seen_args = [] def callback(*args): seen_args.append([expand(arg) for arg in args]) return returned_value fptr = ffi.callback("%s(%s)" % (result, ','.join(args)), callback) print("CALL with callback") received_return = lib.testfcallback(fptr) assert len(seen_args) == 1 assert passed_args == seen_args[0] ret = ffi.new(result + "*", received_return) check(ret, returned_value) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_new_ffi_1.py0000644000175100001770000020223000000000000021226 0ustar00runnerdocker00000000000000import pytest import platform 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 = 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 assert bool(p) is bool(min) 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) c_decl_ptr = '%s *' % c_decl pytest.raises(OverflowError, ffi.new, c_decl_ptr, min - 1) pytest.raises(OverflowError, ffi.new, c_decl_ptr, max + 1) pytest.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1)) pytest.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 = pytest.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 with pytest.raises(IndexError): p[10] with pytest.raises(IndexError): p[10] = 44 with pytest.raises(IndexError): p[-1] with pytest.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 with pytest.raises(IndexError): p[10] # pytest.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 with pytest.raises(IndexError): p[2] assert repr(p) == "" % (2*SIZE_OF_INT) # p = ffi.new("int[]", 0) with pytest.raises(IndexError): p[0] pytest.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 = pytest.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: pytest.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 not bool(ffi.cast("wchar_t", 0)) assert bool(ffi.cast("wchar_t", 1)) assert bool(ffi.cast("wchar_t", 65535)) if SIZE_OF_WCHAR > 2: assert bool(ffi.cast("wchar_t", 65536)) pytest.raises(TypeError, ffi.new, "wchar_t*", 32) pytest.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 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'] pytest.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 with pytest.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 pytest.raises(TypeError, int, p) pytest.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 with pytest.raises(OverflowError): s.b = 32768 # s = ffi.new("struct simple*", [-2, -3]) assert s.a == -2 assert s.b == -3 assert s.c == 0 with pytest.raises((AttributeError, TypeError)): del s.a assert repr(s) == "" % ( SIZE_OF_INT + 2 * SIZE_OF_SHORT) # pytest.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 pytest.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 with pytest.raises(OverflowError): s[0].b = -32769 with pytest.raises(IndexError): s[1] def test_struct_opaque(self): pytest.raises(ffi.error, ffi.new, "struct baz*") # should 'ffi.new("struct baz **") work? it used to, but it was # not particularly useful... pytest.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 with pytest.raises(OverflowError): u.b = 32768 # u = ffi.new("union simple_u*", [-2]) assert u.a == -2 with pytest.raises((AttributeError, TypeError)): del u.a assert repr(u) == "" % ( SIZE_OF_INT,) def test_union_opaque(self): pytest.raises(ffi.error, ffi.new, "union baz*") # should 'ffi.new("union baz **") work? it used to, but it was # not particularly useful... pytest.raises(ffi.error, ffi.new, "union baz**") def test_union_initializer(self): pytest.raises(TypeError, ffi.new, "union init_u*", b'A') pytest.raises(TypeError, ffi.new, "union init_u*", 5) pytest.raises(ValueError, ffi.new, "union init_u*", [b'A', 5]) u = ffi.new("union init_u*", [b'A']) assert u.a == b'A' pytest.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"" pytest.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" with pytest.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" with pytest.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" with pytest.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): pytest.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] with pytest.raises(TypeError): vp[0] pytest.raises(TypeError, ffi.new, "short **", a) # s = ffi.new("struct voidp *") s.p = a # works s.q = a # works with pytest.raises(TypeError): s.r = a # fails b = ffi.cast("int *", a) s.p = b # works s.q = b # works with pytest.raises(TypeError): s.r = b # fails def test_functionptr_simple(self): pytest.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) with pytest.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 with pytest.raises(TypeError): s.e = 'B3' with pytest.raises(TypeError): s.e = '2' with pytest.raises(TypeError): s.e = '#2' with pytest.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]") with pytest.raises(AttributeError): s.b with pytest.raises(AttributeError): s.b = 412 s[0].b = 412 assert s[0].b == 412 with pytest.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"] # pytest.raises(TypeError, iter, ffi.cast("char *", a)) pytest.raises(TypeError, list, ffi.cast("char *", a)) pytest.raises(TypeError, iter, ffi.new("int *")) pytest.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 pytest.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 with pytest.raises(OverflowError): s.a = 512 with pytest.raises(OverflowError): s[0].a = 512 assert s.a == 511 s.a = -512 with pytest.raises(OverflowError): s.a = -513 with pytest.raises(OverflowError): s[0].a = -513 assert s.a == -512 s.c = 3 assert s.c == 3 with pytest.raises(OverflowError): s.c = 4 with pytest.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": pytest.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_integer_comparison(self): x = ffi.cast("int", 123) y = ffi.cast("int", 456) assert x < y # z = ffi.cast("double", 78.9) assert x > z assert y > z def test_ffi_buffer_ptr(self): a = ffi.new("short *", 100) try: b = ffi.buffer(a) except NotImplementedError as e: pytest.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: pytest.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: pytest.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: pytest.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: pytest.skip(str(e)) f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) f.seek(0) assert f.read() == arraytostring(array.array('i', range(1000))) 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: pytest.skip(str(e)) f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) f.seek(0) assert f.read() == arraytostring(array.array('i', range(1000))) 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("= 4.0 pytest.skip("not available on pypy", allow_module_level=True) except TypeError: # older pytest pytest.skip("not available on pypy") cffi_dir = str(Path(os.path.dirname(__file__)).parent.parent / "src/cffi") r_macro = re.compile(r"#define \w+[(][^\n]*|#include [^\n]*") r_define = re.compile(r"(#define \w+) [^\n]*") r_ifdefs = re.compile(r"(#ifdef |#endif)[^\n]*") header = open(os.path.join(cffi_dir, 'parse_c_type.h')).read() header = r_macro.sub(r"", header) header = r_define.sub(r"\1 ...", header) header = r_ifdefs.sub(r"", header) ffi = cffi.FFI() ffi.cdef(header) lib = ffi.verify( open(os.path.join(cffi_dir, '..', 'c', 'parse_c_type.c')).read() + """ static const char *get_common_type(const char *search, size_t search_len) { return NULL; } """, include_dirs=[cffi_dir]) class ParseError(Exception): pass struct_names = ["bar_s", "foo", "foo_", "foo_s", "foo_s1", "foo_s12"] assert struct_names == sorted(struct_names) enum_names = ["ebar_s", "efoo", "efoo_", "efoo_s", "efoo_s1", "efoo_s12"] assert enum_names == sorted(enum_names) identifier_names = ["id", "id0", "id05", "id05b", "tail"] assert identifier_names == sorted(identifier_names) global_names = ["FIVE", "NEG", "ZERO"] assert global_names == sorted(global_names) ctx = ffi.new("struct _cffi_type_context_s *") c_struct_names = [ffi.new("char[]", _n.encode('ascii')) for _n in struct_names] ctx_structs = ffi.new("struct _cffi_struct_union_s[]", len(struct_names)) for _i in range(len(struct_names)): ctx_structs[_i].name = c_struct_names[_i] ctx_structs[3].flags = lib._CFFI_F_UNION ctx.struct_unions = ctx_structs ctx.num_struct_unions = len(struct_names) c_enum_names = [ffi.new("char[]", _n.encode('ascii')) for _n in enum_names] ctx_enums = ffi.new("struct _cffi_enum_s[]", len(enum_names)) for _i in range(len(enum_names)): ctx_enums[_i].name = c_enum_names[_i] ctx.enums = ctx_enums ctx.num_enums = len(enum_names) c_identifier_names = [ffi.new("char[]", _n.encode('ascii')) for _n in identifier_names] ctx_identifiers = ffi.new("struct _cffi_typename_s[]", len(identifier_names)) for _i in range(len(identifier_names)): ctx_identifiers[_i].name = c_identifier_names[_i] ctx_identifiers[_i].type_index = 100 + _i ctx.typenames = ctx_identifiers ctx.num_typenames = len(identifier_names) @ffi.callback("int(unsigned long long *)") def fetch_constant_five(p): p[0] = 5 return 0 @ffi.callback("int(unsigned long long *)") def fetch_constant_zero(p): p[0] = 0 return 1 @ffi.callback("int(unsigned long long *)") def fetch_constant_neg(p): p[0] = 123321 return 1 ctx_globals = ffi.new("struct _cffi_global_s[]", len(global_names)) c_glob_names = [ffi.new("char[]", _n.encode('ascii')) for _n in global_names] for _i, _fn in enumerate([fetch_constant_five, fetch_constant_neg, fetch_constant_zero]): ctx_globals[_i].name = c_glob_names[_i] ctx_globals[_i].address = _fn ctx_globals[_i].type_op = ffi.cast("_cffi_opcode_t", cffi_opcode.OP_CONSTANT_INT if _i != 1 else cffi_opcode.OP_ENUM) ctx.globals = ctx_globals ctx.num_globals = len(global_names) def parse(input): out = ffi.new("_cffi_opcode_t[]", 100) info = ffi.new("struct _cffi_parse_info_s *") info.ctx = ctx info.output = out info.output_size = len(out) for j in range(len(out)): out[j] = ffi.cast("void *", -424242) res = lib.parse_c_type(info, input.encode('ascii')) if res < 0: raise ParseError(ffi.string(info.error_message).decode('ascii'), info.error_location) assert 0 <= res < len(out) result = [] for j in range(len(out)): if out[j] == ffi.cast("void *", -424242): assert res < j break i = int(ffi.cast("intptr_t", out[j])) if j == res: result.append('->') 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 = pytest.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), (" float _Complex", lib._CFFI_PRIM_FLOATCOMPLEX), ("double _Complex ", lib._CFFI_PRIM_DOUBLECOMPLEX), ]: 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) # parse_error("_Complex", "identifier expected", 0) parse_error("int _Complex", "_Complex type combination unsupported", 4) parse_error("long double _Complex", "_Complex type combination unsupported", 12) 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_pkgconfig.py0000644000175100001770000000620400000000000021343 0ustar00runnerdocker00000000000000import sys import subprocess import pytest import cffi.pkgconfig as pkgconfig from cffi import PkgConfigError def mock_call(libname, flag): assert libname=="foobarbaz" flags = { "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", } return flags[flag] def test_merge_flags(): d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} pkgconfig.merge_flags(d1, d2) assert d1 == { "ham": [1, 2, 3], "spam" : ["a", "b", "c", "spam", "spam", "spam"], "bar" : ["b", "a", "z"], "foo" : []} def test_pkgconfig(): assert pkgconfig.flags_from_pkgconfig([]) == {} saved = pkgconfig.call try: pkgconfig.call = mock_call flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) finally: pkgconfig.call = saved assert flags == { 'include_dirs': ['/usr/include/python3.6m'], 'library_dirs': ['/usr/lib64'], 'libraries': ['python3.6'], 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], 'extra_compile_args': ['-O42'], 'extra_link_args': ['-shared'] } class mock_subprocess: PIPE = Ellipsis class Popen: def __init__(self, cmd, stdout, stderr): if mock_subprocess.RESULT is None: raise OSError("oops can't run") assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] def communicate(self): bout, berr, rc = mock_subprocess.RESULT self.returncode = rc return bout, berr def test_call(): saved = pkgconfig.subprocess try: pkgconfig.subprocess = mock_subprocess mock_subprocess.RESULT = None e = pytest.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") assert str(e.value) == "cannot run pkg-config: oops can't run" mock_subprocess.RESULT = b"", "Foo error!\n", 1 e = pytest.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") assert str(e.value) == "Foo error!" mock_subprocess.RESULT = b"abc\\def\n", "", 0 e = pytest.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") assert str(e.value).startswith("pkg-config --cflags libfoo returned an " "unsupported backslash-escaped output:") mock_subprocess.RESULT = b"abc def\n", "", 0 result = pkgconfig.call("libfoo", "--cflags") assert result == "abc def\n" mock_subprocess.RESULT = b"abc def\n", "", 0 result = pkgconfig.call("libfoo", "--cflags") assert result == "abc def\n" if sys.version_info >= (3,): mock_subprocess.RESULT = b"\xff\n", "", 0 e = pytest.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags", encoding="utf-8") assert str(e.value) == ( "pkg-config --cflags libfoo returned bytes that cannot be " "decoded with encoding 'utf-8':\nb'\\xff\\n'") finally: pkgconfig.subprocess = saved ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_re_python.py0000644000175100001770000002273200000000000021407 0ustar00runnerdocker00000000000000import sys, os import pytest from cffi import FFI from cffi import recompiler, ffiplatform, VerificationMissing from testing.udir import udir from testing.support import u, is_musl 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) # test with a non-ascii char ofn, oext = os.path.splitext(outputfilename) if sys.platform == "win32": unicode_name = ofn + (u+'\u03be') + oext else: unicode_name = ofn + (u+'\xe9') + oext try: unicode_name.encode(sys.getfilesystemencoding()) except UnicodeEncodeError: unicode_name = None if unicode_name is not None: print(repr(outputfilename) + ' ==> ' + repr(unicode_name)) os.rename(outputfilename, unicode_name) outputfilename = unicode_name 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, ...); extern int globalvar42; const int globalconst42; const char *const globalconsthello; int no_such_function(int); extern 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 *); struct with_union { union { int a; char b; }; }; union with_struct { struct { int a; char b; }; }; struct with_struct_with_union { struct { union { int x; }; } cp; }; struct NVGcolor { union { float rgba[4]; struct { float r,g,b,a; }; }; }; typedef struct selfref { struct selfref *next; } *selfref_ptr_t; """) 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() if name is None: pytest.skip("dlopen(None) cannot work on Windows with Python 3") 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) if type(extmod) is not str: # unicode, on python 2 str_extmod = extmod.encode('utf-8') else: str_extmod = extmod e = pytest.raises(ffi.error, getattr, lib, 'add42') assert str(e.value) == ( "library '%s' has been closed" % (str_extmod,)) ffi.dlclose(lib) # does not raise 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) pytest.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 pytest.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" pytest.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 = pytest.raises(ffi.error, getattr, lib, 'no_such_function') assert str(e.value).startswith( "symbol 'no_such_function' not found in library '") e = pytest.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 = pytest.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) pytest.raises(VerificationMissing, ffi.emit_python_code, str(tmpdir.join('test_partial_enum.py'))) def test_anonymous_union_inside_struct(): # based on issue #357 from re_python_pysrc import ffi INT = ffi.sizeof("int") assert ffi.offsetof("struct with_union", "a") == 0 assert ffi.offsetof("struct with_union", "b") == 0 assert ffi.sizeof("struct with_union") == INT # assert ffi.offsetof("union with_struct", "a") == 0 assert ffi.offsetof("union with_struct", "b") == INT assert ffi.sizeof("union with_struct") >= INT + 1 # assert ffi.sizeof("struct with_struct_with_union") == INT p = ffi.new("struct with_struct_with_union *") assert p.cp.x == 0 # FLOAT = ffi.sizeof("float") assert ffi.sizeof("struct NVGcolor") == FLOAT * 4 assert ffi.offsetof("struct NVGcolor", "rgba") == 0 assert ffi.offsetof("struct NVGcolor", "r") == 0 assert ffi.offsetof("struct NVGcolor", "g") == FLOAT assert ffi.offsetof("struct NVGcolor", "b") == FLOAT * 2 assert ffi.offsetof("struct NVGcolor", "a") == FLOAT * 3 def test_selfref(): # based on issue #429 from re_python_pysrc import ffi ffi.new("selfref_ptr_t") def test_dlopen_handle(): import _cffi_backend from re_python_pysrc import ffi if sys.platform == 'win32' or is_musl: pytest.skip("uses 'dl' explicitly") ffi1 = FFI() ffi1.cdef("""void *dlopen(const char *filename, int flags); int dlclose(void *handle);""") lib1 = ffi1.dlopen('dl') handle = lib1.dlopen(extmod.encode(sys.getfilesystemencoding()), _cffi_backend.RTLD_LAZY) assert ffi1.typeof(handle) == ffi1.typeof("void *") assert handle lib = ffi.dlopen(handle) assert lib.add42(-10) == 32 assert type(lib.add42) is _cffi_backend.FFI.CData err = lib1.dlclose(handle) assert err == 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_realize_c_type.py0000644000175100001770000000467400000000000022403 0ustar00runnerdocker00000000000000import sys import pytest 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 = pytest.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, ...)") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_recompiler.py0000644000175100001770000026245100000000000021545 0ustar00runnerdocker00000000000000 import sys, os import pytest from cffi import FFI, VerificationError, FFIError, CDefError from cffi import recompiler from testing.udir import udir from testing.support import u, long from testing.support import FdWriteCapture, StdErrCapture, _verify try: import importlib except ImportError: importlib = None 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): no_cpp = kwds.pop('no_cpp', False) ignore_warnings = kwds.pop('ignore_warnings', False) kwds.setdefault('undef_macros', ['NDEBUG']) module_name = '_CFFI_' + module_name ffi.set_source(module_name, source) if not os.environ.get('NO_CPP') and not no_cpp: # test the .cpp mode too kwds.setdefault('source_extension', '.cpp') source = 'extern "C" {\n%s\n}' % (source,) elif sys.platform != 'win32' and not ignore_warnings: # add '-Werror' to the existing 'extra_compile_args' flags from testing.support import extra_compile_args kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) + extra_compile_args) if sys.platform == 'darwin': kwds['extra_link_args'] = (kwds.get('extra_link_args', []) + ['-stdlib=libc++']) return _verify(ffi, module_name, source, *args, **kwds) def test_set_source_no_slashes(): ffi = FFI() pytest.raises(ValueError, ffi.set_source, "abc/def", None) pytest.raises(ValueError, ffi.set_source, "abc/def", "C code") 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("extern 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 ', ignore_warnings=True) 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("extern 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("extern 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 with pytest.raises(OverflowError): lib.a = 2147483648 with pytest.raises(OverflowError): lib.a = -2147483649 lib.b = 525 # try with the first access being in setattr, too assert lib.b == 525 with pytest.raises(AttributeError): del lib.a with pytest.raises(AttributeError): del lib.c with pytest.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 with pytest.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 = pytest.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 with pytest.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 = pytest.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 with pytest.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); extern 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 with pytest.raises(OverflowError): p.a -= 1 with pytest.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 # pytest.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 = pytest.raises(ffi.error, ffi.new, "struct foo_s *", []) # lazily assert str(e.value).startswith( "struct foo_s: wrong offset for field 'b' (cdef " 'says 0, but C compiler says 4). fix it or use "...;" ') 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, ... };") pytest.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 with pytest.raises(IndexError): p.a[42] with pytest.raises(IndexError): p.b[11] def test_dotdotdot_global_array(): ffi = FFI() ffi.cdef("extern int aa[...]; extern 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 with pytest.raises(IndexError): lib.aa[41] with pytest.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 try: # lazily build the fields and boom: p = ffi.new("struct foo_s *") p.a assert False, "should have raised" except ffi.error as e: assert str(e).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, 40]]) assert p.a[2] == 30 assert ffi.sizeof(p) == ffi.sizeof("void *") assert ffi.sizeof(p[0]) == 5 * ffi.sizeof("int") def test_math_sin_type(): ffi = FFI() ffi.cdef("double sin(double); void *xxtestfunc();") lib = verify(ffi, 'test_math_sin_type', """ #include void *xxtestfunc(void) { return 0; } """) # '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) pytest.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__ == ( "double sin(double);\n" "\n" "CFFI C function from _CFFI_test_math_sin_type.lib") assert ffi.typeof(lib.xxtestfunc).cname == "void *(*)()" assert lib.xxtestfunc.__doc__ == ( "void *xxtestfunc();\n" "\n" "CFFI C function from _CFFI_test_math_sin_type.lib") 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('') # getattr(importlib, 'invalidate_caches', object)() # 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("extern short glob;") pytest.raises(VerificationError, verify, ffi, "test_bad_size_of_global_1", "long glob;") def test_bad_size_of_global_2(): ffi = FFI() ffi.cdef("extern int glob[10];") pytest.raises(VerificationError, verify, ffi, "test_bad_size_of_global_2", "int glob[9];") def test_unspecified_size_of_global_1(): ffi = FFI() ffi.cdef("extern 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("extern 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("extern 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("extern 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 (sshort_t)(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 = pytest.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: pytest.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)], ignore_warnings=True) 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(""" extern 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 # pytest.raises(AttributeError, ffi.addressof, lib, 'unknown_var') pytest.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 = pytest.raises(TypeError, lib.foo0, 42) e2 = pytest.raises(TypeError, lib.foo0, 43, 44) e3 = pytest.raises(TypeError, lib.foo1) e4 = pytest.raises(TypeError, lib.foo1, 43, 44) e5 = pytest.raises(TypeError, lib.foo2) e6 = pytest.raises(TypeError, lib.foo2, 42) e7 = pytest.raises(TypeError, lib.foo2, 45, 46, 47) def st1(s): s = str(s) if s.startswith("_CFFI_test_unpack_args.Lib."): s = s[len("_CFFI_test_unpack_args.Lib."):] return s assert st1(e1.value) == "foo0() takes no arguments (1 given)" assert st1(e2.value) == "foo0() takes no arguments (2 given)" assert st1(e3.value) == "foo1() takes exactly one argument (0 given)" assert st1(e4.value) == "foo1() takes exactly one argument (2 given)" assert st1(e5.value) in ["foo2 expected 2 arguments, got 0", "foo2() takes exactly 2 arguments (0 given)"] assert st1(e6.value) in ["foo2 expected 2 arguments, got 1", "foo2() takes exactly 2 arguments (1 given)"] assert st1(e7.value) in ["foo2 expected 2 arguments, got 3", "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); } """, ignore_warnings=True) 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 = pytest.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; extern 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 = pytest.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 = pytest.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; " "extern 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.typeof(s.a) == ffi.typeof("int[5][8]") assert ffi.sizeof(s.a) == 40 * ffi.sizeof('int') assert s.a[4][7] == 0 with pytest.raises(IndexError): s.a[4][8] with pytest.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 with pytest.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("extern 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 with pytest.raises(IndexError): lib.a[0][8] with pytest.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("extern 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 with pytest.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("extern 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 with pytest.raises(IndexError): lib.a[0][8] with pytest.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 pytest.raises(OverflowError, lib.foobar, 2 ** 15, [0, 0]) pytest.raises(OverflowError, lib.foobar, -(2 ** 15) - 1, [0, 0]) pytest.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() pytest.raises((FFIError, # with pycparser <= 2.17 CDefError), # with pycparser >= 2.18 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 = pytest.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; } """, ignore_warnings=True) if ffi.sizeof("long double") == ffi.sizeof("double"): assert lib.neg(12.3) == -12.3 else: e = pytest.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); static 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__ == '_CFFI_test_import_from_lib.lib' assert lib.__class__ is type(sys) # !! hack for help() def test_macro_var_callback(): ffi = FFI() ffi.cdef("extern int my_value; extern 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 pytest.raises(ffi.error, getattr, lib, 'my_value') pytest.raises(ffi.error, setattr, lib, 'my_value', 50) pytest.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 pytest.raises(ffi.error, getattr, lib, 'my_value') e = pytest.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("""extern 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 with pytest.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)) pytest.raises(TypeError, lib.call1, cb2) pytest.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: pytest.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) pytest.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) pytest.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) pytest.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: pytest.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) pytest.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) pytest.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) pytest.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(): import warnings ffi = FFI() with warnings.catch_warnings(record=True) as log: ffi.cdef(""" extern "Python" { int bar(int, int); void baz(int, int); int bok(void); void boz(void); } """) assert len(log) == 0, "got a warning: %r" % (log,) lib = verify(ffi, 'test_extern_python_1', """ static void baz(int, int); /* forward */ """) 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 _CFFI_test_extern_python_1.bar() called, " b"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("extern int abc;") lib = verify(ffi, 'test_extern_python_bogus_name', "int abc;") def fn(): pass pytest.raises(ffi.error, ffi.def_extern("unknown_name"), fn) pytest.raises(ffi.error, ffi.def_extern("abc"), fn) assert lib.abc == 0 e = pytest.raises(ffi.error, ffi.def_extern("abc"), fn) assert str(e.value) == ("ffi.def_extern('abc'): no 'extern \"Python\"' " "function with this name") e = pytest.raises(ffi.error, ffi.def_extern(), fn) assert str(e.value) == ("ffi.def_extern('fn'): no 'extern \"Python\"' " "function with this name") # pytest.raises(TypeError, ffi.def_extern(42), fn) pytest.raises((TypeError, AttributeError), ffi.def_extern(), "foo") class X: pass x = X() x.__name__ = x pytest.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 msg = f.getvalue() assert "rom cffi callback %r" % (bar,) in msg assert "rying to convert the result back to C:\n" in msg assert msg.endswith( "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', "") pytest.raises(TypeError, ffi.def_extern(425), None) pytest.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 pytest.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); static int (__stdcall * mycb1)(int); static 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 def test_extern_python_plus_c(): ffi = FFI() ffi.cdef(""" extern "Python+C" int foo(int); extern "C +\tPython" int bar(int); int call_me(int); """) lib = verify(ffi, 'test_extern_python_plus_c', """ int foo(int); #ifdef __GNUC__ __attribute__((visibility("hidden"))) #endif int bar(int); static int call_me(int x) { return foo(x) - bar(x); } """) # @ffi.def_extern() def foo(x): return x * 42 @ffi.def_extern() def bar(x): return x * 63 assert lib.foo(100) == 4200 assert lib.bar(100) == 6300 assert lib.call_me(100) == -2100 def test_introspect_function(): ffi = FFI() ffi.cdef("float f1(double);") lib = verify(ffi, 'test_introspect_function', """ float f1(double x) { return (float)x; } """) assert dir(lib) == ['f1'] FUNC = ffi.typeof(lib.f1) assert FUNC.kind == 'function' assert FUNC.args[0].cname == 'double' assert FUNC.result.cname == 'float' assert ffi.typeof(ffi.addressof(lib, 'f1')) is FUNC def test_introspect_global_var(): ffi = FFI() ffi.cdef("extern float g1;") lib = verify(ffi, 'test_introspect_global_var', """ float g1; """) assert dir(lib) == ['g1'] FLOATPTR = ffi.typeof(ffi.addressof(lib, 'g1')) assert FLOATPTR.kind == 'pointer' assert FLOATPTR.item.cname == 'float' def test_introspect_global_var_array(): ffi = FFI() ffi.cdef("extern float g1[100];") lib = verify(ffi, 'test_introspect_global_var_array', """ float g1[100]; """) assert dir(lib) == ['g1'] FLOATARRAYPTR = ffi.typeof(ffi.addressof(lib, 'g1')) assert FLOATARRAYPTR.kind == 'pointer' assert FLOATARRAYPTR.item.kind == 'array' assert FLOATARRAYPTR.item.length == 100 assert ffi.typeof(lib.g1) is FLOATARRAYPTR.item def test_introspect_integer_const(): ffi = FFI() ffi.cdef("#define FOO 42") lib = verify(ffi, 'test_introspect_integer_const', """ #define FOO 42 """) assert dir(lib) == ['FOO'] assert lib.FOO == ffi.integer_const('FOO') == 42 def test_introspect_typedef(): ffi = FFI() ffi.cdef("typedef int foo_t;") lib = verify(ffi, 'test_introspect_typedef', """ typedef int foo_t; """) assert ffi.list_types() == (['foo_t'], [], []) assert ffi.typeof('foo_t').kind == 'primitive' assert ffi.typeof('foo_t').cname == 'int' def test_introspect_typedef_multiple(): ffi = FFI() ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;") lib = verify(ffi, 'test_introspect_typedef_multiple', """ typedef signed char a_t, c_t, g_t, b_t; """) assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'g_t'], [], []) def test_introspect_struct(): ffi = FFI() ffi.cdef("struct foo_s { int a; };") lib = verify(ffi, 'test_introspect_struct', """ struct foo_s { int a; }; """) assert ffi.list_types() == ([], ['foo_s'], []) assert ffi.typeof('struct foo_s').kind == 'struct' assert ffi.typeof('struct foo_s').cname == 'struct foo_s' def test_introspect_union(): ffi = FFI() ffi.cdef("union foo_s { int a; };") lib = verify(ffi, 'test_introspect_union', """ union foo_s { int a; }; """) assert ffi.list_types() == ([], [], ['foo_s']) assert ffi.typeof('union foo_s').kind == 'union' assert ffi.typeof('union foo_s').cname == 'union foo_s' def test_introspect_struct_and_typedef(): ffi = FFI() ffi.cdef("typedef struct { int a; } foo_t;") lib = verify(ffi, 'test_introspect_struct_and_typedef', """ typedef struct { int a; } foo_t; """) assert ffi.list_types() == (['foo_t'], [], []) assert ffi.typeof('foo_t').kind == 'struct' assert ffi.typeof('foo_t').cname == 'foo_t' def test_introspect_included_type(): SOURCE = """ typedef signed char schar_t; struct sint_t { int x; }; """ ffi1 = FFI() ffi1.cdef(SOURCE) ffi2 = FFI() ffi2.include(ffi1) verify(ffi1, "test_introspect_included_type_parent", SOURCE) verify(ffi2, "test_introspect_included_type", SOURCE) assert ffi1.list_types() == ffi2.list_types() == ( ['schar_t'], ['sint_t'], []) def test_introspect_order(): ffi = FFI() ffi.cdef("union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;") ffi.cdef("union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb;") ffi.cdef("union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb;") verify(ffi, "test_introspect_order", """ union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb; union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb; union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb; """) assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'], ['CFFIa', 'CFFIcc', 'CFFIccc'], ['CFFIaa', 'CFFIaaa', 'CFFIg']) def test_bool_in_cpp(): # this works when compiled as C, but in cffi < 1.7 it fails as C++ ffi = FFI() ffi.cdef("bool f(void);") lib = verify(ffi, "test_bool_in_cpp", "char f(void) { return 2; }") assert lib.f() is True def test_bool_in_cpp_2(): ffi = FFI() ffi.cdef('int add(int a, int b);') lib = verify(ffi, "test_bool_bug_cpp", ''' typedef bool _Bool; /* there is a Windows header with this line */ int add(int a, int b) { return a + b; }''', source_extension='.cpp') c = lib.add(2, 3) assert c == 5 def test_struct_field_opaque(): ffi = FFI() ffi.cdef("struct a { struct b b; };") e = pytest.raises(TypeError, verify, ffi, "test_struct_field_opaque", "?") assert str(e.value) == ("struct a: field 'a.b' is of an opaque" " type (not declared in cdef())") ffi = FFI() ffi.cdef("struct a { struct b b[2]; };") e = pytest.raises(TypeError, verify, ffi, "test_struct_field_opaque", "?") assert str(e.value) == ("struct a: field 'a.b' is of an opaque" " type (not declared in cdef())") ffi = FFI() ffi.cdef("struct a { struct b b[]; };") e = pytest.raises(TypeError, verify, ffi, "test_struct_field_opaque", "?") assert str(e.value) == ("struct a: field 'a.b' is of an opaque" " type (not declared in cdef())") def test_function_arg_opaque(): pytest.skip("can currently declare a function with an opaque struct " "as argument, but AFAICT it's impossible to call it later") def test_function_returns_opaque(): ffi = FFI() ffi.cdef("struct a foo(int);") e = pytest.raises(TypeError, verify, ffi, "test_function_returns_opaque", "?") assert str(e.value) == ("function foo: 'struct a' is used as result type," " but is opaque") def test_function_returns_union(): ffi = FFI() ffi.cdef("union u1 { int a, b; }; union u1 f1(int);") lib = verify(ffi, "test_function_returns_union", """ union u1 { int a, b; }; static union u1 f1(int x) { union u1 u; u.b = x; return u; } """) assert lib.f1(51).a == 51 def test_function_returns_partial_struct(): ffi = FFI() ffi.cdef("struct aaa { int a; ...; }; struct aaa f1(int);") lib = verify(ffi, "test_function_returns_partial_struct", """ struct aaa { int b, a, c; }; static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; } """) assert lib.f1(52).a == 52 def test_function_returns_float_complex(): if sys.platform == 'win32': pytest.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("float _Complex f1(float a, float b);"); lib = verify(ffi, "test_function_returns_float_complex", """ #include static float _Complex f1(float a, float b) { return a + I*2.0f*b; } """, no_cpp=True) # fails on some systems with C++ result = lib.f1(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact def test_function_returns_double_complex(): if sys.platform == 'win32': pytest.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("double _Complex f1(double a, double b);"); lib = verify(ffi, "test_function_returns_double_complex", """ #include static double _Complex f1(double a, double b) { return a + I*2.0*b; } """, no_cpp=True) # fails on some systems with C++ result = lib.f1(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert result.imag == 2*5.1 # exact def test_function_argument_float_complex(): if sys.platform == 'win32': pytest.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("float f1(float _Complex x);"); lib = verify(ffi, "test_function_argument_float_complex", """ #include static float f1(float _Complex x) { return cabsf(x); } """, no_cpp=True) # fails on some systems with C++ x = complex(12.34, 56.78) result = lib.f1(x) assert abs(result - abs(x)) < 1e-5 def test_function_argument_double_complex(): if sys.platform == 'win32': pytest.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("double f1(double _Complex);"); lib = verify(ffi, "test_function_argument_double_complex", """ #include static double f1(double _Complex x) { return cabs(x); } """, no_cpp=True) # fails on some systems with C++ x = complex(12.34, 56.78) result = lib.f1(x) assert abs(result - abs(x)) < 1e-11 def test_typedef_array_dotdotdot(): ffi = FFI() ffi.cdef(""" typedef int foo_t[...], bar_t[...]; extern int gv[...]; typedef int mat_t[...][...]; typedef int vmat_t[][...]; """) lib = verify(ffi, "test_typedef_array_dotdotdot", """ typedef int foo_t[50], bar_t[50]; int gv[23]; typedef int mat_t[6][7]; typedef int vmat_t[][8]; """) assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") assert ffi.sizeof("bar_t") == 50 * ffi.sizeof("int") assert len(ffi.new("foo_t")) == 50 assert len(ffi.new("bar_t")) == 50 assert ffi.sizeof(lib.gv) == 23 * ffi.sizeof("int") assert ffi.sizeof("mat_t") == 6 * 7 * ffi.sizeof("int") assert len(ffi.new("mat_t")) == 6 assert len(ffi.new("mat_t")[3]) == 7 pytest.raises(ffi.error, ffi.sizeof, "vmat_t") p = ffi.new("vmat_t", 4) assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int") def test_typedef_array_dotdotdot_usage(): ffi = FFI() ffi.cdef(""" typedef int foo_t[...]; typedef int mat_t[...][...]; struct s { foo_t a; foo_t *b; foo_t **c; }; int myfunc(foo_t a, foo_t *b, foo_t **c); struct sm { mat_t a; mat_t *b; mat_t **c; }; int myfuncm(mat_t a, mat_t *b, mat_t **c); """) lib = verify(ffi, "test_typedef_array_dotdotdot_usage", """ typedef int foo_t[50]; typedef int mat_t[6][7]; struct s { foo_t a; foo_t *b; foo_t **c; }; static int myfunc(foo_t a, foo_t *b, foo_t **c) { return (**c)[49]; } struct sm { mat_t a; mat_t *b; mat_t **c; }; static int myfuncm(mat_t a, mat_t *b, mat_t **c) { return (**c)[5][6]; } """) assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") p = ffi.new("struct s *") assert ffi.sizeof(p[0]) == 50 * ffi.sizeof("int") + 2 * ffi.sizeof("void *") p.a[49] = 321 p.b = ffi.addressof(p, 'a') p.c = ffi.addressof(p, 'b') assert lib.myfunc(ffi.NULL, ffi.NULL, p.c) == 321 # assert ffi.sizeof("mat_t") == 42 * ffi.sizeof("int") p = ffi.new("struct sm *") assert ffi.sizeof(p[0]) == 42 * ffi.sizeof("int") + 2 * ffi.sizeof("void *") p.a[5][6] = -321 p.b = ffi.addressof(p, 'a') p.c = ffi.addressof(p, 'b') assert lib.myfuncm(ffi.NULL, ffi.NULL, p.c) == -321 def test_call_with_custom_field_pos(): ffi = FFI() ffi.cdef(""" struct foo { int x; ...; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_custom_field_pos", """ struct foo { int y, x; }; struct foo f(void) { struct foo s = { 40, 200 }; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().x == 200 e = pytest.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( 'ctype \'struct foo\' not supported as return value. It is a ' 'struct declared with "...;", but the C calling convention may ' 'depend on the missing fields; or, it contains anonymous ' 'struct/unions. Such structs are only supported ' 'as return value if the function is \'API mode\' and non-variadic ' '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' 'and not taking a final \'...\' argument)') def test_call_with_nested_anonymous_struct(): if sys.platform == 'win32': pytest.skip("needs a GCC extension") ffi = FFI() ffi.cdef(""" struct foo { int a; union { int b, c; }; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_nested_anonymous_struct", """ struct foo { int a; union { int b, c; }; }; struct foo f(void) { struct foo s; s.a = 40; s.b = 200; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().b == 200 e = pytest.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( 'ctype \'struct foo\' not supported as return value. It is a ' 'struct declared with "...;", but the C calling convention may ' 'depend on the missing fields; or, it contains anonymous ' 'struct/unions. Such structs are only supported ' 'as return value if the function is \'API mode\' and non-variadic ' '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' 'and not taking a final \'...\' argument)') def test_call_with_bitfield(): ffi = FFI() ffi.cdef(""" struct foo { int x:5; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_bitfield", """ struct foo { int x:5; }; struct foo f(void) { struct foo s = { 11 }; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().x == 11 e = pytest.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'struct foo' not supported as return value. It is a struct " "with bit fields, which libffi does not support. Such structs are " "only supported as return value if the function is 'API mode' and " "non-variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." "set_source() and not taking a final '...' argument)") def test_call_with_zero_length_field(): if sys.platform == 'win32': pytest.skip("zero-length field not supported by MSVC") ffi = FFI() ffi.cdef(""" struct foo { int a; int x[0]; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_zero_length_field", """ struct foo { int a; int x[0]; }; struct foo f(void) { struct foo s = { 42 }; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().a == 42 e = pytest.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'struct foo' not supported as return value. It is a " "struct with a zero-length array, which libffi does not support." " Such structs are only supported as return value if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" "+ffibuilder.set_source() and not taking a final '...' argument)") def test_call_with_union(): ffi = FFI() ffi.cdef(""" union foo { int a; char b; }; union foo f(void); union foo g(int, ...); """) lib = verify(ffi, "test_call_with_union", """ union foo { int a; char b; }; union foo f(void) { union foo s = { 42 }; return s; } union foo g(int a, ...) { return f(); } """) assert lib.f().a == 42 e = pytest.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'union foo' not supported as return value by libffi. " "Unions are only supported as return value if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" "+ffibuilder.set_source() and not taking a final '...' argument)") def test_call_with_packed_struct(): if sys.platform == 'win32': pytest.skip("needs a GCC extension") ffi = FFI() ffi.cdef(""" struct foo { char y; int x; }; struct foo f(void); struct foo g(int, ...); """, packed=True) lib = verify(ffi, "test_call_with_packed_struct", """ struct foo { char y; int x; } __attribute__((packed)); struct foo f(void) { struct foo s = { 40, 200 }; return s; } struct foo g(int a, ...) { struct foo s = { 41, 201 }; return s; } """) assert ord(lib.f().y) == 40 assert lib.f().x == 200 e = pytest.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'struct foo' not supported as return value. It is a " "'packed' structure, with a different layout than expected by libffi." " Such structs are only supported as return value if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" "+ffibuilder.set_source() and not taking a final '...' argument)") def test_pack_not_supported(): ffi = FFI() ffi.cdef("""struct foo { char y; int x; };""", pack=2) pytest.raises(NotImplementedError, verify, ffi, "test_pack_not_supported", "") def test_gcc_visibility_hidden(): if sys.platform == 'win32': pytest.skip("test for gcc/clang") ffi = FFI() ffi.cdef(""" int f(int); """) lib = verify(ffi, "test_gcc_visibility_hidden", """ int f(int a) { return a + 40; } """, extra_compile_args=['-fvisibility=hidden']) assert lib.f(2) == 42 def test_override_default_definition(): ffi = FFI() ffi.cdef("typedef long int16_t, char16_t;") lib = verify(ffi, "test_override_default_definition", "") assert ffi.typeof("int16_t") is ffi.typeof("char16_t") is ffi.typeof("long") def test_char16_char32_type(no_cpp=False): if no_cpp is False and sys.platform == "win32": pytest.skip("aaaaaaa why do modern MSVC compilers still define " "a very old __cplusplus value") ffi = FFI() ffi.cdef(""" char16_t foo_2bytes(char16_t); char32_t foo_4bytes(char32_t); """) lib = verify(ffi, "test_char16_char32_type" + no_cpp * "_nocpp", """ #if !defined(__cplusplus) || (!defined(_LIBCPP_VERSION) && __cplusplus < 201103L) typedef uint_least16_t char16_t; typedef uint_least32_t char32_t; #endif char16_t foo_2bytes(char16_t a) { return (char16_t)(a + 42); } char32_t foo_4bytes(char32_t a) { return (char32_t)(a + 42); } """, no_cpp=no_cpp) assert lib.foo_2bytes(u+'\u1234') == u+'\u125e' assert lib.foo_4bytes(u+'\u1234') == u+'\u125e' assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f' pytest.raises(TypeError, lib.foo_2bytes, u+'\U00012345') pytest.raises(TypeError, lib.foo_2bytes, 1234) pytest.raises(TypeError, lib.foo_4bytes, 1234) def test_char16_char32_plain_c(): test_char16_char32_type(no_cpp=True) def test_loader_spec(): ffi = FFI() lib = verify(ffi, "test_loader_spec", "") if sys.version_info < (3,): assert not hasattr(lib, '__loader__') assert not hasattr(lib, '__spec__') else: assert lib.__loader__ is None assert lib.__spec__ is None def test_realize_struct_error(): ffi = FFI() ffi.cdef("""typedef ... foo_t; struct foo_s { void (*x)(foo_t); };""") lib = verify(ffi, "test_realize_struct_error", """ typedef int foo_t; struct foo_s { void (*x)(foo_t); }; """) pytest.raises(TypeError, ffi.new, "struct foo_s *") def test_from_buffer_struct(): ffi = FFI() ffi.cdef("""struct foo_s { int a, b; };""") lib = verify(ffi, "test_from_buffer_struct_p", """ struct foo_s { int a, b; }; """) p = ffi.new("struct foo_s *", [-219239, 58974983]) q = ffi.from_buffer("struct foo_s[]", ffi.buffer(p)) assert ffi.typeof(q) == ffi.typeof("struct foo_s[]") assert len(q) == 1 assert q[0].a == p.a assert q[0].b == p.b assert q == p q = ffi.from_buffer("struct foo_s *", ffi.buffer(p)) assert ffi.typeof(q) == ffi.typeof("struct foo_s *") assert q.a == p.a assert q.b == p.b assert q[0].a == p.a assert q[0].b == p.b assert q == p def test_unnamed_bitfield_1(): ffi = FFI() ffi.cdef("""struct A { char : 1; };""") lib = verify(ffi, "test_unnamed_bitfield_1", """ struct A { char : 1; }; """) p = ffi.new("struct A *") assert ffi.sizeof(p[0]) == 1 # Note: on gcc, the type name is ignored for anonymous bitfields # and that's why the result is 1. On MSVC, the result is # sizeof("char") which is also 1. def test_unnamed_bitfield_2(): ffi = FFI() ffi.cdef("""struct A { short c : 1; short : 1; short d : 1; short : 1; };""") lib = verify(ffi, "test_unnamed_bitfield_2", """ struct A { short c : 1; short : 1; short d : 1; short : 1; }; """) p = ffi.new("struct A *") assert ffi.sizeof(p[0]) == ffi.sizeof("short") def test_unnamed_bitfield_3(): ffi = FFI() ffi.cdef("""struct A { struct { char : 1; char : 1; } b; };""") lib = verify(ffi, "test_unnamed_bitfield_3", """ struct A { struct { char : 1; char : 1; } b; }; """) p = ffi.new("struct A *") assert ffi.sizeof(p[0]) == 1 # Note: on gcc, the type name is ignored for anonymous bitfields # and that's why the result is 1. On MSVC, the result is # sizeof("char") which is also 1. def test_unnamed_bitfield_4(): ffi = FFI() ffi.cdef("""struct A { struct { unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a; }; struct B { struct A a; };""") lib = verify(ffi, "test_unnamed_bitfield_4", """ struct A { struct { unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a; }; struct B { struct A a; }; """) b = ffi.new("struct B *") a = ffi.new("struct A *") assert ffi.sizeof(a[0]) == ffi.sizeof("unsigned") assert ffi.sizeof(b[0]) == ffi.sizeof(a[0]) def test_struct_with_func_with_struct_pointer_arg(): ffi = FFI() ffi.cdef("""struct BinaryTree { int (* CompareKey)(struct BinaryTree *tree); };""") lib = verify(ffi, "test_struct_with_func_with_struct_pointer_arg", """ struct BinaryTree { int (* CompareKey)(struct BinaryTree *tree); }; """) ffi.new("struct BinaryTree *") def test_struct_with_func_with_struct_arg(): ffi = FFI() ffi.cdef("""struct BinaryTree { int (* CompareKey)(struct BinaryTree tree); };""") lib = verify(ffi, "test_struct_with_func_with_struct_arg", """ struct BinaryTree { int (* CompareKey)(struct BinaryTree tree); }; """) pytest.raises(RuntimeError, ffi.new, "struct BinaryTree *") def test_passing_large_list(): ffi = FFI() ffi.cdef("""void passing_large_list(long[]);""") lib = verify(ffi, "test_passing_large_list", """ static void passing_large_list(long a[]) { } """) arg = list(range(20000000)) lib.passing_large_list(arg) # assert did not segfault ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_unicode_literals.py0000644000175100001770000000210400000000000022714 0ustar00runnerdocker00000000000000# # ---------------------------------------------- # WARNING, ALL LITERALS IN THIS FILE ARE UNICODE # ---------------------------------------------- # from __future__ import unicode_literals # # # from _cffi_backend import FFI 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_callback(): ffi = FFI() cb = ffi.callback("int(int)", # unicode literal lambda x: x + 42) assert cb(5) == 47 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_verify1.py0000644000175100001770000023535000000000000020767 0ustar00runnerdocker00000000000000import os, sys, math import pytest from cffi import FFI, FFIError, VerificationError, VerificationMissing, model from cffi import CDefError from cffi import recompiler from testing.support import * from testing.support import _verify, extra_compile_args, is_musl 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'] 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 _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'): pytest.skip("needs GCC") if '-Wno-error=sign-conversion' in extra_compile_args: pytest.skip("gcc 9.2.0 compiler bug exposed by Python 3.12+ prevents compilation with sign-conversion warnings-as-errors") ffi = FFI() ffi.cdef(cdef) pytest.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: pytest.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() X = tp.is_complex_type() I = tp.is_integer_type() assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t')) assert F == (typename in ('float', 'double', 'long double')) assert X == (typename in ('float _Complex', 'double _Complex')) assert I + F + C + X == 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 all_primitive_types[typename] == 'j' or # complex 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 pytest.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: pytest.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(["static %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 pytest.raises(OverflowError, setattr, lib, varname, max+1) pytest.raises(OverflowError, setattr, lib, varname, min-1) def test_var_unsigned_integer_types(): ffi = FFI() lst = all_unsigned_integer_types(ffi) csource = "\n".join(["static %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 pytest.raises(OverflowError, setattr, lib, varname, max+1) pytest.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 pytest.raises(OverflowError, fn, max + 1) pytest.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 pytest.raises(OverflowError, fn, max + 1) pytest.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" pytest.raises(TypeError, lib.foo, b"bar") pytest.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; }") pytest.raises(TypeError, lib.foo, ffi.new("short *", 42)) def test_verify_typedefs(): pytest.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: pytest.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 pytest.raises(VerificationError, check, "struct foo_s { char x; int y; int *z; };") # pytest.raises(VerificationError, check, "struct foo_s { int y; long *z; };") # cdef'ed field x is missing # e = pytest.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 = pytest.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 = pytest.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; ...; }; """) pytest.raises(VerificationMissing, ffi.sizeof, 'struct foo_s') pytest.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x') pytest.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': pytest.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[]') # implicit max length assert len(s.a) == 18 # max length, computed from the size and start offset 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') with pytest.raises(IndexError): s.a[17] def test_struct_array_c99_1(): if sys.platform == 'win32': pytest.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(ffi.typeof(s[0])) == 1 * ffi.sizeof('int') assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') # ^^^ explanation: if you write in C: "char x[5];", then # "sizeof(x)" will evaluate to 5. The behavior above is # a generalization of that to "struct foo_s[len(a)=5] x;" # if you could do that in C. assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) assert ffi.sizeof(s[0]) == 5 * 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': pytest.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]) == 6 * ffi.sizeof('int') assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) assert ffi.sizeof(s[0]) == 6 * 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 with pytest.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() pytest.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 };") pytest.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" # 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 };") lib = ffi.verify("enum ee { EE1, EE2, EE3 };") assert [lib.EE1, lib.EE2, lib.EE3] == [0, 1, 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 };") pytest.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... };") pytest.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("static int foo(void);\n" "static 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("static 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("static int foo(int);\n" "static 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': pytest.skip("XXX fixme: only gives warnings") ffi = FFI() ffi.cdef("int foo(int x, ...);") pytest.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" "static 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; } static int (*foo)(struct foo_s s) = &foo1; """) e = pytest.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; or, it contains anonymous struct/unions. " "Such structs are only supported as argument " "if the function is 'API mode' and non-variadic (i.e. declared " "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking " "a final '...' argument)") 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 pytest.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 pytest.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'): # pytest.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') #elif platform.machine() == 'mips64' and sys.maxsize > 2**32: # pytest.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 = pytest.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': pytest.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': pytest.skip("nested anonymous struct/union") ffi = FFI() ffi.cdef(""" struct foo_s { struct { int a; char b; }; union { char c, d; }; }; """) pytest.raises(VerificationError, ffi.verify, """ struct foo_s { struct { int a; short b; }; union { char c, d; }; }; """) # works fine now #pytest.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': pytest.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(): pytest.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': pytest.skip("_Bool not in MSVC") ffi = FFI() ffi.cdef("struct foo_s { _Bool x; };" "_Bool foo(_Bool); static _Bool (*foop)(_Bool);") lib = ffi.verify(""" struct foo_s { _Bool x; }; int foo(int arg) { return !arg; } _Bool _foofunc(_Bool x) { return !x; } static _Bool (*foop)(_Bool) = _foofunc; """) p = ffi.new("struct foo_s *") p.x = 1 assert p.x is True with pytest.raises(OverflowError): p.x = -1 with pytest.raises(TypeError): p.x = 0.0 assert lib.foop(1) is False assert lib.foop(True) is False assert lib.foop(0) is True pytest.raises(OverflowError, lib.foop, 42) pytest.raises(TypeError, lib.foop, 0.0) assert lib.foo(1) is False assert lib.foo(True) is False assert lib.foo(0) is True pytest.raises(OverflowError, lib.foo, 42) pytest.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 # pytest.raises(TypeError, ffi.cast, "_Bool", []) def test_bool_on_long_double(): if sys.platform == 'win32': pytest.skip("_Bool not in MSVC") f = 1E-250 if f == 0.0 or f*f != 0.0: pytest.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): pytest.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 pytest.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 *") with pytest.raises(TypeError): p.x = 0.0 assert lib.foo(42) == 0 assert lib.foo(0) == 1 pytest.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 pytest.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 pytest.raises(TypeError, lib.sum_coord, res2) def test_callback_in_thread(): pytest.xfail("adapt or remove") if sys.platform == 'win32': pytest.skip("pthread only") import os, subprocess from cffi import _imp_emulation as 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(): pytest.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(): pytest.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') or is_musl: pytest.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); extern 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("extern 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(""" static 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("""extern 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("""extern const int a[];""") lib = ffi.verify("""const int a[5];""") assert repr(ffi.typeof(lib.a)) == "" def _test_various_calls(force_libffi): cdef_source = """ extern int xvalue; extern long long ivalue, rvalue; extern float fvalue; extern double dvalue; extern 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': pytest.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'): pytest.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("extern 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 = pytest.raises(NotImplementedError, fooptr, ffi.new("Data *")) assert str(e.value) == ( "ctype 'Data' not supported as argument by libffi. Unions are only " "supported as argument if the function is 'API mode' and " "non-variadic (i.e. declared inside ffibuilder.cdef()+" "ffibuilder.set_source() and not taking a final '...' argument)") e = pytest.raises(NotImplementedError, bazptr) assert str(e.value) == ( "ctype 'Data' not supported as return value by libffi. Unions are " "only supported as return value if the function is 'API mode' and " "non-variadic (i.e. declared inside ffibuilder.cdef()+" "ffibuilder.set_source() and not taking a final '...' argument)") e = pytest.raises(NotImplementedError, barptr) assert str(e.value) == ( "ctype 'MyStr' not supported as return value. It is a struct with " "bit fields, which libffi does not support. Such structs are only " "supported as return value if the function is 'API mode' and non-" "variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." "set_source() and not taking a final '...' argument)") 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': pytest.skip("win32-only test") ffi = FFI() e = pytest.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 with pytest.raises(ffi.error) as e: 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() pytest.raises((FFIError, # with pycparser <= 2.17 CDefError), # with pycparser >= 2.18 ffi.cdef, """typedef void... foo_t;""") # ffi.cdef("typedef int... foo_t;") pytest.raises(VerificationError, ffi.verify, "typedef float foo_t;") def test_windows_dllimport_data(): if sys.platform != 'win32': pytest.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("extern 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': pytest.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 *") def _only_test_on_linux_intel(): if not sys.platform.startswith('linux'): pytest.skip('only running the memory-intensive test on Linux') import platform machine = platform.machine() if 'x86' not in machine and 'x64' not in machine: pytest.skip('only running the memory-intensive test on x86/x64') def test_ffi_gc_size_arg(): _only_test_on_linux_intel() ffi = FFI() ffi.cdef("void *malloc(size_t); void free(void *);") lib = ffi.verify(r""" #include """) for i in range(2000): p = lib.malloc(20*1024*1024) # 20 MB p1 = ffi.cast("char *", p) for j in range(0, 20*1024*1024, 4096): p1[j] = b'!' p = ffi.gc(p, lib.free, 20*1024*1024) del p # with PyPy's GC, the above would rapidly consume 40 GB of RAM # without the third argument to ffi.gc() def test_ffi_gc_size_arg_2(): # a variant of the above: this "attack" works on cpython's cyclic gc too # and I found no obvious way to prevent that. So for now, this test # is skipped on CPython, where it eats all the memory. if '__pypy__' not in sys.builtin_module_names: pytest.skip("find a way to tweak the cyclic GC of CPython") _only_test_on_linux_intel() ffi = FFI() ffi.cdef("void *malloc(size_t); void free(void *);") lib = ffi.verify(r""" #include """) class X(object): pass for i in range(2000): p = lib.malloc(50*1024*1024) # 50 MB p1 = ffi.cast("char *", p) for j in range(0, 50*1024*1024, 4096): p1[j] = b'!' p = ffi.gc(p, lib.free, 50*1024*1024) x = X() x.p = p x.cyclic = x del p, x def test_ffi_new_with_cycles(): # still another variant, with ffi.new() if '__pypy__' not in sys.builtin_module_names: pytest.skip("find a way to tweak the cyclic GC of CPython") ffi = FFI() ffi.cdef("") lib = ffi.verify("") class X(object): pass for i in range(2000): p = ffi.new("char[]", 50*1024*1024) # 50 MB for j in range(0, 50*1024*1024, 4096): p[j] = b'!' x = X() x.p = p x.cyclic = x del p, x ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/cffi1/test_zdist.py0000644000175100001770000004103000000000000020525 0ustar00runnerdocker00000000000000import sys, os import pytest import subprocess import cffi from testing.udir import udir from shutil import rmtree from tempfile import mkdtemp 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...) # NOTE: pointing $HOME to a nonexistent directory can break certain things # that look there for configuration (like ccache). tmp_home = mkdtemp() assert tmp_home != None, "cannot create temporary homedir" env['HOME'] = tmp_home pathlist = sys.path[:] if cwd is None: pathlist.insert(0, self.rootdir) env['PYTHONPATH'] = os.pathsep.join(pathlist) try: subprocess.check_call([self.executable] + args, cwd=cwd, env=env) finally: rmtree(tmp_home) def _prepare_setuptools(self): if hasattr(TestDist, '_setuptools_ready'): return try: import setuptools except ImportError: pytest.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) pytest.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*/") pytest.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: # https://bugs.python.org/issue23246 import setuptools 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}}}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/conftest.py0000644000175100001770000000107000000000000017166 0ustar00runnerdocker00000000000000import pytest import sys from ctypes import util # this problem was supposedly fixed in a newer Python 3.8 release, but after binary installer support expired # https://github.com/python/cpython/pull/28054 if sys.platform == 'darwin' and sys.version_info[:2] == (3, 8): orig_find_library = util.find_library def hacked_find_library(*args, **kwargs): res = orig_find_library(*args, **kwargs) if res is None: pytest.xfail("busted find_library on MacOS Python 3.8") return res util.find_library = hacked_find_library ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1695922997.1637678 cffi-1.16.0/testing/embedding/0000755000175100001770000000000000000000000016707 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/__init__.py0000644000175100001770000000000000000000000021006 0ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/add1-test.c0000644000175100001770000000044100000000000020640 0ustar00runnerdocker00000000000000#include #ifdef _MSC_VER #include #endif 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); #ifdef _MSC_VER if (x == 0 && y == 0) Sleep(2000); #endif return 0; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/add1.py0000644000175100001770000000151300000000000020072 0ustar00runnerdocker00000000000000import 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() # Windows: sometimes time.sleep() doesn't sleep at all. # This appears to occur on recent versions of python only. t_end = time.time() + 0.19 while time.time() < t_end: time.sleep(0.2) 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,)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/add2-test.c0000644000175100001770000000032300000000000020640 0ustar00runnerdocker00000000000000#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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/add2.py0000644000175100001770000000105600000000000020075 0ustar00runnerdocker00000000000000import 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,)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/add3.py0000644000175100001770000000067600000000000020105 0ustar00runnerdocker00000000000000import 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,)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/add_recursive-test.c0000644000175100001770000000073300000000000022652 0ustar00runnerdocker00000000000000#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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/add_recursive.py0000644000175100001770000000134400000000000022102 0ustar00runnerdocker00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api(""" extern 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,)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/empty-test.c0000644000175100001770000000022000000000000021160 0ustar00runnerdocker00000000000000#include void initialize_my_empty_cffi(void); int main(void) { initialize_my_empty_cffi(); printf("OK\n"); return 0; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/empty.py0000644000175100001770000000047500000000000020425 0ustar00runnerdocker00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api("") ffi.set_source("_empty_cffi", """ void initialize_my_empty_cffi(void) { if (cffi_start_python() != 0) { printf("oops, cffi_start_python() returned non-0\\n"); abort(); } } """) fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/initerror.py0000644000175100001770000000036300000000000021300 0ustar00runnerdocker00000000000000import cffi ffi = cffi.FFI() ffi.embedding_api(""" int add1(int, int); """) ffi.embedding_init_code(r""" raise KeyError """) ffi.set_source("_initerror_cffi", """ """) fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/perf-test.c0000644000175100001770000000361300000000000020767 0ustar00runnerdocker00000000000000#include #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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/perf.py0000644000175100001770000000047100000000000020217 0ustar00runnerdocker00000000000000import 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,)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/test_basic.py0000644000175100001770000001745100000000000021411 0ustar00runnerdocker00000000000000import sys, os, re import shutil, subprocess, time import pytest 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: pytest.skip(str(_link_error)) def prefix_pythonpath(): cffi_base = os.path.dirname(os.path.dirname(local_dir)) pythonpath = org_env.get('PYTHONPATH', '').split(os.pathsep) if cffi_base not in pythonpath: pythonpath.insert(0, cffi_base) return os.pathsep.join(pythonpath) def copy_away_env(): global org_env try: org_env except NameError: org_env = os.environ.copy() 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, **kwds): print('RUNNING:', args, kwds) return subprocess.Popen(args, **kwds) def _run(self, args): popen = self._run_base(args, 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\n\n%s" % ( err, args, output)).rstrip()) print(output.rstrip()) return output def prepare_module(self, name): self.patch_environment() 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(). pathname = os.path.join(path, filename) with open(pathname, 'w') as g: g.write(''' # https://bugs.python.org/issue23246 import sys if sys.platform == 'win32': try: import setuptools except ImportError: pass ''') with open(os.path.join(local_dir, filename), 'r') as f: g.write(f.read()) output = self._run([sys.executable, pathname]) 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 patch_environment(self): copy_away_env() path = self.get_path() # for libpypy-c.dll or Python27.dll path = os.path.split(sys.executable)[0] + os.path.pathsep + path env_extra = {'PYTHONPATH': prefix_pythonpath()} if sys.platform == 'win32': envname = 'PATH' else: envname = 'LD_LIBRARY_PATH' libpath = org_env.get(envname) if libpath: libpath = path + os.path.pathsep + libpath else: libpath = path env_extra[envname] = libpath for key, value in sorted(env_extra.items()): if os.environ.get(key) != value: print('* setting env var %r to %r' % (key, value)) os.environ[key] = value def execute(self, name): path = self.get_path() 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], 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" % ( os.path.join(path, executable_name), err)) return result class TestBasic(EmbeddingTests): def test_empty(self): empty_cffi = self.prepare_module('empty') self.compile('empty-test', [empty_cffi]) output = self.execute('empty-test') assert output == 'OK\n' 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") def test_init_time_error(self): initerror_cffi = self.prepare_module('initerror') self.compile('add1-test', [initerror_cffi]) output = self.execute('add1-test') assert output == "got: 0 0\n" # plus lots of info to stderr def test_embedding_with_unicode(self): withunicode_cffi = self.prepare_module('withunicode') self.compile('add1-test', [withunicode_cffi]) output = self.execute('add1-test') assert output == "255\n4660\n65244\ngot: 0 0\n" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/test_performance.py0000644000175100001770000000342400000000000022624 0ustar00runnerdocker00000000000000import sys from testing.embedding.test_basic import EmbeddingTests if sys.platform == 'win32': import pytest pytestmark = pytest.mark.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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/test_recursive.py0000644000175100001770000000116200000000000022327 0ustar00runnerdocker00000000000000from 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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/test_thread.py0000644000175100001770000000534600000000000021577 0ustar00runnerdocker00000000000000from 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(20): 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) for i in range(3): output = self.execute('thread2-test') print('='*79) print(output) print('='*79) 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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/test_tlocal.py0000644000175100001770000000055200000000000021600 0ustar00runnerdocker00000000000000from testing.embedding.test_basic import EmbeddingTests class TestThreadLocal(EmbeddingTests): def test_thread_local(self): tlocal_cffi = self.prepare_module('tlocal') self.compile('tlocal-test', [tlocal_cffi], threads=True) for i in range(10): output = self.execute('tlocal-test') assert output == "done\n" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/thread-test.h0000644000175100001770000000430500000000000021306 0ustar00runnerdocker00000000000000/************************************************************/ #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 /************************************************************/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/thread1-test.c0000644000175100001770000000135000000000000021357 0ustar00runnerdocker00000000000000#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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/thread2-test.c0000644000175100001770000000200400000000000021355 0ustar00runnerdocker00000000000000#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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/thread3-test.c0000644000175100001770000000213500000000000021363 0ustar00runnerdocker00000000000000#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"); fflush(stdout); /* this is occasionally needed on Windows */ return 0; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/tlocal-test.c0000644000175100001770000000150100000000000021303 0ustar00runnerdocker00000000000000#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; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/tlocal.py0000644000175100001770000000123600000000000020541 0ustar00runnerdocker00000000000000import 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,)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/embedding/withunicode.py0000644000175100001770000000076500000000000021613 0ustar00runnerdocker00000000000000import sys, cffi if sys.version_info < (3,): u_prefix = "u" else: u_prefix = "" unichr = chr ffi = cffi.FFI() ffi.embedding_api(u""" int add1(int, int); """) ffi.embedding_init_code((""" import sys, time for c in %s'""" + unichr(0x00ff) + unichr(0x1234) + unichr(0xfedc) + """': sys.stdout.write(str(ord(c)) + '\\n') sys.stdout.flush() """) % u_prefix) ffi.set_source("_withunicode_cffi", """ """) fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/support.py0000644000175100001770000001212700000000000017062 0ustar00runnerdocker00000000000000import sys, os from cffi._imp_emulation import load_dynamic if sys.version_info < (3,): __all__ = ['u', 'arraytostring', 'load_dynamic'] 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'") def arraytostring(a): return a.tostring() else: __all__ = ['u', 'unicode', 'long', 'arraytostring', 'load_dynamic'] u = "" unicode = str long = int def arraytostring(a): return a.tobytes() 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() if hasattr(sys, '__unraisablehook__'): # work around pytest self.old_unraisablebook = sys.unraisablehook # on recent CPythons sys.unraisablehook = sys.__unraisablehook__ return f def __exit__(self, *args): sys.stderr = self.old_stderr if hasattr(self, 'old_unraisablebook'): sys.unraisablehook = self.old_unraisablebook 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 pytest pytest.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 def _verify(ffi, module_name, preamble, *args, **kwds): from cffi.recompiler import recompile from .udir import udir 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 = 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 for name in dir(ffi): if not name.startswith('_') and not hasattr(module.ffi, name): setattr(ffi, name, NotImplemented) return module.lib # For testing, we call gcc with "-Werror". This is fragile because newer # versions of gcc are always better at producing warnings, particularly for # auto-generated code. We need here to adapt and silence them as needed. if sys.platform == 'win32': 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', '-Wno-unused-parameter', '-Wno-unreachable-code'] # special things for clang extra_compile_args.append('-Qunused-arguments') else: # assume a standard gcc extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', '-Wno-unused-parameter', '-Wno-unreachable-code'] is_musl = False if sys.platform == 'linux': try: from packaging.tags import platform_tags except ImportError: pass else: tagset = frozenset(platform_tags()) is_musl = any(t.startswith('musllinux') for t in tagset) if is_musl and sys.version_info >= (3, 12): if any(t.startswith('musllinux_1_1_') for t in tagset) and not any(t.startswith('musllinux_1_2_') for t in tagset): # gcc 9.2.0 in the musllinux_1_1 build container has a bug in its sign-conversion warning detection that # bombs on the definition of _PyLong_CompactValue under Python 3.12; disable warnings-as-errors for that # specific error on musl 1.1 extra_compile_args.append('-Wno-error=sign-conversion') del platform_tags ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1695922987.0 cffi-1.16.0/testing/udir.py0000644000175100001770000001153200000000000016310 0ustar00runnerdocker00000000000000import py import sys, os, atexit # This is copied from PyPy's vendored py lib. The latest py lib release # (1.8.1) contains a bug and crashes if it sees another temporary directory # in which we don't have write permission (e.g. because it's owned by someone # else). def make_numbered_dir(prefix='session-', rootdir=None, keep=3, lock_timeout = 172800, # two days min_timeout = 300): # five minutes """ return unique directory with a number greater than the current maximum one. The number is assumed to start directly after prefix. if keep is true directories with a number less than (maxnum-keep) will be removed. """ if rootdir is None: rootdir = py.path.local.get_temproot() def parse_num(path): """ parse the number out of a path (if it matches the prefix) """ bn = path.basename if bn.startswith(prefix): try: return int(bn[len(prefix):]) except ValueError: pass # compute the maximum number currently in use with the # prefix lastmax = None while True: maxnum = -1 for path in rootdir.listdir(): num = parse_num(path) if num is not None: maxnum = max(maxnum, num) # make the new directory try: udir = rootdir.mkdir(prefix + str(maxnum+1)) except py.error.EEXIST: # race condition: another thread/process created the dir # in the meantime. Try counting again if lastmax == maxnum: raise lastmax = maxnum continue break # put a .lock file in the new directory that will be removed at # process exit if lock_timeout: lockfile = udir.join('.lock') mypid = os.getpid() if hasattr(lockfile, 'mksymlinkto'): lockfile.mksymlinkto(str(mypid)) else: lockfile.write(str(mypid)) def try_remove_lockfile(): # in a fork() situation, only the last process should # remove the .lock, otherwise the other processes run the # risk of seeing their temporary dir disappear. For now # we remove the .lock in the parent only (i.e. we assume # that the children finish before the parent). if os.getpid() != mypid: return try: lockfile.remove() except py.error.Error: pass atexit.register(try_remove_lockfile) # prune old directories if keep: for path in rootdir.listdir(): num = parse_num(path) if num is not None and num <= (maxnum - keep): if min_timeout: # NB: doing this is needed to prevent (or reduce # a lot the chance of) the following situation: # 'keep+1' processes call make_numbered_dir() at # the same time, they create dirs, but then the # last process notices the first dir doesn't have # (yet) a .lock in it and kills it. try: t1 = path.lstat().mtime t2 = lockfile.lstat().mtime if abs(t2-t1) < min_timeout: continue # skip directories too recent except py.error.Error: continue # failure to get a time, better skip lf = path.join('.lock') try: t1 = lf.lstat().mtime t2 = lockfile.lstat().mtime if not lock_timeout or abs(t2-t1) < lock_timeout: continue # skip directories still locked except py.error.Error: pass # assume that it means that there is no 'lf' try: path.remove(rec=1) except KeyboardInterrupt: raise except: # this might be py.error.Error, WindowsError ... pass # make link... try: username = os.environ['USER'] #linux, et al except KeyError: try: username = os.environ['USERNAME'] #windows except KeyError: username = 'current' src = str(udir) dest = src[:src.rfind('-')] + '-' + username try: os.unlink(dest) except OSError: pass try: os.symlink(src, dest) except (OSError, AttributeError, NotImplementedError): pass return udir udir = make_numbered_dir(prefix = 'ffi-') # Windows-only workaround for some configurations: see # https://bugs.python.org/issue23246 (Python 2.7.9) if sys.platform == 'win32': try: import setuptools except ImportError: pass