├── .ci ├── README.md ├── build-cffi ├── build-cffi-windows ├── install-env └── install-env-windows ├── .github └── workflows │ └── build.yaml ├── .gitignore ├── .mailmap ├── CHANGELOG.md ├── COPYING.md ├── README.md ├── STATUS.md ├── cffi-lua-0.2.3-1.rockspec ├── docs ├── api.md ├── introduction.md ├── semantics.md └── syntax.md ├── luarocks └── build.sh ├── meson.build ├── meson_options.txt ├── src ├── ast.cc ├── ast.hh ├── ffi.cc ├── ffi.hh ├── ffilib.cc ├── lib.cc ├── lib.hh ├── libffi.hh ├── lua.cc ├── lua.hh ├── main.cc ├── parser.cc ├── parser.hh ├── platform.hh ├── util.cc └── util.hh ├── subprojects └── libffi.wrap └── tests ├── abi.lua ├── callbacks.lua ├── callconv.lua ├── cast.lua ├── cexpr.lua ├── copy_fill.lua ├── dump_string.lua ├── globals.lua ├── istype.lua ├── meson.build ├── metatype.lua ├── metatype54.lua ├── parameterized.lua ├── redef.lua ├── redir.lua ├── runner.lua ├── scalar.lua ├── simple.lua ├── simple_pass.lua ├── struct_array.lua ├── struct_pass.lua ├── struct_union_array_fields.lua ├── table_init.lua ├── testlib.cc ├── testlib.lua ├── unionval.lua └── variadic.lua /.ci/README.md: -------------------------------------------------------------------------------- 1 | # cffi-lua CI scripts 2 | 3 | These CI scripts are meant to provide a helper environment for continuous 4 | integration. They are written to be used only in the CI environment, not 5 | general purpose environments. 6 | -------------------------------------------------------------------------------- /.ci/build-cffi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | expected_triplet=$TARGET 4 | 5 | if [ -z "$expected_triplet" ]; then 6 | echo "ERROR: target triplet not provided!" 7 | exit 1 8 | fi 9 | 10 | current_triplet=`$CC -dumpmachine` 11 | 12 | if [ "$CC" = "clang" ]; then 13 | export CXX="clang++" 14 | else 15 | export CXX="g++" 16 | fi 17 | 18 | if [ "$TARGET" != "darwin" -a "$CC" != "clang" -a "$expected_triplet" != "$current_triplet" ]; then 19 | cross=yes 20 | export CC="${expected_triplet}-${CC}" 21 | export CXX="${expected_triplet}-${CXX}" 22 | export STRIP="${expected_triplet}-strip" 23 | export AR="${expected_triplet}-ar" 24 | export AS="${expected_triplet}-as" 25 | else 26 | export STRIP="strip" 27 | export AR="ar" 28 | fi 29 | 30 | extra_cflags="-fPIC" 31 | meson_system="linux" 32 | 33 | case "${expected_triplet}" in 34 | darwin) 35 | # special case here 36 | meson_system="darwin" 37 | ;; 38 | x86_64*) 39 | meson_cpu_family="x86_64" 40 | meson_cpu="x86_64" 41 | meson_endian="little" 42 | case "${expected_triplet}" in 43 | *w64*) 44 | meson_system="windows" 45 | extra_cflags="" 46 | ;; 47 | *) 48 | qemu_cpu="x86_64" 49 | ;; 50 | esac 51 | ;; 52 | i686*) 53 | meson_cpu_family="x86" 54 | meson_cpu="i686" 55 | meson_endian="little" 56 | qemu_cpu="i386" 57 | ;; 58 | powerpc64le*) 59 | meson_cpu_family="ppc64" 60 | meson_cpu="ppc64le" 61 | meson_endian="little" 62 | qemu_cpu="ppc64le" 63 | ;; 64 | powerpc64*) 65 | meson_cpu_family="ppc64" 66 | meson_cpu="ppc64" 67 | meson_endian="big" 68 | qemu_cpu="ppc64" 69 | ;; 70 | powerpcle*) 71 | echo "ERROR: ppcle not supported in qemu" 72 | exit 1 73 | ;; 74 | powerpc*) 75 | meson_cpu_family="ppc" 76 | meson_cpu="ppc" 77 | meson_endian="big" 78 | qemu_cpu="ppc" 79 | ;; 80 | aarch64-*) 81 | meson_cpu_family="aarch64" 82 | meson_cpu="aarch64" 83 | meson_endian="little" 84 | qemu_cpu="aarch64" 85 | ;; 86 | arm-*) 87 | meson_cpu_family="arm" 88 | meson_cpu="armv6l" 89 | meson_endian="little" 90 | qemu_cpu="arm" 91 | ;; 92 | riscv64-*) 93 | meson_cpu_family="riscv64" 94 | meson_cpu="riscv64" 95 | meson_endian="little" 96 | qemu_cpu="riscv64" 97 | ;; 98 | s390x*) 99 | meson_cpu_family="s390x" 100 | meson_cpu="s390x" 101 | meson_endian="big" 102 | qemu_cpu="s390x" 103 | ;; 104 | mips-*) 105 | meson_cpu_family="mips" 106 | meson_cpu="mips" 107 | meson_endian="big" 108 | qemu_cpu="mips" 109 | ;; 110 | m68k*) 111 | meson_cpu_family="m68k" 112 | meson_cpu="m68k" 113 | meson_endian="big" 114 | qemu_cpu="m68k" 115 | ;; 116 | *) 117 | echo "ERROR: Cross CPU unspecified" 118 | exit 1 119 | ;; 120 | esac 121 | 122 | export PATH="$(pwd)/host_tools:$PATH" 123 | 124 | if [ -n "$qemu_cpu" -a -n "$cross" ]; then 125 | echo ">> Preparing qemu..." 126 | # work around glibc being dumb 127 | # the cache format is not endian agnostic, so unless a dummy file exists 128 | # here, qemu will try to use host's and it will crash guest glibc on BE 129 | sudo mkdir -p /usr/${expected_triplet}/etc 130 | sudo touch /usr/${expected_triplet}/etc/ld.so.cache 131 | fi 132 | 133 | # sanitizer setup, need asan-instrumented lua 134 | if [ -n "$SANITIZE" ]; then 135 | extra_cflags="${extra_cflags} -fsanitize=address" 136 | extra_ldflags="${extra_ldflags} -fsanitize=address -fuse-ld=lld" 137 | # cfi-icall is buggy with the clang on CI, also the same functionality 138 | # is covered by -fsanitize=function (included in undefined) 139 | export CXXFLAGS="-fsanitize=undefined -fsanitize-trap=undefined -fsanitize=address -fsanitize=cfi -fno-sanitize=cfi-icall -fvisibility=hidden -flto=thin" 140 | export LDFLAGS="${CXXFLAGS} -fuse-ld=lld" 141 | # only test the most recent lua version 142 | LUA_VERSIONS=${LUA_VERSIONS%% *} 143 | fi 144 | 145 | echo ">> Building lua..." 146 | 147 | lua_plat=linux 148 | case "$TARGET" in 149 | darwin) lua_plat=macosx;; 150 | x86_64-w64*) lua_plat=mingw;; 151 | esac 152 | 153 | for luaver in ${LUA_VERSIONS}; do 154 | deps_dir="deps-${luaver}-${expected_triplet}" 155 | deps_path="$(pwd)/${deps_dir}" 156 | wine_pfx="$(pwd)/.wine" 157 | mkdir -p ${deps_dir}/include 158 | cd lua-${luaver} 159 | # drop unneeded functionality that'd be a hassle otherwise 160 | if [ "$lua_plat" = "linux" ]; then 161 | sed -i '/.*define LUA_USE_READLINE/d' src/luaconf.h 162 | sed -i 's/-lreadline//g' src/Makefile 163 | sed -i 's/-lhistory//g' src/Makefile 164 | sed -i 's/-lncurses//g' src/Makefile 165 | fi 166 | make -j8 PLAT=${lua_plat} MYCFLAGS="${extra_cflags}" MYLDFLAGS="${extra_ldflags}" CC="$CC" AR="$AR rcu" || exit 1 167 | cp src/lua*.h* ../${deps_dir}/include || exit 1 168 | cp src/lauxlib.h ../${deps_dir}/include || exit 1 169 | if [ "${meson_system}" = "windows" ]; then 170 | cp src/lua*.dll ../${deps_dir} || exit 1 171 | fi 172 | if [ -d "etc" -a -f "etc/lua.hpp" ]; then 173 | cp etc/lua.hpp ../${deps_dir}/include 174 | fi 175 | if [ -n "${cross}" ]; then 176 | if [ "${meson_system}" = "windows" ]; then 177 | cp src/lua.exe ../${deps_dir} || exit 1 178 | cat << EOF > ../${deps_dir}/lua 179 | #!/bin/sh 180 | export WINEDEBUG=-all 181 | export WINEPREFIX="${wine_pfx}" 182 | export DISPLAY= 183 | wine ${deps_path}/lua.exe "\$@" 184 | EOF 185 | else 186 | cp src/lua ../${deps_dir}/lua.target || exit 1 187 | cat << EOF > ../${deps_dir}/lua 188 | #!/bin/sh 189 | qemu-${qemu_cpu} -L /usr/${expected_triplet} ${deps_path}/lua.target "\$@" 190 | EOF 191 | fi 192 | chmod +x ../${deps_dir}/lua 193 | echo ">> Testing cross lua:" 194 | ../${deps_dir}/lua -v || exit 1 195 | else 196 | cp src/lua ../${deps_dir} || exit 1 197 | fi 198 | cd .. 199 | done 200 | 201 | echo ">> Building and testing cffi..." 202 | 203 | # lsan seems broken in the ci env? 204 | export LSAN_OPTIONS=detect_leaks=0 205 | 206 | for luaver in ${LUA_VERSIONS}; do 207 | deps_dir="deps-${luaver}-${expected_triplet}" 208 | lua_path="$(pwd)/${deps_dir}/lua" 209 | 210 | mkdir -p build-${luaver}-${expected_triplet} 211 | cd build-${luaver}-${expected_triplet} 212 | 213 | args="" 214 | if [ -n "${cross}" ]; then 215 | cat << EOF > crossfile 216 | [binaries] 217 | c = '${CC}' 218 | cpp = '${CXX}' 219 | strip = '${STRIP}' 220 | 221 | [host_machine] 222 | system = '${meson_system}' 223 | cpu_family = '${meson_cpu_family}' 224 | cpu = '${meson_cpu}' 225 | endian = '${meson_endian}' 226 | EOF 227 | args="${args} --cross-file=crossfile" 228 | fi 229 | if [ -n "$BUILDTYPE" ]; then 230 | args="${args} --buildtype=$BUILDTYPE" 231 | fi 232 | # needed for CFI and -fsanitize=function 233 | if [ -n "$SANITIZE" ]; then 234 | args="${args} -Db_lto=true -Dcpp_rtti=true" 235 | fi 236 | 237 | meson .. -Dlua_version=vendor -Dtests_cross=true \ 238 | -Ddeps_dir=${deps_dir} -Dlua_path="${lua_path}" ${args} || exit 1 239 | ninja all --verbose || exit 1 240 | ninja test || exit 1 241 | cd .. 242 | done 243 | 244 | exit 0 245 | -------------------------------------------------------------------------------- /.ci/build-cffi-windows: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | unset CC CXX CC_FOR_BUILD CXX_FOR_BUILD 4 | 5 | export PATH="$(pwd)/host_tools:$PATH" 6 | 7 | echo ">> Setting up lua..." 8 | 9 | for luaver in ${LUA_VERSIONS}; do 10 | mv lua-${luaver} deps-${luaver} || exit 1 11 | done 12 | 13 | echo ">> Building and testing cffi..." 14 | 15 | args="" 16 | if [ -n "$BUILDTYPE" ]; then 17 | args="${args} --buildtype=$BUILDTYPE" 18 | fi 19 | 20 | for luaver in ${LUA_VERSIONS}; do 21 | mkdir -p build-${luaver} 22 | cd build-${luaver} 23 | 24 | cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat' amd64 '&&' \ 25 | meson .. -Dlua_version=vendor -Ddeps_dir=deps-${luaver} ${args} '&&' \ 26 | ninja all '&&' ninja test || exit 1 27 | 28 | cd .. 29 | done 30 | 31 | exit 0 32 | -------------------------------------------------------------------------------- /.ci/install-env: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | expected_triplet=$TARGET 4 | 5 | if [ -z "$expected_triplet" ]; then 6 | echo "ERROR: target triplet not provided!" 7 | exit 1 8 | fi 9 | 10 | ensure_tool() { 11 | command -v "$1" > /dev/null 12 | if [ $? -ne 0 ]; then 13 | echo "ERROR: Missing tool: $1" 14 | exit 1 15 | fi 16 | } 17 | 18 | if [ "$(uname -s)" = "Linux" ]; then 19 | is_linux=yes 20 | fi 21 | 22 | echo ">> Checking tools..." 23 | 24 | ensure_tool gcc 25 | ensure_tool g++ 26 | ensure_tool clang 27 | ensure_tool clang++ 28 | ensure_tool git 29 | ensure_tool tar 30 | ensure_tool wget 31 | ensure_tool make 32 | 33 | mkdir -p host_tools 34 | 35 | echo ">> Updating package database..." 36 | 37 | [ -n "$is_linux" ] && sudo apt-get update 38 | 39 | echo ">> Installing meson..." 40 | 41 | if [ -n "$is_linux" ]; then 42 | sudo apt-get install ninja-build 43 | else 44 | ninja_version=1.10.2 45 | cd host_tools 46 | wget "https://github.com/ninja-build/ninja/releases/download/v${ninja_version}/ninja-mac.zip" || exit 1 47 | tar xf ninja-mac.zip || exit 1 48 | rm ninja-mac.zip 49 | cd .. 50 | export PATH="$(pwd)/host_tools:$PATH" 51 | fi 52 | 53 | if [ -n "$(command -v pip3)" ]; then 54 | sudo pip3 install --break-system-packages meson || sudo pip3 install meson || exit 1 55 | elif [ -n "$(command -v pip)" ]; then 56 | sudo pip install --break-system-packages meson || sudo pip install meson || exit 1 57 | else 58 | echo "ERROR: pip not found" 59 | exit 1 60 | fi 61 | 62 | ensure_tool meson 63 | ensure_tool ninja 64 | 65 | echo ">> Getting lua..." 66 | 67 | for luaver in ${LUA_VERSIONS}; do 68 | wget "https://www.lua.org/ftp/lua-${luaver}.tar.gz" || exit 1 69 | tar xf lua-${luaver}.tar.gz || exit 1 70 | done 71 | 72 | if [ "$(uname -s)" != "Linux" ]; then 73 | exit 0 74 | fi 75 | 76 | current_triplet=`gcc -dumpmachine` 77 | 78 | if [ -z "$current_triplet" ]; then 79 | echo "ERROR: Native compiler not present!" 80 | exit 1 81 | fi 82 | 83 | if [ "$expected_triplet" = "$current_triplet" ]; then 84 | exit 0 85 | fi 86 | 87 | echo ">> Installing toolchain..." 88 | 89 | if [ "$expected_triplet" = "x86_64-w64-mingw32" ]; then 90 | gcc_suffix="mingw-w64" 91 | extra_packages="wine" 92 | else 93 | gcc_suffix="${expected_triplet}" 94 | extra_packages="qemu-user" 95 | fi 96 | 97 | sudo apt-get install gcc-${gcc_suffix} g++-${gcc_suffix} ${extra_packages} || exit 1 98 | 99 | exit $? 100 | -------------------------------------------------------------------------------- /.ci/install-env-windows: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ninja_version=1.10.2 4 | 5 | echo ">> Installing meson..." 6 | 7 | mkdir -p host_tools 8 | 9 | curl -L -o ninja.zip https://github.com/ninja-build/ninja/releases/download/v${ninja_version}/ninja-win.zip || exit 1 10 | 7z x ninja.zip || exit 1 11 | mv ninja.exe host_tools 12 | 13 | pip3 install meson 14 | 15 | echo ">> Getting lua..." 16 | 17 | lua_build="Win64_dll16_lib" 18 | lua_bin="Win64_bin" 19 | 20 | for luaver in ${LUA_VERSIONS}; do 21 | lua_dmaj=${luaver:0:3} 22 | lua_maj=${lua_dmaj/./} 23 | # lua from luabinaries 24 | curl -L -o lua-${luaver}-bin.zip https://downloads.sourceforge.net/project/luabinaries/${luaver}/Tools%20Executables/lua-${luaver}_${lua_bin}.zip || exit 1 25 | curl -L -o lua-${luaver}-lib.zip https://downloads.sourceforge.net/project/luabinaries/${luaver}/Windows%20Libraries/Dynamic/lua-${luaver}_${lua_build}.zip || exit 1 26 | mkdir lua-${luaver} 27 | cd lua-${luaver} 28 | # unzip bin first 29 | 7z x ../lua-${luaver}-bin.zip || exit 1 30 | 7z x ../lua-${luaver}-lib.zip -y || exit 1 31 | cd .. 32 | done 33 | 34 | exit 0 35 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | 5 | jobs: 6 | linux: 7 | name: Linux 8 | runs-on: ubuntu-22.04 9 | 10 | env: 11 | TARGET: '${{ matrix.config.target }}' 12 | LUA_VERSIONS: '5.4.4 5.3.6 5.2.4 5.1.5' 13 | CC: '${{ matrix.config.cc }}' 14 | BUILDTYPE: '${{ matrix.config.buildtype }}' 15 | SANITIZE: '${{ matrix.config.sanitize }}' 16 | 17 | strategy: 18 | matrix: 19 | config: 20 | # x86_64: test gcc, clang, + release mode to catch assert bugs 21 | - { target: x86_64-linux-gnu, cc: gcc, buildtype: debugoptimized } 22 | - { target: x86_64-linux-gnu, cc: gcc, buildtype: release } 23 | - { target: x86_64-linux-gnu, cc: clang, buildtype: debugoptimized } 24 | - { target: x86_64-linux-gnu, cc: clang, buildtype: debugoptimized, sanitize: 1 } 25 | # 32-bit x86 26 | - { target: i686-linux-gnu, cc: gcc, buildtype: debugoptimized } 27 | # all powerpc 28 | - { target: powerpc64le-linux-gnu, cc: gcc, buildtype: debugoptimized } 29 | - { target: powerpc64-linux-gnu, cc: gcc, buildtype: debugoptimized } 30 | - { target: powerpc-linux-gnu, cc: gcc, buildtype: debugoptimized } 31 | # aarch64 and arm 32 | - { target: aarch64-linux-gnu, cc: gcc, buildtype: debugoptimized } 33 | - { target: arm-linux-gnueabi, cc: gcc, buildtype: debugoptimized } 34 | # riscv64 and s390x 35 | - { target: riscv64-linux-gnu, cc: gcc, buildtype: debugoptimized } 36 | - { target: s390x-linux-gnu, cc: gcc, buildtype: debugoptimized } 37 | # mips, m68k 38 | # on m68k, building with optimizations results in some broken tests, 39 | # notably returning a struct with a single float inside by value 40 | # this is not our bug, a standalone testcase reproduces it 41 | - { target: mips-linux-gnu, cc: gcc, buildtype: debugoptimized } 42 | # m68k qemu segfaults on 22.04 43 | #- { target: m68k-linux-gnu, cc: gcc, buildtype: debug } 44 | # x86_64 windows cross, release mode 45 | - { target: x86_64-w64-mingw32, cc: gcc, buildtype: release } 46 | 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@v2 50 | with: 51 | persist-credentials: false 52 | 53 | - name: Prepare environment 54 | run: sh ./.ci/install-env 55 | 56 | - name: Build and test cffi 57 | run: sh ./.ci/build-cffi 58 | 59 | windows: 60 | name: Windows 61 | runs-on: windows-2019 62 | 63 | env: 64 | LUA_VERSIONS: '5.4.2 5.3.6 5.2.4 5.1.5' 65 | 66 | steps: 67 | - name: Checkout 68 | uses: actions/checkout@v2 69 | with: 70 | persist-credentials: false 71 | 72 | - name: Prepare environment 73 | run: bash ./.ci/install-env-windows 74 | 75 | - name: Build and test cffi 76 | run: bash ./.ci/build-cffi-windows 77 | 78 | mac: 79 | name: MacOS 80 | runs-on: macos-latest 81 | 82 | env: 83 | TARGET: 'darwin' 84 | LUA_VERSIONS: '5.4.2 5.3.6 5.2.4 5.1.5' 85 | CC: 'clang' 86 | 87 | steps: 88 | - name: Checkout 89 | uses: actions/checkout@v2 90 | with: 91 | persist-credentials: false 92 | 93 | - name: Prepare environment 94 | run: sh ./.ci/install-env 95 | 96 | - name: Build and test cffi 97 | run: sh ./.ci/build-cffi 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | subprojects/libffi 3 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # add yourself here if name/email changes 2 | # 3 | # format: 4 | # 5 | # propername commitname 6 | 7 | q66 Daniel Kolesa 8 | q66 Daniel Kolesa 9 | q66 Daniel Kolesa 10 | q66 q66 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 8 January 2023 - 0.2.3 2 | 3 | **Changes:** 4 | 5 | - Fixed initialization and sizeof for flexible structs 6 | - Fixed handling of pointer arithmetic with reference types 7 | - Fixed handling of references in cffi.copy and other places 8 | - Reworked module memory management to respect type alignment 9 | - Added a Clang build with sanitizers (UBSan, ASan, CFI) in CI 10 | - Fixed issues found by sanitized build 11 | - Updated CI to Ubuntu 22.04 12 | 13 | # 1 November 2022 - 0.2.2 14 | 15 | **Changes:** 16 | 17 | - Passing unions by value is now permitted on specific platforms: 18 | - Windows (x86 and x86_64), MIPS32/64, all 32-bit ARM, PowerPC 19 | - Initializer fixes in `ffi.new` 20 | - Fixed unary metamethods on newer versions of Lua 21 | - Expanded architecture identifier coverage 22 | - CI updates 23 | - Various fixes and cleanups 24 | 25 | # 31 January 2021 - 0.2.1 26 | 27 | **Changes:** 28 | 29 | - Fixed multidimensional arrays in structs 30 | - Fixed release builds with `NDEBUG` 31 | - CI updates 32 | 33 | # 29 November 2020 - 0.2.0 34 | 35 | **Changes:** 36 | 37 | - No longer using the C++ standard library, exceptions, RTTI and TLS 38 | - Lower resource utilization, simpler memory management 39 | - Constant expressions now properly handle errors 40 | - A large amount of fixes (unhandled cases, LuaJIT FFI compatibility, etc.) 41 | - Greatly expanded test suite 42 | - Removed custom test runner (using just Lua) 43 | 44 | **Requirements:** 45 | 46 | - No changes 47 | 48 | # 01 August 2020 - 0.1.1 49 | 50 | **Changes:** 51 | 52 | - Build system enhancements 53 | - CI enhancements 54 | - We no longer ship a library/API (except a static library for embedded uses) 55 | - Initial LuaRocks support (Windows support pending) 56 | - Added option to disable tests 57 | 58 | **Requirements:** 59 | 60 | - No changes 61 | 62 | **Library clarification:** 63 | 64 | By default, you will only get a Lua module now. You can pass `-Dstatic=true` 65 | to `meson` to switch the build to a static library; you will then get that 66 | instead of a module. This is intended for systems that don't have Lua module 67 | support - there you will want to link the FFI into the application instead. 68 | 69 | It is not possible to build the static library and the module at the same 70 | time, and usually the recommended thing is to build the module. 71 | 72 | # 26 July 2020 - 0.1.0 - Initial release 73 | 74 | This is the initial release of the `cffi` module. It is of beta quality. 75 | See `STATUS.md` for status and `README.md` for building and usage. 76 | 77 | Production usage is currently not recommended, since the module is not 78 | battle tested. Testing is highly encouraged. 79 | 80 | **Requirements:** 81 | 82 | - Lua 5.1 - 5.4 83 | - `libffi` 84 | - `meson` 0.50+, optional `pkg-config` 85 | - GCC 7+, Clang 8+ or Visual Studio 2017+ with updates recommended 86 | - Theoretical minimum is GCC 4.8, Clang 3.4 or any compiler with enough C++14 87 | -------------------------------------------------------------------------------- /COPYING.md: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | 3 | Copyright (c) 2020-2025 q66 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cffi-lua 2 | 3 | [![Build Status](https://github.com/q66/cffi-lua/actions/workflows/build.yaml/badge.svg)](https://github.com/q66/cffi-lua/actions) 4 | 5 | This is a portable C FFI for Lua, based on `libffi` and aiming to be mostly 6 | compatible with LuaJIT FFI, but written from scratch. Compatibility is 7 | preserved where reasonable, but not where not easily implementable (e.g. 8 | the parser extensions for 64-bit `cdata` and so on). Thanks to `libffi`, 9 | it works on many operating systems and CPU architectures. The `cffi-lua` 10 | codebase itself does not contain any non-portable code (with the exception 11 | of things such as Windows calling convention handling on x86, and some 12 | adjustments for big endian architectures). Some effort was also taken to 13 | ensure compatibility with custom Lua configurations (e.g. with changed 14 | numeric type representations), though this is not tested or guaranteed 15 | to work (patches welcome if broken). 16 | 17 | Unlike LuaJIT's `ffi` module or other efforts such as `luaffifb`, it works 18 | with every common version of the reference Lua implementation (currently 5.1, 19 | 5.2, 5.3 and 5.4, 5.0 could be supported but wasn't considered worth it) as 20 | well as compatible non-reference ones (like LuaJIT). Functionality from newer 21 | Lua versions is also supported, when used with that version (e.g. with 5.3+ 22 | you will get seamless integer and bit-op support, with 5.4 you will get 23 | metatype support for to-be-closed variables, and so on). 24 | 25 | Since it's written from scratch, having 1:1 bug-for-bug C parser compatibility 26 | is a non-goal. The parser is meant to comply with C11, plus a number of 27 | extensions from GCC, MSVC and C++ (where it doesn't conflict with C). 28 | 29 | The project was started because there isn't any FFI for standard Lua that's 30 | as user friendly as LuaJIT's and doesn't have portability issues. 31 | 32 | ## Current status 33 | 34 | See `STATUS.md`. 35 | 36 | ## Notable differences from LuaJIT 37 | 38 | - Equality comparisons against `nil` always result in `false` 39 | - Equality comparisons between `cdata` and Lua values are always `false` 40 | - Passing unions (or structs containing unions) is not supported on all platforms 41 | - Bitfields are not supported 42 | - Several new API extensions 43 | 44 | Equality comparions work this way due to limitations of the Lua metamethod 45 | semantics. Use `cffi.nullptr` instead. The other limitations are caused by 46 | `libffi` not supporting these features portably. 47 | 48 | ## Dependencies 49 | 50 | The dependencies are kept intentionally minimal. 51 | 52 | - A C++ compiler supporting the right subset of C++14 53 | - Lua 5.1 or newer (tested up to and including 5.4) or equivalent (e.g. LuaJIT) 54 | - `libffi` (built with `meson` subproject if missing) 55 | - `meson` 56 | 57 | Optional dependencies: 58 | 59 | - `pkg-config` (for automated Lua finding) 60 | - A Lua executable (only for tests) 61 | 62 | These toolchains have been tested: 63 | 64 | - GCC 7+ (all platforms) 65 | - Clang 8+ (all platforms) 66 | - Visual Studio 2017+ (with updates) 67 | 68 | Other toolchains may also work. The theoretical minimum is GCC 4.8 and 69 | Clang 3.4 (an updated VS 2017 is already the minimum, older versions are 70 | missing necessary language features). It is ensured that no non-standard 71 | extensions are used, so as long as your compiler is C++14 compliant, it 72 | should work (technically there are some GCC/Clang/MSVC-specific diagnostic 73 | pragmas used, but these are conditional and only used to control warnings). 74 | 75 | The module should work on any CPU architecture supported by `libffi`. The CI 76 | system tests a large variety of CPU architectures (see `STATUS.md`). If you 77 | encounter any issues on yours, please send patches or at least report them 78 | so they can be fixed. 79 | 80 | The `pkg-config` tool is optional when using `-Dlua_version=custom` and 81 | vendored `libffi` (through build options or subproject). However, for 82 | `custom` libffi, you will need to manually specify what to include and link. 83 | 84 | ## Building 85 | 86 | On Unix-like systems: 87 | 88 | ``` 89 | $ mkdir build 90 | $ cd build 91 | $ meson .. 92 | $ ninja all 93 | ``` 94 | 95 | This will configure the module for the default Lua version in your system. 96 | If your system does not provide a default `lua.pc` `pkg-config` file, you 97 | will need to explicitly set the version with e.g. `-Dlua_version=5.2` 98 | passed to `meson`. You will also need to do this if you with to compile 99 | for a different Lua version than your default `lua.pc` provides. 100 | 101 | By default, a Lua loadable module will be built. This module can be installed 102 | in a path that Lua expects. On Unix-like systems, the `ninja install` target 103 | can do that. 104 | 105 | There is also an option to build a static library, by passing `-Dstatic=true` 106 | to `meson`. This is mainly intended for either application usages that will 107 | embed the FFI, or for various specialized platforms that do not support 108 | shared libraries or don't have a version of Lua configured to support modules. 109 | This version is usually not meant to be distributed. To use the static version, 110 | you will need to declare the `luaopen_cffi` symbol with the usual Lua function 111 | signature, `lua_pushcfunction` it on the stack and for example store it in 112 | `package.preload`. 113 | 114 | You can also pass `luajit` to `-Dlua_version` to build against LuaJIT (it 115 | will use `luajit.pc` then). Additionally, if you have a different Lua 116 | implementation than that but it still provides the same compliant API, 117 | you can bypass the check with `-Dlua_version=custom` and then provide 118 | the appropriate include path and linkage via `CXXFLAGS` and `LDFLAGS`. 119 | 120 | It is also possible to pass `-Dlua_version=vendor`, in which case the 121 | library will be taken from `deps` and the includes from `deps/include`. 122 | The `deps` directory can be either in the source root or in the directory 123 | you run `meson` from. 124 | 125 | Keep in mind that on Unix-likes, it is not necessary to actually link against 126 | the Lua library. Even when using `pkg-config`, the build system will always 127 | remove the linkage. The Lua symbols are instead supplied to the module 128 | through the executable it's loaded from. This does not work on Windows, 129 | where you actually need to link against the DLL for Lua modules to work. 130 | 131 | When using `homebrew` on macOS, its `libffi` is not installed globally. 132 | Therefore, you will need to set your `PKG_CONFIG_PATH` so that `pkg-config` 133 | can find its `.pc` file. 134 | 135 | You can also use `-Dlibffi=custom` if you wish to completely override what 136 | `libffi` is used. In that case you will need to provide the right include 137 | path in `CXXFLAGS` so that either `` or `` can be included, 138 | plus linkage in `LDFLAGS`. 139 | 140 | When `libffi` cannot be found in the system and you have not overridden how 141 | it is supplied, a Meson subproject will be automatically used (and `libffi` 142 | will be statically linked into the module). 143 | 144 | It is also possible to pass `-Dlua_install_path=...` to override where the 145 | Lua module will be installed. See below for that. 146 | 147 | The `shared_libffi` option will make libffi provide `dllimport`-decorated APIs 148 | on Windows; for Lua this is the default as there is always a DLL. On other 149 | systems, it does nothing. This is not strictly necessary, but it will make 150 | things faster when you're really using dynamic versions of those, and it's not 151 | possible to autodetect. Usually, you should be using static libffi on Windows. 152 | 153 | ### Windows and MSVC style environment 154 | 155 | To build on Windows with an MSVC-style toolchain, first get yourself the right 156 | version of Lua and optionally a binary distribution of `libffi`. They must be 157 | compatible with the runtime you're targeting. 158 | 159 | Drop the `.lib` files (import lib for Lua, optionally static or import lib for 160 | `libffi`) in the `deps` directory (either in the source root or the directory 161 | you are running `meson` from). The naming is up to you, `meson` will accept 162 | library names with or without `lib` prefix, and the build system accepts both 163 | unversioned and versioned to cover all environments. Usually, for Lua you will 164 | have something like `lua53.lib`. Also, if providing your own `libffi`, drop the 165 | include files (`ffi.h` and `ffitarget.h`) into `deps/include`, same with the Lua 166 | include files. 167 | 168 | It is recommended that you always use a static library for `libffi` if providing 169 | one. 170 | 171 | Drop any `.dll` files in the `deps` directory also. This would be the Lua 172 | dll file typically (e.g. `lua53.dll`). 173 | 174 | If you wish to run tests, also drop in the Lua executable, following the 175 | same naming scheme as the `.dll` file (or simply called `lua.exe`). If 176 | you don't do that, you will need to pass `-Dtests=false` to `meson` as well. 177 | 178 | Afterwards, run `meson` from the `build` directory (create it), like this: 179 | 180 | ``` 181 | meson .. -Dlua_version=vendor 182 | ``` 183 | 184 | Add `-Dlibffi=vendor` if providing a `libffi`. 185 | 186 | Then proceed with the usual: 187 | 188 | ``` 189 | ninja all 190 | ninja test 191 | ``` 192 | 193 | Examples of such environment are the Visual Studio environment itself and 194 | also Clang for Windows by default. 195 | 196 | ### Windows and MinGW/MSYS style environment 197 | 198 | This environment is Unix-like, so install the necessary dependencies as you 199 | would on Linux. In an MSYS2 environment, this would be something like: 200 | 201 | ``` 202 | pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-pkg-config 203 | pacman -S mingw-w64-x86_64-meson 204 | pacman -S mingw-w64-x86_64-lua 205 | ``` 206 | 207 | Particularly for MSYS2, you should use dependencies from just one repo, 208 | as e.g. `meson` installed from the MSYS2 repo won't detect `mingw-w64` 209 | libraries and so on. 210 | 211 | After that, proceed as you would on Linux: 212 | 213 | ``` 214 | meson .. -Dlua_version=5.3 215 | ``` 216 | 217 | You might also want to provide `-static-libgcc -static-libstdc++` in `LDFLAGS` 218 | if you wish to distribute the resulting module/library, otherwise they will 219 | carry dependencies the `libgcc` and `libstdc++-6` DLLs. 220 | 221 | Compile and test with: 222 | 223 | ``` 224 | ninja all 225 | ninja test 226 | ``` 227 | 228 | For plain MinGW, this will be similar, except you will need to manually provide 229 | your the dependencies. 230 | 231 | ## Installing 232 | 233 | ``` 234 | $ ninja install 235 | ``` 236 | 237 | This will install either the module or the static library depending on how 238 | you have configured the build. 239 | 240 | By default, the Lua module will install in `$(libdir)/lua/$(luaver)`, e.g. 241 | `/usr/lib/lua/5.2`. This is the default for most Lua installations. You can 242 | override that with `-Dlua_install_path=...`. The path is the entire 243 | installation path. You can insert `@0@` in it, which will be replaced with 244 | the Lua version you're building for (e.g. `5.2`). No other substitutions are 245 | performed. 246 | 247 | The goal of this is to make sure the module will be installed in a location 248 | contained in your Lua's `package.cpath`. 249 | 250 | ## Testing 251 | 252 | The module uses a native Lua executable to run tests. Since by default tests 253 | are enabled, the build system will search for the executable. If your copy of 254 | Lua is in a non-standard path, you can use `-Dlua_path=...` when configuring 255 | to explicitly specify where the executable is stored. 256 | 257 | Tests are only runnable when all of the following is met: 258 | 259 | - You are not cross-compiling 260 | - You are doing a module build (i.e. not `-Dstatic=true`) 261 | - The Lua executable matches the language version you are building for 262 | 263 | Either way, you can run tests with the following: 264 | 265 | ``` 266 | $ ninja test 267 | ``` 268 | 269 | You can see the available test cases in `tests`, they also serve as examples. 270 | 271 | Some of the tests only work if `cffi` is built with working `cffi.load`. This 272 | is nearly always true when you are building a module, since `cffi` supports 273 | more targets than Lua itself with module loading. 274 | 275 | You can also run the individual test cases standalone, like this: 276 | 277 | ``` 278 | $ lua path/to/cffi/tests/runner.lua path/to/test/case.lua 279 | ``` 280 | 281 | The environment variable `TESTS_PATH` can be used to manually specify the tests 282 | directory. Usually this is not necessary as it's automatically figured out. 283 | 284 | You can also specify `CFFI_PATH` as the path where `cffi.so` or `.dll` is stored. 285 | By default, it is assumed default `package.cpath` contains it somewhere. 286 | 287 | Additionally, `TESTLIB_PATH` should be specified as a path to the test support 288 | library. This library contains utilities used by some of the tests, like various 289 | native calls and global symbols. By default, it is stored in `build/tests`. 290 | If you do not specify this, tests requiring it will not run. 291 | 292 | Test cases ordinarily do not print anything to standard output or error. If the 293 | return code is `0`, the test has succeeded. If it is `77`, the test was skipped, 294 | e.g. because of the testlib not being found. In case of hard failures, an 295 | assertion error will be raised. 296 | -------------------------------------------------------------------------------- /STATUS.md: -------------------------------------------------------------------------------- 1 | # Current status 2 | 3 | Currently, the FFI supports most features one would expect it to support, 4 | notably: 5 | 6 | - Near complete C11 declarations syntax, plus the following extensions: 7 | - Unnamed parameters 8 | - Opaque `enum` declarations 9 | - Transparent `struct` inside `struct` or `union` 10 | - `__const__`, `__volatile__` 11 | - C++ references 12 | - `__alignof__` 13 | - Zero sized arrays, structs and unions 14 | - `__asm__("symbol")` (redirection) 15 | - `__cdecl`, `__fastcall`, `__stdcall`, `__thiscall` 16 | - `__attribute__` with: `cdecl`, `fastcall`, `stdcall`, `thiscall` 17 | - Empty argument list is treated like C++, i.e. `void foo();` has no args 18 | - All API supported by LuaJIT FFI, plus the following extensions: 19 | - `cffi.addressof` (like C++ `&`: `T` or `T &` becomes `T *`) 20 | - `cffi.toretval` (cdata -> Lua return value conversion) 21 | - `cffi.eval` (constant expression -> cdata) 22 | - `cffi.nullptr` (a `NULL` pointer constant for comparisons) 23 | - `cffi.tonumber` (`cdata`-aware `tonumber`) 24 | - `cffi.type` (`cdata`-aware `type`) 25 | - Semantics generally follow LuaJIT closely, with these exceptions: 26 | - All metamethods of the respective Lua version are respected 27 | - Lua integers are supported (and used) when using Lua 5.3 or newer 28 | - Windows `__stdcall` functions must be explicitly marked as such 29 | - Equality comparisons of `cdata` and `nil` do not work (use `cffi.nullptr`) 30 | - This is a limitation of the Lua metamethod semantics 31 | - You can reassign the values of fields of a `metatype` 32 | - Which fields are used is decided at `cffi.metatype` call time, though 33 | - `cffi.gc` can be used with any `cdata` 34 | - Callbacks are currently unrestricted (no limit, no handle reuse) 35 | - This may change in the future, so do not rely on it 36 | 37 | ## Passing unions by value 38 | 39 | Since `libffi` does not have reliable support for `union`s, `cffi` does not 40 | support it for all targets, only those that are explicitly handled. Currently 41 | this includes: 42 | 43 | - 32-bit x86 (all) 44 | - 64-bit x86 (Windows) 45 | - 32-bit ARM (all) 46 | - 32-bit PowerPC 47 | - MIPS(64) (all) 48 | 49 | Every other target forbids this. Both passing and returning is forbidden, and 50 | structs containing unions are likewise not allowed. Keep in mind that this 51 | only applies to parameter passing and returning by value; you can pass them by 52 | pointer everywhere, as well as allocate them. 53 | 54 | The system will not let you declare such functions on unsupported targets. 55 | 56 | The `cffi.abi("unionval")` can be used to check the support from Lua in a generic 57 | manner. 58 | 59 | ## Missing features 60 | 61 | There are some things notably missing at this point. 62 | 63 | ### Supported by LuaJIT 64 | 65 | - Syntax: 66 | - Transparent `enum` inside `struct` (non-standard extension) 67 | - `static const` declarations inside `struct`/`union` (C++ extension) 68 | - Bitfields (`libffi` limitation) 69 | - Complex types (`complex`, `_Complex`, `complex double`, `complex float`) 70 | - `alignas` (and `_Alignas`) 71 | - Vector types (GCC extension) 72 | - `__attribute__` with `packed`, `vector_size`, `aligned`, `mode` (GCC extension) 73 | - `__extension__` (GCC extension) 74 | - `__declspec(align(n))` (MSVC extension) 75 | - `__ptr32`, `__ptr64` (MSVC extension) 76 | - `#pragma pack` (MSVC extension) 77 | - Passing `union` by value is not supported everywhere 78 | - `__stdcall` on Windows is not auto-guessed and must be tagged explicitly 79 | 80 | ### Not supported by LuaJIT 81 | 82 | - Non-integer support in constant expressions 83 | - Builtin C preprocessor 84 | 85 | ### Internals 86 | 87 | - Type system cleanup/rework 88 | - Performance optimizations 89 | - Resource usage optimizations 90 | 91 | ## Platform support 92 | 93 | The project supports every system and architecture that has a `libffi` 94 | implementation and a C++14 compiler. We have a continuous integration 95 | setup that tests multiple architectures on Linux as well as `x86_64` 96 | macOS and Windows. 97 | 98 | In addition to supporting every Lua version starting with 5.1, effort is 99 | made when it comes to supporting unusual Lua configurations (such as ones 100 | with differently configured numeric types). This is however not tested, so 101 | if you run into issues, feel free to provide a patch. 102 | 103 | The CI matrix currently includes the following: 104 | 105 | | Architecture | Word size | Endianness | OS | Compiler | Build type | 106 | |--------------|-----------|------------|---------|----------|------------------| 107 | | `x86_64` | 64 | little | Linux | `gcc` | `debugoptimized` | 108 | | `x86_64` | 64 | little | Linux | `gcc` | `release` | 109 | | `x86_64` | 64 | little | Linux | `clang` | `debugoptimized` | 110 | | `x86_64` | 64 | little | Windows | `gcc` | `release` | 111 | | `x86_64` | 64 | little | Windows | `cl.exe` | `debugoptimized` | 112 | | `x86_64` | 64 | little | MacOS | `clang` | `debugoptimized` | 113 | | `ppc64le` | 64 | little | Linux | `gcc` | `debugoptimized` | 114 | | `ppc64` | 64 | big | Linux | `gcc` | `debugoptimized` | 115 | | `aarch64` | 64 | little | Linux | `gcc` | `debugoptimized` | 116 | | `riscv64` | 64 | little | Linux | `gcc` | `debugoptimized` | 117 | | `s390x` | 64 | big | Linux | `gcc` | `debugoptimized` | 118 | | `i686` | 32 | little | Linux | `gcc` | `debugoptimized` | 119 | | `mips` | 32 | big | Linux | `gcc` | `debugoptimized` | 120 | | `ppc` | 32 | big | Linux | `gcc` | `debugoptimized` | 121 | | `armv6l` | 32 | little | Linux | `gcc` | `debugoptimized` | 122 | 123 | Note: `ppc64` is ABIv1. 124 | 125 | Clang CI builds on `x86_64` Linux also build with sanitizers (ASan, UBSan, CFI). 126 | 127 | This is not an exhaustive list of supported targets but rather just a limited 128 | list intended to cover everything (64-bit systems, 32-bit systems, little and 129 | big endian, Linux, macOS and Windows, and release build to catch assertion 130 | related bugs). 131 | -------------------------------------------------------------------------------- /cffi-lua-0.2.3-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "cffi-lua" 2 | version = "0.2.3-1" 3 | source = { 4 | url = "git+https://github.com/q66/cffi-lua", 5 | tag = "v0.2.3" 6 | } 7 | description = { 8 | summary = "A portable C FFI for Lua 5.1+", 9 | detailed = [[ 10 | This is a portable C FFI for Lua, based on libffi. It aims to be 11 | mostly compatible with the LuaJIT FFI, but written from scratch 12 | and compatible with different systems and CPU architectures. 13 | 14 | It doesn't aim to provide strictly only what LuaJIT FFI provides; 15 | there is also support for features from newer Lua versions as well 16 | as various other extensions, both in its API and in its language 17 | support. 18 | ]], 19 | homepage = "https://github.com/q66/cffi-lua", 20 | license = "MIT" 21 | } 22 | dependencies = { 23 | "lua >= 5.1" 24 | } 25 | external_dependencies = { 26 | MESON = { program = "meson" }, 27 | } 28 | build = { 29 | type = "command", 30 | build_command = [[\ 31 | LUA="$(LUA)" CC="$(CC)" LD="$(LD)" \ 32 | CFLAGS='$(CFLAGS) -I$(LUA_INCDIR)' \ 33 | PREFIX="$(PREFIX)" LIBDIR="$(LIBDIR)" \ 34 | sh ./luarocks/build.sh build 35 | ]], 36 | install_command = [[\ 37 | LUA="$(LUA)" CC="$(CC)" LD="$(LD)" \ 38 | CFLAGS='$(CFLAGS) -I$(LUA_INCDIR)' \ 39 | PREFIX="$(PREFIX)" LIBDIR="$(LIBDIR)" \ 40 | sh ./luarocks/build.sh install 41 | ]], 42 | } 43 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # FFI API 2 | 3 | This document describes the API provided by the FFI module. It aims to stay 4 | mostly comparible with LuaJIT's FFI: http://luajit.org/ext_ffi.html 5 | 6 | However, there is a number of differences. 7 | 8 | ## Terms 9 | 10 | Largely identical to LuaJIT. 11 | 12 | - `cvalue` - Either `ctype` or `cdata` 13 | - `cdata` - A `cvalue` holding a value with the matching `ctype` 14 | - `ctype` - A kind of `cvalue` that has a type but not a value 15 | - `cdecl` - A string holding a C type declaration 16 | - `ct` - A C type specification, either a `cdecl`, `ctype` or `cdata` 17 | - `cb` - A callback object, this is a kind of `cdata` function pointer 18 | that actually points to a native Lua function 19 | - `VLA` - A variable length array, or an array with `?` in place of element 20 | count; the number of elements must be provided during creation 21 | - `VLS` - A `struct` with a flexible array member 22 | 23 | Differences from LuaJIT API will be clearly marked as such. 24 | 25 | ## Declaring and accessing symbols 26 | 27 | There are two steps to accessing external symbols. First they must be declared 28 | with `cffi.cdef`, then accessed through the appropriate namespace. 29 | 30 | ### cffi.cdef(def [, params...]) 31 | 32 | This function takes one or more C declarations as a string and declares them 33 | within the FFI. The string is actually a chunk of C code. Common C syntax 34 | is generally supported, please refer to [syntax.md](syntax.md) for supported 35 | syntax. 36 | 37 | There is no preprocessor and the parser doesn't support any C preprocessor 38 | tokens. Otherwise, the syntax support is reasonably complete. You could use 39 | an external preprocessor if you wish. 40 | 41 | The best way to use this function is with the Lua syntax sugar for function 42 | calls with one string argument: 43 | 44 | ``` 45 | cffi.cdef [[ 46 | /* some function */ 47 | void foo(); 48 | // another function 49 | int bar(float); 50 | ]] 51 | ``` 52 | 53 | Declarations are not subject to namespacing; all declarations apply to all 54 | namespaces, their existence is then checked at runtime. There is no separation 55 | between individual `cdef` calls, declarations made in one call carry over. 56 | 57 | Any errors in the declaration (e.g. syntax errors) will be propagated as Lua 58 | errors and any declarations staged during the call will be discarded. You do 59 | not have to worry about partial declarations leaking through. 60 | 61 | **Note:** The `cdef` function supports parameterized types. Read up on those 62 | in the [semantics.md](semantics.md) document. The extra parameters are used 63 | with those. 64 | 65 | ### cffi.C 66 | 67 | The default C library namespace, bound to the default set of symbols available 68 | on your system. 69 | 70 | On POSIX systems, this will contain all the symbols available by default, which 71 | includes the C standard library and related things (on Linux, `libm`, `libdl` 72 | and so on), possibly `libgcc` as well as symbols exported from Lua. 73 | 74 | On Windows, symbols are looked up in this order: 75 | 76 | - The current running executable 77 | - The executable or library containing the FFI (`cffi.dll` when a module 78 | or a dynamically linked library, or the executable when compiled in) 79 | - The C runtime library 80 | - `kernel32.dll` 81 | - `user32.dll` 82 | - `gdi32.dll` 83 | 84 | Keep in mind that C standard library symbols such as `stdio.h` symbols may 85 | not always be exported - e.g. MinGW defines them as inline functions. For 86 | Microsoft toolchain, we have an override in place that forces `stdio` 87 | symbols to be exported, but it's not possible on other toolchains. 88 | 89 | ### clib = cffi.load(name, [,global]) 90 | 91 | This loads a dynamic library given by `name` and returns a namespace object 92 | that you can access the library symbols through. 93 | 94 | The `name` argument can be a path, in which case the library is loaded from 95 | the path, otherwise it is treated in a system-specific way and searched for 96 | in default path(s). Generally that means the following: 97 | 98 | On POSIX systems, if the name does not contain a dot, the `.so` extension is 99 | appended (`.dylib` on macOS). The `lib` prefix is also prepended, if necessary. 100 | Therefore, calling something like `cffi.load("foo")` will look for `libfoo.so` 101 | (or `libfoo.dylib`) in the default search path. 102 | 103 | On Windows systems, if there is no dot in the name, the `.dll` extension is 104 | appended. Therefore, calling `cffi.load("foo")` will look for `foo.dll` in the 105 | default path. 106 | 107 | ## Creating cdata objects 108 | 109 | The following functions create `cdata` objects. All created `cdata` objects 110 | are subject to garbage collection. 111 | 112 | ### cdata = cffi.new(ct, [,nelem] [,init...]) 113 | 114 | This creates a `cdata` object for the given `ct`. VLA/VLS types require the 115 | `nelem` argument and will raise an error if that is not given. 116 | 117 | The `cdata` object is initialized using standard initializer rules described 118 | in [semantics.md](semantics.md), using the optional `init` argument(s). Excess 119 | initializers will raise an error. 120 | 121 | Keep in mind that anonymous `struct` or `union` declarations in `ct` will 122 | create a new declaration every time, which is probably not something you want. 123 | Use `cffi.typeof` to cache the declaration, then use that. 124 | 125 | ### cdata = ctype([nelem,] [init...]) 126 | 127 | This is fully equivalent to `cffi.new`, but using an object previously returned 128 | from `cffi.typeof` or other means. 129 | 130 | ### ctype = cffi.typeof(ct [, params...]) 131 | 132 | Creates a `ctype` object for the given `ct`. This is largely useful to parse 133 | the `ct` argument once and return a permanent handle to it, which you can use 134 | with a constructor like above. 135 | 136 | **Note:** The `typeof` function supports parameterized types. Read up on those 137 | in the [semantics.md](semantics.md) document. The extra parameters are used with 138 | those. 139 | 140 | ### cdata = cffi.cast(ct, init) 141 | 142 | This creates a new `cdata` object using the C type cast rules. See the right 143 | section in [semantics.md](semantics.md) for more information. In some cases 144 | it will be the same as `cffi.new`, but things like pointer compatibility checks 145 | are ignored. 146 | 147 | ### ctype = cffi.metatype(ct, metatable) 148 | 149 | This creates a `ctype` object for the given `ct` and associates it with a 150 | metatable. 151 | 152 | This is only supported for `struct`/`union` types. You can't change the 153 | metatable once assigned, an error will be raised. 154 | 155 | **Difference from LuaJIT:** you can reassign the contents of the metatable 156 | later, but only as long as the actual fields do not change. The FFI internally 157 | builds a mapping of metamethods that have been assigned to the `ctype`, thus 158 | adding more metamethods into the table after the assignment will have no 159 | effect (they will never be used) and removing some will result in calls to 160 | `nil` values, which you do not want. 161 | 162 | All metamethods implementable for `userdata` in the given Lua version are 163 | supported. That means at very least those supported by Lua 5.1, plus anything 164 | added later (5.2 supports `__pairs` and `__ipairs`, 5.3 adds bitwise ops). 165 | 166 | When using Lua 5.3 or later, `__name` from the metatable is used when calling 167 | `tostring`. Its type must be `string`, any other type is ignored, following the 168 | standard Lua semantics (this includes numbers, i.e. `type(v) == "string"` must 169 | be true). For `ctype` objects, the result of `tostring` will be the value of 170 | `__name`, for `cdata` objects the `__name` will replace the C type signature 171 | of the result (i.e. you will get something like `foo: 0xDEADBEEF`). 172 | 173 | With Lua 5.4, the `__close` metamethod for to-be-closed variables is also 174 | supported. On `ctype` objects, it is ignored, as if it didn't exist; on 175 | `cdata` objects it follows the standard semantics. 176 | 177 | **Difference from LuaJIT:** because of how metamethods in Lua work, the 178 | `__eq` metamethod will never be called if any of the sides is not `userdata`. 179 | That means checking equality against `nil` or any Lua value will always return 180 | `false` regardless of your implementation, so keep that in mind. 181 | 182 | Like in LuaJIT, the `__gc` metamethod mostly results in implicit `ffi.gc` 183 | call during creation of any `cdata` of that type. Therefore, you can still 184 | assign a custom finalizer to specific `cdata` objects. 185 | 186 | ### cdata = cffi.gc(cdata, finalizer) 187 | 188 | Associates a finalizer with `cdata`. The finalizer will be called on the 189 | object during garbage collection and can be any callable object. The obvious 190 | usage case is automatically freeing pointers allocated with e.g. `malloc`, 191 | like this: 192 | 193 | ``` 194 | local p = cffi.gc(cffi.C.malloc(100), cffi.C.free) 195 | p = nil -- will be garbage collected and finalizer will be called 196 | ``` 197 | 198 | **Difference from LuaJIT:** Can be used with any `cdata`. 199 | 200 | ### cdata = cffi.addressof(cdata) 201 | 202 | **Extension, does not exist in LuaJIT.** 203 | 204 | For pointer reference (`T &`) `cdata`, this returns a pointer cdata `T *` with 205 | the same address. 206 | 207 | For any other `cdata` (`T`), this takes an address to that and returns a `T *`. 208 | 209 | ## C type information 210 | 211 | ### size = cffi.sizeof(ct, nelem) 212 | 213 | Returns the size of `ct` in bytes. Never raises an error, if the size is not 214 | known, returns `nil` (e.g. for `void` or function types). If `ct` is a type 215 | or a declaration string specifying VLA/VLS, `nelem` is required. Otherwise 216 | if it is a VLA/VLS `cdata`, the size is determined automatically. 217 | 218 | ### align = cffi.alignof(ct) 219 | 220 | Returns the minimum required alignment of the `ct`. 221 | 222 | ### offset = cffi.offsetof(ct, field) 223 | 224 | If `field` does not exist in `ct`, returns `nil`. Otherwise, it returns the 225 | offset (in bytes) within the aggregate (`struct`/`union`) type for the member. 226 | 227 | **Difference from LuaJIT:** Since we do not support bit fields, the special 228 | case of returning the position and offset for those is not handled. 229 | 230 | ### bool = cffi.istype(ct, obj) 231 | 232 | Returns `true` if `obj` has the C type given by `ct`. Otherwise returns `false`. 233 | 234 | C type qualifiers (`const` and so on) are ignored. Pointers are checked using 235 | the standard rules, but without a special case for `void *`. If `ct` specifies 236 | an aggregate type, then a pointer to this is also accepted, otherwise the match 237 | must be exact. 238 | 239 | For non-`cdata` objects this always returns `false`. 240 | 241 | ## Utility functions 242 | 243 | ### err = cffi.errno([newerr]) 244 | 245 | Returns the `errno` number set by the last C call and optionally assigns a 246 | new value specified by `newerr`. 247 | 248 | This function exists in order to portably handle `errno`. You should call this 249 | as close to the C function that sets it as possible to make sure it is not 250 | overridden by something else. 251 | 252 | ### str = cffi.string(ptr [,len]) 253 | 254 | Creates a Lua string from the data pointed to by `ptr`. 255 | 256 | If the optional `len` argument is not specified, `ptr` is converted to `char *` 257 | and the data is assumed to be zero terminated. 258 | 259 | Otherwise `ptr` is converted to `void *` and `len` is used to specify the number 260 | of bytes. The string may contain embedded zeroes and does not necessarily need 261 | to be byte oriented (but be aware of potential endianness issues). 262 | 263 | The main purpose of this function is to convert string pointers returned from C 264 | to Lua strings. The resulting Lua string is a standard interned string, unrelated 265 | to the original. 266 | 267 | ### cffi.copy(dst, src, len) 268 | 269 | This is pretty much an equivalent of `memcpy`. Accepts a destination pointer, 270 | a source pointer and length. The `dst` is converted to `void *` while the `src` 271 | is converted to `void const *`. 272 | 273 | The `src` input can also be a Lua string. 274 | 275 | ### cffi.copy(dst, str) 276 | 277 | Given a Lua string, this copies its contents into `dst`. Equivalent to calling 278 | `cffi.copy(dst, str, #str)`. 279 | 280 | ### cffi.fill(dst, len [,c]) 281 | 282 | Fills the data pointed to by `dst` with `len` constant bytes, given by `c`. If 283 | `c` is not provided, the data is filled with zeroes. 284 | 285 | ### val = cffi.toretval(cdata) 286 | 287 | **Extension, does not exist in LuaJIT.** 288 | 289 | Given a `cdata`, this converts it to a Lua value (which may or may not be 290 | `cdata`) in a manner identical to if the type of the `cdata` was returned from 291 | a C function and called through the FFI. 292 | 293 | In general this means things fully representable using Lua values (most numbers 294 | and so on) will be returned as the appropriate Lua types, while others will 295 | be returned as `cdata`. 296 | 297 | ### val = cffi.eval(str) 298 | 299 | **Extension, does not exist in LuaJIT.** 300 | 301 | Given a Lua string, this attempts to evaluate the string as a C constant 302 | expression and returns the `cdata` created from that. This is useful to 303 | create 64-bit integer `cdata` without having the LuaJIT parser extensions, 304 | as Lua numbers don't have enough precision to represent all values. 305 | 306 | ### cffi.nullptr 307 | 308 | **Extension, does not exist in LuaJIT.** 309 | 310 | As Lua metamethod semantics do not allow equality comparisons of different 311 | types, LuaJIT style comparisons `cdata == nil` cannot be done. Instead, this 312 | constant is provided and used like `cdata == cffi.nullptr`. 313 | 314 | It is just a `void *` `cdata` with address `0x0`. 315 | 316 | ## Target-specific information 317 | 318 | ### bool = cffi.abi(param) 319 | 320 | Returns `true` if `param` (which is a Lua string) applies to the target ABI, 321 | and `false` otherwise. The following parameters are defined: 322 | 323 | | Parameter | Description | 324 | |-----------|-------------------------| 325 | | 32bit | 32-bit architecture | 326 | | 64bit | 64-bit architecture | 327 | | be | Big endian | 328 | | le | Little endian | 329 | | win | Windows ABI | 330 | | eabi | 32-bit ARM EABI | 331 | | uwp | Windows UWP | 332 | | elfv2 | 64-bit POWER ELFv2 | 333 | | unionval | Can pass union by value | 334 | | fpu | Hardware FPU | 335 | | hardfp | Hard float ABI | 336 | | softfp | Soft float ABI | 337 | 338 | **Extensions:** The `elfv2` and `unionval` parameters have been added. 339 | The others match LuaJIT. 340 | 341 | ### cffi.os 342 | 343 | The target OS name. Can be `Windows`, `Linux`, `OSX`, `BSD`, `POSIX` or `Other`. 344 | These values exactly match LuaJIT. 345 | 346 | ### cffi.arch 347 | 348 | The target architecture name. 349 | 350 | | Name | Architecture | Present in LuaJIT | 351 | | ---------------|-----------------|-------------------| 352 | | `alpha` | DEC Alpha | no | 353 | | `arm` | 32-bit ARM LE | yes | 354 | | `armeb` | 32-bit ARM BE | yes | 355 | | `arm64` | AArch64 LE | yes | 356 | | `arm64be` | AArch64 BE | yes | 357 | | `hppa` | PA-RISC | no | 358 | | `m68k` | Motorola 68k | no | 359 | | `microblaze` | Microblaze BE | no | 360 | | `microblazeel` | Microblaze LE | no | 361 | | `mips` | MIPS BE | yes | 362 | | `mipsel` | MIPS LE | yes | 363 | | `mips32r6` | MIPS R6 BE | yes | 364 | | `mips32r6el` | MIPS R6 LE | yes | 365 | | `mips64` | MIPS64 BE | yes | 366 | | `mips64el` | MIPS64 LE | yes | 367 | | `mips64r6` | MIPS64 R6 BE | yes | 368 | | `mips64r6el` | MIPS64 R6 LE | yes | 369 | | `or1k` | OpenRISC | no | 370 | | `ppc` | PowerPC BE | yes | 371 | | `ppcle` | PowerPC LE | no | 372 | | `ppc64le` | 64-bit POWER LE | no | 373 | | `ppc64` | 64-bit POWER | no | 374 | | `riscv32` | 32-bit RISC-V | no | 375 | | `riscv64` | 64-bit RISC-V | no | 376 | | `s390` | IBM System/390 | no | 377 | | `s390x` | IBM Z | no | 378 | | `sh4` | SH-4 | no | 379 | | `sh4eb` | SH-4 BE | no | 380 | | `sparc` | 32-bit SPARC | no | 381 | | `sparc64` | 64-bit SPARC | no | 382 | | `x64` | x86\_64 | yes | 383 | | `x86` | IA-32 | yes | 384 | | `unknown` | Other | yes | 385 | 386 | This is extended over LuaJIT; the entries which LuaJIT had stay compatible, 387 | others are custom extensions for new architectures. 388 | 389 | ## Callback methods 390 | 391 | ### cb:free() 392 | 393 | Frees the resources associated with the callback. The Lua function is unanchored 394 | and may be garbage collected. The callback handle is no longer valid and must 395 | not be called anymore (an error will be raised). 396 | 397 | **Difference from LuaJIT:** For now the callback handles are not reused. This 398 | may change in the future, so you should assume they could be. 399 | 400 | ### cb:set(func) 401 | 402 | Associates a new Lua function with the callback. The old function is unanchored 403 | and the new function takes its place. This is useful so you can reuse callback 404 | resources without allocating a new closure every time, which is fairly expensive. 405 | 406 | ## Standard cdata metamethods 407 | 408 | The default `cdata` metatable implements all possible metamethods available in 409 | this Lua version. This is necessary in order to be able to call custom 410 | metamethods set through `cffi.metatype`. Most of them will raise errors 411 | if there is no default behavior for them. 412 | 413 | Arithmetic and comparison metamethods have default implementations that 414 | work on relevant `cdata`, as described in [semantics.md](semantics.md). 415 | 416 | The `__index` and `__newindex` metamethods are implemented by default for 417 | pointer, reference, array and aggregate type `cdata`. The semantics of those 418 | are described in [semantics.md](semantics.md). 419 | 420 | ### mt(cdata).__tostring 421 | 422 | Normally, this will return `cdata: 0xADDRESS`. There is a special case for 423 | 64-bit integer `cdata`, which instead return the numeric value, with the 424 | appropriate `LL` or `ULL` suffix. The address of the `cdata` in the default 425 | printing is either the pointer value (if pointer-like) or the address of the 426 | actual value. 427 | 428 | Ctypes also have an implementation, they will return `ctype`. 429 | 430 | ### mt(cdata).__metatable 431 | 432 | This has the value `ffi`. It is used to prevent retrieval of the default `cdata` 433 | metatable through standard interfaces, instead the string `ffi` will always be 434 | returned. You can still inspect the metatable through the `debug` interfaces 435 | but it is not recommended to do so. Any changes done to it may break the FFI. 436 | 437 | ## Extended Lua functions 438 | 439 | ### n = cffi.tonumber(v) 440 | 441 | If given a `cdata`, this is converted to a Lua number. This may incur a 442 | precision loss, particularly on pre-5.3 Lua without integer support. Returns 443 | `nil` if the `cdata` cannot be converted (isn't numeric). 444 | 445 | If given a non-`cdata`, performs a conversion according to the standard Lua 446 | semantics of `tonumber`. 447 | 448 | ### tp = cffi.type(v) 449 | 450 | Like Lua `type()`, but returns `cdata` for `cvalue`s (regular Lua `type()` 451 | will return `userdata`). Returns the same thing as the standard function 452 | for anything else. 453 | -------------------------------------------------------------------------------- /docs/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | The `cffi-lua` project aims to provide a Lua module that allows calling C 4 | functions and using C data structures from Lua. 5 | 6 | The most obvious purpose for this is writing bindings to C libraries. Normally 7 | you would need to create manual glue code in C to expose the functionality 8 | into Lua; this is made unnecessary with an FFI, simply provide it with some 9 | C declarations and you're good to go. 10 | 11 | The module largely emulates the API and semantics of the FFI library contained 12 | in LuaJIT (http://luajit.org/ext_ffi.html). It does not aim to be bug-for-bug 13 | compatible, and some Lua semantics cause assorted differences (e.g. comparisons 14 | of C data for equality and so on). Where there are differences, the other 15 | documentation generally makes it clear. 16 | 17 | Unlike the LuaJIT FFI, it works with any common Lua version, starting with 18 | Lua 5.1. It works with LuaJIT itself, too, but there isn't much use to that. 19 | 20 | ## Simple example 21 | 22 | ``` 23 | local cffi = require("cffi") 24 | cffi.cdef [[ 25 | int printf(char const *fmt, ...); 26 | ]] 27 | ffi.C.printf("hello %s\n", "world") 28 | ``` 29 | 30 | Note how this is pretty much identical to the example on the LuaJIT website. 31 | 32 | Using the FFI consists of three steps: 33 | 34 | 1) First you need to load the module using `require` 35 | 2) Declare the C functions/data structures you wish to use 36 | 3) Call the C function using the appropriate namespace 37 | 38 | The module internally uses `libffi` (https://sourceware.org/libffi) to ensure 39 | portability. Thanks to that, it can be reasonably portable. It will probably 40 | run on any common architecture and operating system you can think of, no 41 | matter if it's x86, ARM, PowerPC, Linux, macOS or some BSD. 42 | 43 | ## C data structures 44 | 45 | Since the FFI parses plain C declarations, it makes sense it wouldn't stop 46 | at just functions. We can define C structures, and even give the mmetatables, 47 | to make pretty Lua interfaces. 48 | 49 | First, let's start with a plain structure: 50 | 51 | ``` 52 | local cffi = require("cffi") 53 | cffi.cdef [[ 54 | typedef struct point_t { double x; double y; } point_t; 55 | ]] 56 | ``` 57 | 58 | We can create an instance of such structure with `cffi.new`, and perhaps give 59 | it some default values, too. If no initializer is provided, the fields are 60 | automatically zeroed, not left undefined like in C. 61 | 62 | ``` 63 | local pt = cffi.new("point_t") -- {x = 0, y = 0} 64 | local pt = cffi.new("point_t", 5, 10) -- {x = 5, y = 10} 65 | local pt = cffi.new("point_t", {5, 10}) -- {x = 5, y = 10} 66 | local pt = cffi.new("point_t", {x = 5, y = 10}) -- {x = 5, y = 10} 67 | pt.x = 20 68 | print(pt.x, pt.y) 69 | ``` 70 | 71 | Now let's give it a metatable, for a prettier interface. 72 | 73 | ``` 74 | local point 75 | point = cffi.metatype("point_t", { 76 | __add = function(a, b) 77 | return point(a.x + b.x, a.y + b.y) 78 | end, 79 | __len = function(a) 80 | return math.sqrt(a:area()) 81 | end, 82 | __index = { 83 | area = function(a) 84 | return a.x * a.x + a.y * a.y 85 | end 86 | } 87 | }) 88 | ``` 89 | 90 | The we can use it like this: 91 | 92 | ``` 93 | local pt = point(3, 4) 94 | print(pt.x, pt.y) -- 3, 4 95 | print(#pt) -- 5 96 | print(pt:area()) -- 25 97 | local pt2 = a + point(0.5, 8) 98 | print(#pt2) -- 12.5 99 | ``` 100 | 101 | The `cffi.metatype` takes a C declaration and permanently associates it with 102 | a metatable. This metatable is then used for all instances of that type. You 103 | can use all metamethods you could otherwise use in that Lua version on other 104 | types. 105 | 106 | You should not modify the metatable once it has been associated. You can not 107 | normally retrieve it, it's protected; it is still possible to do that via 108 | the `debug` interface, but keep in mind that the metamethods that have been 109 | set in the metatable are registered with the type during binding, therefore 110 | anyhow changing the set of metamethods in the table will either not take 111 | effect or will break. 112 | 113 | Otherwise, you can refer to the LuaJIT FFI documentation for most things. 114 | Just keep in mind the potential differences. 115 | 116 | ## C idioms 117 | 118 | These are like in LuaJIT. 119 | 120 | ### Pointer dereference 121 | 122 | Lua has no dereference operator. 123 | 124 | For: 125 | 126 | ``` 127 | int *p; 128 | ``` 129 | 130 | With: 131 | 132 | ``` 133 | x = *p; 134 | *p = y; 135 | ``` 136 | 137 | Use: 138 | 139 | ``` 140 | x = p[0] 141 | p[0] = y 142 | ``` 143 | 144 | ### Array and pointer indexing 145 | 146 | For: 147 | 148 | ``` 149 | int i, a[]; // or int i, *a; 150 | ``` 151 | 152 | With: 153 | 154 | ``` 155 | x = a[i]; 156 | a[i] = y; 157 | ``` 158 | 159 | Use: 160 | 161 | ``` 162 | x = a[i] 163 | a[i] = y 164 | ``` 165 | 166 | ### Struct/union field access (value or pointer) 167 | 168 | For: 169 | 170 | ``` 171 | struct foo s; // or struct foo *s; 172 | ``` 173 | 174 | With: 175 | 176 | ``` 177 | x = s.field; // or x = s->field; 178 | s.field = y; // or s->field = y; 179 | ``` 180 | 181 | Use: 182 | 183 | ``` 184 | x = s.field 185 | s.field = y 186 | ``` 187 | 188 | ### Pointer arithmetic 189 | 190 | For: 191 | 192 | ``` 193 | int *p, i; 194 | ``` 195 | 196 | With: 197 | 198 | ``` 199 | x = p + i; 200 | y = p - i; 201 | ``` 202 | 203 | Use: 204 | 205 | ``` 206 | x = p + i 207 | y = p - i 208 | ``` 209 | 210 | ### Pointer difference 211 | 212 | For: 213 | 214 | ``` 215 | int *p1, *p2; 216 | ``` 217 | 218 | With: 219 | 220 | ``` 221 | d = p1 - p2; 222 | ``` 223 | 224 | Use: 225 | 226 | ``` 227 | d = p1 - p2 228 | ``` 229 | 230 | ### Pointer to array element 231 | 232 | For: 233 | 234 | ``` 235 | int i, a[]; 236 | ``` 237 | 238 | With: 239 | 240 | ``` 241 | x = &a[i]; 242 | ``` 243 | 244 | Use: 245 | 246 | ``` 247 | x = a + i; 248 | ``` 249 | 250 | ### Cast pointer to address 251 | 252 | For: 253 | 254 | ``` 255 | int *p; 256 | ``` 257 | 258 | With: 259 | 260 | ``` 261 | x = (uintptr_t)p; 262 | ``` 263 | 264 | Use: 265 | 266 | ``` 267 | x = ffi.cast("uintptr_t", x) 268 | ``` 269 | 270 | ### Out-arguments 271 | 272 | **Different from LuaJIT.** Well, you can use the same approach with allocating 273 | a single-element array if you want. We have an extension for this, though. 274 | 275 | For: 276 | 277 | ``` 278 | void foo(int *oarg); 279 | ``` 280 | 281 | With: 282 | 283 | ``` 284 | int x = ...; 285 | foo(&x); 286 | y = x; 287 | ``` 288 | 289 | Use: 290 | 291 | ``` 292 | local x = cffi.new("int", ...) 293 | foo(cffi.addressof(x)) 294 | y = x; 295 | ``` 296 | 297 | ### Vararg conversions 298 | 299 | For: 300 | 301 | ``` 302 | int printf(char const *fmt, ...); 303 | ``` 304 | 305 | With: 306 | 307 | ``` 308 | printf("%g", 1.0); 309 | printf("%d", 1); 310 | ``` 311 | 312 | Use: 313 | 314 | ``` 315 | printf("%g", 1.0) 316 | printf("%d", ffi.new("int", 1)) 317 | ``` 318 | 319 | **This applies in the default Lua configuration.** If your Lua is configured 320 | to use a different type for numbers, the conversions may be different; see 321 | the [semantics.md](semantics.md) document. 322 | 323 | **With Lua 5.3, integers will always get converted to an integer type, not 324 | a floating point type.** That means passing `1.0` and `1` is different in 5.3, 325 | while it's the same in `5.2` and older. 326 | 327 | ## Caching 328 | 329 | **The caching advice from LuaJIT does not apply here.** LuaJIT suggests to 330 | cache only namespaces, without caching C functions themselves, as it has a JIT 331 | engine that can optimize these out into direct calls. We have no such thing, 332 | and each namespace index call has its own overhead (it has to retrieve the 333 | symbol). Calls don't concern us; they are always indirect, or well, they have 334 | to go through Lua before getting to `libffi`. 335 | 336 | Therefore, it is advisable to cache function handles when you can. 337 | 338 | ``` 339 | -- definitely do this 340 | local foo = cffi.C.foo 341 | local bar = cffi.C.bar 342 | local baz = function(a, b) 343 | return foo(a) + bar(b) 344 | end 345 | ``` 346 | 347 | This is the fastest you can get. As for caching `ctype`s, the usual LuaJIT 348 | advice still applies. 349 | -------------------------------------------------------------------------------- /docs/semantics.md: -------------------------------------------------------------------------------- 1 | # FFI semantics 2 | 3 | This document aims to describe the semantics of the FFI as closely as possible. 4 | 5 | In general, we mimic the LuaJIT FFI semantics as well as the C language 6 | semantics. Sometimes this is not possible, the sections where this differs 7 | will be clearly marked as such. 8 | 9 | ## C language support 10 | 11 | You can refer to [syntax.md](syntax.md) for this, which covers all of the 12 | syntax implemented by the FFI at this point. 13 | 14 | Our C parser aims to be simple first and foremost. Therefore, it does not 15 | have detailed error message analysis and so on. It will, however, reject 16 | invalid declarations, and likely is stricter than LuaJIT's. 17 | 18 | ## Conversion rules 19 | 20 | Conversions follow the same rules as LuaJIT. Since the LuaJIT documentation 21 | on those is relatively confusing, it is described with more detail here. 22 | 23 | ### C types -> Lua values 24 | 25 | There are several contexts in which C types are converted to Lua values. In 26 | each context, a conversion rule is used, which influences the manner in which 27 | the value is converted. 28 | 29 | - Return values from C function calls (*return rule*) 30 | - Passing arguments to Lua callbacks (*pass rule*) 31 | - Reading external C variables (*return rule*) 32 | - Reading constants (*return rule*) 33 | - Reading array elements and aggregate fields (*conversion rule*) 34 | - Converting `cdata` to Lua numbers with `cffi.tonumber` (*conversion rule*) 35 | - Using `cffi.toretval` (*return rule*) 36 | 37 | It generally applies that conversions are lossless. That means there is no 38 | precision loss when creating the Lua value; in practice it means e.g. when 39 | dealing with large integers, they will be converted to Lua numbers but only 40 | when the Lua number can fit the entire value losslessly, otherwise you will 41 | get a boxed `cdata`. 42 | 43 | There is only one lossy conversion and that's `cffi.tonumber`. 44 | 45 | Here is a table of all conversions: 46 | 47 | | Input C type | Output Lua type | Context | 48 | |------------------|-------------------|-----------------------------| 49 | | bool | `boolean` | any | 50 | | any numeric | `number` | lossy or representable | 51 | | any numeric | numeric `cdata` | lossless, not representable | 52 | | pointer | pointer `cdata` | any | 53 | | `va_list` | `va_list` `cdata` | any | 54 | | reference | see below | conversion rule | 55 | | reference | reference `cdata` | any other | 56 | | array | reference `cdata` | any applicable | 57 | | `struct`/`union` | reference `cdata` | conversion rule | 58 | | `struct`/`union` | value `cdata` | any other rule | 59 | 60 | In practice, this means that scalar types are always converted either to 61 | a matching Lua value type or a value `cdata`, while other types are converted 62 | to their matching `cdata`. An exception to this is when accessing `struct` or 63 | equivalent fields/elements in aggregates, which will return a reference `cdata` 64 | to the base type. 65 | 66 | **Note**: accessing references in *conversion rule* contexts is special: they 67 | get dereferenced first, and the conversion rule applies to the base type. The 68 | only notable case of this is the fairly niche case of having reference fields 69 | in structs. E.g. accessing an `int &` field in a `struct` will undergo standard 70 | numeric conversion. 71 | 72 | ### Lua values -> C types 73 | 74 | Just like with the opposite conversions, there are several contexts in which 75 | Lua values are converted to C types. In each context, a conversion rule is 76 | used, and these are similar to the conversion rules for the opposite operation. 77 | 78 | - Return values from Lua callbacks (*return rule*) 79 | - Passing arguments to C function calls (*pass rule*) 80 | - Initializing array elements/aggregate fields (*conversion rule*) 81 | - Writing into external C varialbes (*conversion rule*) 82 | - Writing values to array elements and aggregate fields (*conversion rule*) 83 | - Allocating `cdata` with `cffi.new` (*conversion rule*) 84 | - Casting `cdata` with `cffi.cast` (*cast rule*) 85 | 86 | These conversions are always lossless. 87 | 88 | Here is a table of all conversions: 89 | 90 | | Input Lua type | Ouput C type | Context | 91 | |-----------------|------------------|---------------------------| 92 | | `number` | see note | any | 93 | | `boolean` | `bool` | any | 94 | | `nil` | `NULL` | any | 95 | | `userdata` | `void *` | any | 96 | | `lightuserdata` | `void *` | any | 97 | | `io.* file` | `FILE *` | any | 98 | | `string` | `enum` | match against `enum` type | 99 | | `string` | byte array | initializer | 100 | | `string` | `char const[]` | any | 101 | | `function` | function pointer | callback creation | 102 | | `table` | C aggregate type | table initializer | 103 | | `cdata` | C type | see C type conversions | 104 | 105 | For `cdata` conversions, see the C type conversions section below. 106 | 107 | Lua numbers are converted to their matching `cdata` scalar. The type depends 108 | on your Lua configuration. For default configurations of Lua 5.2 and older, 109 | you will always get a `double`. Lua 5.3 added support for integer types, 110 | so **in 5.3 and newer**, you will get either an integer type or a floating 111 | point type. You may also get an integer type in 5.2 and older for special 112 | configurations that configure numbers as integers. 113 | 114 | Please keep in mind that unusual configurations are not as well tested as 115 | regular configurations, but it is still attempted to support them on best 116 | effort basis. 117 | 118 | Strings may be converted to arbitrary byte arrays in initializers (with 119 | `cffi.new`) but not in any other context. 120 | 121 | Casting (with `cffi.cast`) only supports scalar types (numeric, pointers, 122 | references). 123 | 124 | Callback creation may happen in any context, but you will not be able to 125 | manage the callback's resources afterwards, so you should use `cffi.cast` 126 | or `cffi.new` to create an accessible handle first. 127 | 128 | References are dereferenced in context of conversions and casts. The operation 129 | is applied on their underlying type. 130 | 131 | ### C type conversions 132 | 133 | These are similar to standard C/C++ conversion rules. Conversions not 134 | mentioned here will error. 135 | 136 | These conversions may apply in any context: 137 | 138 | | Input | Output | Context | 139 | |---------|---------|-----------------------------------------| 140 | | numeric | numeric | any; may undergo narrow/trunc/extend | 141 | | numeric | `bool` | any; `false` when `0`, `true` otherwise | 142 | | `bool` | numeric | any; `1` for `true`, `0` for `false` | 143 | 144 | These conversions only apply in *cast rule* context: 145 | 146 | | Input | Output | Context | 147 | |------------------|-----------|--------------------------------| 148 | | pointer | reference | address init; no compat checks | 149 | | `io.* file` | reference | address init; no compat checks | 150 | | `userdata` | reference | address init; no compat checks | 151 | | `string` | reference | address init; no compat checks | 152 | | `struct`/`union` | reference | address init; no compat checks | 153 | | array | reference | address init; no compat checks | 154 | | integer | reference | address value initialization | 155 | | pointer | pointer | address init; no compat checks | 156 | | `io.* file` | pointer | address init; no compat checks | 157 | | `userdata` | pointer | address init; no compat checks | 158 | | `string` | pointer | address init; no compat checks | 159 | | `struct`/`union` | pointer | address init; no compat checks | 160 | | array | pointer | address init; no compat checks | 161 | | integer | pointer | address value initialization | 162 | | pointer | funcptr | address init; no compat checks | 163 | | C function | funcptr | address init; no compat checks | 164 | 165 | Note that references passed to casts are dereferenced first and the underlying 166 | type is used for conversion. Also, pointers mean both regular and function 167 | pointers in cast inputs. 168 | 169 | Only scalar types are allowed as target type of `cast`. 170 | 171 | These conversions only apply in *conversion rule* context: 172 | 173 | | Input | Output | Context | 174 | |------------------|-----------|--------------------------------| 175 | | pointer | reference | address init; checked | 176 | | `io.* file` | reference | address init; checked | 177 | | `userdata` | reference | address init; checked | 178 | | `string` | reference | address init; checked | 179 | | `struct`/`union` | reference | address init; checked | 180 | | array | reference | address init; checked | 181 | | pointer | pointer | address init; checked | 182 | | `io.* file` | pointer | address init; checked | 183 | | `userdata` | pointer | address init; checked | 184 | | `string` | pointer | address init; checked | 185 | | `struct`/`union` | pointer | address init; checked | 186 | | array | pointer | address init; checked | 187 | | pointer | funcptr | address init; checked | 188 | | C function | funcptr | address init; checked | 189 | | `struct`/`union` | same | must be same type | 190 | 191 | In this context, you can generally create references from pointers as well as 192 | pointers from arrays, but the underlying type has to be compatible, which means 193 | both that the type itself has to match and the cv-qualifiers have to be 194 | compatible (e.g. `char *` to `char const *` conversion is possible but not 195 | the other way around). Generic `void *` pointers will convert to/from anything 196 | just like in C, but the cv-qualifiers still have to be compatible. 197 | 198 | As for `struct`/`union` types, you can create their respective pointers, but 199 | the underlying type of the pointer must be compatible. Strings will only 200 | convert to `char const *` or `char const[]`, or to arbitrary byte arrays 201 | (the underlying type is a signed or unsigned or unspecified byte) in 202 | initializer contexts (`cffi.new`). 203 | 204 | Copy construction works in this context. You can create `struct`/`union` types 205 | by passing `struct`/`union` cdata to them (or pointer), and a copy will happen. 206 | It will also happen with other types (like scalars). 207 | 208 | Function pointers are arbitrarily convertible as long as argument count is the 209 | same. This is a relic of LuaJIT and may be changed to actual strict checks, 210 | so don't count on it. 211 | 212 | Table initializers are allowed in `cffi.new` but nowhere else. 213 | 214 | There are some special considerations for the *pass rule*. When an argument 215 | is expecting a reference, you can pass a base type `cdata` to it and its 216 | address will be taken automatically. **This is not allowed in LuaJIT.** 217 | However, this only applies to `cdata`, plain Lua values will not undergo 218 | any intermediate conversions. 219 | 220 | Arrays are allowed as output types under *pass rule*, but not any other rules. 221 | 222 | ### Special vararg conversions 223 | 224 | These conversions apply when passing Lua values to C varargs. They are 225 | different from the standard conversions. 226 | 227 | | Input Lua type | Output C type | 228 | |--------------------------|--------------------------| 229 | | `number` | see note | 230 | | `boolean` | `bool` | 231 | | `nil` | `NULL` | 232 | | `userdata` | `void *` | 233 | | `lightuserdata` | `void *` | 234 | | `io.* file` | `FILE *` | 235 | | `string` | `char const *` | 236 | | `float` `cdata` | `double` | 237 | | array `cdata` | element pointer | 238 | | function `cdata` | function pointer | 239 | | `struct`/`union` `cdata` | `struct`/`union` pointer | 240 | | any other `cdata` | C type | 241 | 242 | What this means in practice compared to standard argument passing is that as 243 | required by C, `float`s are always promoted to `double`s, while `struct`s and 244 | `union`s are never passed by value, but rather by address. Other conversions 245 | should be trivial and obvious. 246 | 247 | **Note**: if your Lua is configured with a floating point `number`, which it 248 | generally is, numbers will be passed as `double`s in the general case, except 249 | when configured to use `long double` and the `long double` is larger than 250 | a `double` on your platform. With Lua 5.3, there is also support for integers, 251 | which will be passed as matching integer C type. Therefore, be careful with 252 | how you pass your values. 253 | 254 | ## Initializers 255 | 256 | Just like in LuaJIT, creating a `cdata` object with `cffi.new` (or the `ctype` 257 | constructor syntax, which is functionally identical) always initializes its 258 | contents. Depending on the given initializers, different rules apply. 259 | 260 | - Without any initializers, the memory is zeroed (`memset(p, 0, nbytes)`) 261 | - Scalar types such as numbers and pointers accept a single initializer. The 262 | given initializer is converted to the scalar C type. 263 | - (*not yet implemented*) Complex numbers (and vectors) are treated like 264 | scalars with a single initializer, otherwise like arrays. 265 | - Aggregate types (structs and arrays) accept either a single `cdata` 266 | initializer of the same type (copy constructor), a single table initializer, 267 | or a list of initializers (each element is initialized with one argument) 268 | - The elements of an array are initialized (starting at index zero). If a 269 | single initializer is given, it is repeated for all remaining elements. 270 | If two or more initializers are given, only the given fields are initialized, 271 | with the remaining ones getting filled with zero bytes. If too many are given, 272 | an error is raised. 273 | - Byte arrays may also be initialized with a Lua string. The string is copied 274 | including its terminating zero, and no errors are raised on size mismatch. 275 | - Only the first field of a `union` can be initialized with a flat initializer. 276 | - Elements or fields which are aggregates are initialized with a single 277 | initializer, which follows the standard rules. 278 | - Excess initializers raise an error. 279 | 280 | ## Table initializers 281 | 282 | This is also identical to LuaJIT. Only arrays, `struct`s and `union`s can be 283 | initialized with a table. 284 | 285 | - If the table index `[0]` is not `nil`, then the table is assumed to be zero 286 | based, otherwise it's one based. 287 | - Array elements, starting at index zero, are initialized one-by-one with 288 | the consecutive table elements. The moment a `nil` element is reached, the 289 | initialization stops. 290 | - If exactly one array element was initialized, it's repeated for all remaining 291 | elements. Otherwise, all remaining elements are filled with zero bytes. 292 | - The above logic does not apply for VLAs, which are only initialized with 293 | the elements given in the table. 294 | - A `struct` or `union` type can be initialized in the order of the declaration 295 | of its fields. Each field is initialized like it was an array and the process 296 | stops at the first `nil`. 297 | - Otherwise, if neither `[0]` or `[1]` are present, a `struct`/`union` is 298 | initialized by looking up each field in the table by name. Each non-`nil` 299 | field is initialized. 300 | - Uninitialized fields of a `struct` are filled with zero bytes, except for 301 | flexible array members. 302 | - Initialization of a `union` stops after one field has been initialized. If 303 | none has been initialized, the `union` is filled with zero bytes. 304 | - Elements that are aggregates are each initialized with a single initializer, 305 | which may be a table. 306 | - Excess initializers raise an error for arrays, but not for `struct`s or 307 | `union`s. Unrelated table entries are ignored. 308 | 309 | ## Operations on cdata 310 | 311 | Generally identical to LuaJIT, with some caveats. Standard Lua operators can 312 | generally be applied to `cdata` objects or a mix of `cdata` and Lua object. 313 | 314 | Reference types are dereferenced before performing their operations. The 315 | operation is applied to the C type pointed to by the reference. 316 | 317 | The pre-defined operations are always tried first, following standard Lua 318 | semantics. If that is not possible, a metamethod is used (or an `__index` 319 | table). An error is raised if the metamethod lookup or index table lookup 320 | fails. 321 | 322 | ### Indexing a cdata 323 | 324 | - **Pointers/arrays**: a `cdata` pointer/array can be indexed by a `cdata` 325 | number or a Lua number. The element address is computed like in C. Read 326 | access will convert the element value to a Lua object. Write access will 327 | convert the Lua object to the element type and store it in the array. An 328 | error is raised if the element size is undefined or write access to a 329 | constant element is attempted. 330 | - **Accessing struct/union fields**: a `cdata` `struct`/`union` or a pointer 331 | to it can have its fields accessed by name. The field address is computed 332 | like in C. Read access will convert the element value to a Lua object, 333 | write access will convert the Lua object to the element type and store it. 334 | An error is raised if either the aggregate or the field is constant. 335 | - **Indexing a complex number**: (*not yet implemented*) a complex number 336 | can be indexed by a `cdata` number or a Lua number with the values 0 or 1, 337 | or by the strings `re` or `im`. Read access loads the real part or the 338 | imaginary part and converts it to a Lua number. The sub-parts of a complex 339 | number are immutable. Accessing out-of-bounds elements is undefined but 340 | will not trigger a segfault. 341 | - **Indexing a vector**: (*not yet implemented*) a vector is treated like an 342 | array for indexing, except the elements are immutable. 343 | 344 | A `ctype` object can be indexed with a key as well. The only defined operation 345 | is accessing constant fields. All other accesses will trigger metamethods. 346 | 347 | **Difference from LuaJIT**: Since we have an address-of function, you can 348 | modify contents of value types after they are created, by taking their address 349 | and indexing them. 350 | 351 | ### Calling a cdata 352 | 353 | - **Constructor**: a `ctype` object can be called and used as a constructor. 354 | This is equivalent to `cffi.new(ct, ...)` unless a `__new` metamethod is 355 | defined, in which case it's called instead. Note that inside you have to 356 | use `cffi.new` directly in order not to cause infinite recursion. 357 | - **C function call**: a `cdata` function or a function pointer can 358 | be called. The passed arguments are converted to C types as required 359 | by the declaration. Arguments passed to the vararg part undergo special 360 | conversion rules. The C function is called and the return value is converted 361 | to a Lua object, losslessly. 362 | 363 | **Difference from LuaJIT**: Windows `__stdcall` functions must be explicitly 364 | declared as so. 365 | 366 | ### Arithmetic on cdata 367 | 368 | - **Pointer arithmetic**: a `cdata` pointer/array and a `cdata` number or a 369 | Lua number can be added or subtracted. The number must be on the right hand 370 | side. The result is a pointer of the same type with the address changed the 371 | same way as in C. An error is raised if the element size is not defined. 372 | - **Pointer difference**: two compatible pointers/arrays can be subtracted. 373 | The result is a difference in their addresses, divided by their element size 374 | in bytes, thus effectively the number of elements. An error is raised if 375 | the element size is unknown or zero. 376 | - **64-bit integer arithmetic**: the standard arithmetic operators can all 377 | be applied to two `cdata` numbers or a mix of `cdata` number and Lua number. 378 | If one of them is an unsigned 64-bit integer, the other is ocnverted to 379 | the same type and the operation is unsigned. Otherwise, both sides are 380 | converted to a signed 64-bit `cdata` and a signed operation is performed. 381 | The result is a boxed 64-bit `cdata` object. 382 | 383 | Not yet implemented: if one side in arithmetic is an `enum` and the other side 384 | is a string, the string is converted to the value of a matching `enum` before 385 | the conversion, the result is still a 64-bit `cdata` integer though. 386 | 387 | You will explicitly have to convert `cdata` numbers with `cffi.tonumber`. This 388 | may incur a precision loss, at least depending on your Lua version and/or 389 | configuration (5.3+ integers are respected). 390 | 391 | ### Comparisons of cdata 392 | 393 | - **Pointer comparison**: two compatible `cdata` pointers/arrays can be compared. 394 | The result is the same as unsigned comparison of their addresses. The `nil` 395 | value is treated like a `NULL` pointer, compatible with any other pointer type. 396 | - **64-bit integer comparison**: two `cdata` number or a `cdata` number and a 397 | Lua number can be compared. The same conversions as in arithmetic are 398 | performed first, same with `enum`s. 399 | - **Equality comparisons** never raise an error, but a notable **difference 400 | from LuaJIT** is that metamethods are only ever triggered on compatible 401 | Lua types, which means comparisons of `cdata` and `nil` (or other Lua 402 | values) will always be `false` no matter what. Incompatible `cdata` 403 | pointers can always be tested for address equality. 404 | 405 | ### Table keys and cdata 406 | 407 | Do not use `cdata` as table keys. Since they are `userdata` objects to Lua, 408 | they will always be handled by address of the Lua object, which means not 409 | even any two scalar `cdata` objects will ever hash to the same value. 410 | 411 | For numbers, if you can deal with the precision of numbers in your Lua version, 412 | that may be an option. Especially with Lua 5.3 and its integer support, you 413 | should not get any precision loss by default. 414 | 415 | You can also always create your own hash table with the FFI, which will allow 416 | indexing by `cdata`. 417 | 418 | ## Parameterized types 419 | 420 | Just like LuaJIT, our FFI supports parameterized types in `cffi.typeof` and 421 | `cffi.cdef`. In whichever C context where a type, a name or a number is 422 | expected, you can write `$`. These placeholders are then replaced with 423 | extra arguments pased. 424 | 425 | ``` 426 | cffi.cdef([[ 427 | typedef struct { $ $; } foo_t; 428 | ]], cffi.typeof("int"), "foo") 429 | 430 | local bar_t = cffi.typeof("struct { int $; }", "foo") 431 | local bar_ptr_t = cffi.typeof("$ *", bar_t) 432 | 433 | -- works even where VLAs won't 434 | local multi_t = cffi.typeof("uint8_t[$][$]", w, h) 435 | ``` 436 | 437 | Like in LuaJIT, parameterized types are not simple text substitution. Where 438 | a type is expected, you must supply a `ctype` or `cdata`, you can't simply 439 | pass a string. 440 | 441 | You can think of it as something similar to C++ templates. 442 | 443 | Parameterized types are also useful to refer to anonymous `ctype`s where you 444 | can't refer to them by name (because they're, well, anonymous). As long as 445 | you have a Lua handle to the anonymous `ctype`, you can still pass it in 446 | a parameterized context. 447 | 448 | Same precautions as in LuaJIT generally apply. Use them sparingly. 449 | 450 | ## Garbage collection of cdata 451 | 452 | All `cdata` objects are garbage collected `userdata`. That means when you 453 | allocate memory, e.g. with `cffi.new`, you need to make sure to retain this 454 | somewhere in Lua for as long as it stays on C side. Once the last reference 455 | is gone, the memory may be freed arbitrarily. 456 | 457 | ``` 458 | cffi.cdef [[ 459 | void foo(int *p); 460 | ]] 461 | cffi.C.foo(cffi.new("int[10]")) -- bad, may be GC'd 462 | local p = cffi.new("int[10]") 463 | cffi.C.foo(p) -- correct 464 | ``` 465 | 466 | Note that for pointers, the address itself is that memory, not what it points 467 | to. So when you e.g. use `cffi.C.malloc` to allocate a block of memory, it 468 | will not be garbage collected, only its `cdata` pointer object will. 469 | 470 | In those cases, you need to manually manage the memory in order not to leak 471 | it, though. You can use `cffi.gc` to associate it with a finalizer. Then 472 | the manually allocated memory will be garbage collected in the same manner. 473 | 474 | ## Callbacks 475 | 476 | The FFI provides a callback API that is identical to LuaJIT's. Whenever you 477 | convert a Lua function to a C function pointer, a callback is allocated. The 478 | resulting function pointer can then be called by C or by Lua. 479 | 480 | Callback creation can happen implicitly (e.g. by passing Lua functions) or 481 | you can explicitly use `cffi.cast` (or `cffi.new`) to create a callback. 482 | 483 | Keep in mind that **vararg functions are not supported as callbacks**. You 484 | also **can pass a struct by value**, unlike LuaJIT; passing of unions by 485 | value is subject to the same limitations as in normal functions. 486 | 487 | There are no limitations or checks on the Lua function. The arguments passed 488 | to the Lua function will undergo conversions according to their standard 489 | rules, without a precision loss. There is no reasonable way to make sure 490 | argument count matches and so on, so keep that in mind. 491 | 492 | ### Callback resources 493 | 494 | Unlike LuaJIT, there is no artificial limit on how many callbacks you can have 495 | at a time. 496 | 497 | Just like in LuaJIT, callbacks are permanent, as the same lifetime concerns 498 | apply. You will want to manually manage the callback memory when used 499 | frequently. 500 | 501 | ``` 502 | cffi.cdef [[ ... ]] 503 | local cb = cffi.cast("cb_t", function(...) ... end) 504 | cffi.C.func(cb) 505 | -- we asume func doesn't store the callback and doesn't need it anymore 506 | cb:free() -- callback no longer valid, can't be used 507 | ``` 508 | 509 | In general, you should still avoid callbacks when you can. There is always 510 | a cost to them. Also, use `cb:set` to reuse callbacks of the same type when 511 | you can. That way we can avoid allocating a new callback every time. 512 | 513 | ## Library namespaces 514 | 515 | A namespace is a special `userdata` that allows access to C functions and 516 | other symbols. The `cffi.C` namespace always exists. Other namespaces are 517 | created by the user when loading libraries. 518 | 519 | Indexing a namespace will result in the appropriate symbol being retrieved 520 | and provided to you as `cdata`. Symbols must always be declared first, with 521 | `cffi.cdef`. Both undeclared and missing symbols will result in errors being 522 | raised. 523 | 524 | This is what happens on reads: 525 | 526 | - Functions: a `cdata` object with the type of the function is returned 527 | - Variables: the symbol undergoes conversion to a Lua object according 528 | to the rules defined above; this never undergoes precision loss, so 529 | you may get either a Lua value or a `cdata` 530 | - Constants: undergoes conversion to a Lua object, without precision loss 531 | 532 | This is what happens on writes: 533 | 534 | - Variables: the value undergoes conversion to its C type according to the 535 | rules defined above and the resulting value is written 536 | - Other declarations: an error is raised 537 | 538 | C library namespaces themselves are garbage collected, once the last reference 539 | is gone, the namespace will eventually be released. This may result in existing 540 | symbol pointers to be invalidated, so keep that in mind and keep your library 541 | namespace alive for as long as any symbols you may be using. 542 | 543 | Unlike LuaJIT, we don't have a JIT engine, so caching C functions from 544 | namespaces is useful, to avoid the overhead of the retrieval. 545 | 546 | ## Other precautions 547 | 548 | Keep in mind that just like LuaJIT's FFI, this is a low level library and 549 | provides no memory safety, bounds checking or other measures. Therefore, you 550 | should probably keep this away from high level code when writing bindings, as 551 | it is easily possible to segfault the program and so on from Lua code. 552 | 553 | **Do not allow FFI access to untrusted Lua code.** You will want to sandbox 554 | this away, always. 555 | -------------------------------------------------------------------------------- /docs/syntax.md: -------------------------------------------------------------------------------- 1 | # Supported C syntax 2 | 3 | The project supports a subset of the C11 language, plus some extensions. There 4 | are still extensions as well as actual official language features missing. This 5 | document does not aim to provide any sort of spec or formal grammar, as all 6 | the features implemented are just C features; it's more or less a set of 7 | guidelines on what kind of syntax is allowed. 8 | 9 | This will also not cover things such as parameterized types, which are not 10 | a part of the core syntax per se, but rather specific to certain APIs. 11 | 12 | This document will get expanded as more syntax is implemented. 13 | 14 | ## Declarations 15 | 16 | ### Functions 17 | 18 | #### Basic functions 19 | 20 | **Syntax:** 21 | 22 | ``` 23 | T function_name(); 24 | T function_name(void); 25 | T function_name(A1, A2, A3); 26 | T function_name(A1, A2, A3, ...); 27 | ``` 28 | 29 | Basic function declarations are supported, including variadic function 30 | declarations. 31 | 32 | **Note:** Declaring a function with an empty argument list follows C++ 33 | semantics! Therefore, `T foo();` is equivalent to `T foo(void);`. 34 | 35 | **Extension:** Unnamed arguments are supported, therefore `void foo(int x)` and 36 | simple `void foo(int)` are identical. 37 | 38 | You can also write: 39 | 40 | ``` 41 | T (function_name(...)); // function returning T 42 | T (*function_name(...)); // function returning T * 43 | ``` 44 | 45 | This is a part of standard C rules on how types can be parenthesized, in 46 | this case leaving out the parenthesis doesn't change anything. Note how 47 | things change when you return function pointers though: 48 | 49 | #### Returning function pointers 50 | 51 | **Syntax:** 52 | 53 | ``` 54 | // T == return type of function pointer 55 | // A1 == arglist of the function 56 | // A2 == arglist of the function pointer 57 | T (*function_name(A1))(A2); 58 | ``` 59 | 60 | This is the same as in C. It can be nested as well: 61 | 62 | ``` 63 | T (*(*function_name(A1))(A2))(A3); 64 | ``` 65 | 66 | Where `A1` is the argument list of the function, `A2` is the argument list 67 | of the function pointer it returns, and `A3` is the argument list of the 68 | function pointer the function pointer returns, with `T` being the return 69 | type of the last returned function pointer. 70 | 71 | ### Variables 72 | 73 | **Syntax:** 74 | 75 | ``` 76 | T var; 77 | extern T var; 78 | T (var); 79 | extern T (var); 80 | ``` 81 | 82 | These syntaxes mean the same thing. 83 | 84 | #### Pointer variables 85 | 86 | **Syntax:** 87 | 88 | ``` 89 | T *var; 90 | T (*var); 91 | ``` 92 | 93 | These syntaxes mean the same thing. This extends to how pointers bind to 94 | their underlying types, like in function pointers. 95 | 96 | #### Array variables 97 | 98 | **Syntax:** 99 | 100 | ``` 101 | T var[N]; 102 | T *var[N]; // array of pointers to T 103 | T (*var)[N]; // pointer to array of T 104 | T *(*var)[N]; // pointer to array of pointers to T 105 | ``` 106 | 107 | Unbounded array declarations are allowed, but you will not be able to get 108 | their size or instantiate their type. 109 | 110 | ``` 111 | T var[]; 112 | ``` 113 | 114 | All array types effectively decay to pointers. 115 | 116 | See the section about types below for more array-related details. 117 | 118 | ### Typedefs 119 | 120 | **Syntax:** 121 | 122 | ``` 123 | typedef FROM TO; // typedef from normal type 124 | typedef T (*TO)(...); // typedef from function pointer type 125 | ``` 126 | 127 | These follow standard C rules. 128 | 129 | **Note:** Unlike in C, `typedef` is currently not treated as storage class. 130 | Therefore, writing `int typedef foo;` is currently not possible. 131 | 132 | **Note:** Using `typedef` will not create a type, but rather just an internal 133 | alias. Referring to something via `typedef` will always expand to the underlying 134 | type during usage, regardless of the number of alias levels. 135 | 136 | ### Structs and unions 137 | 138 | **Syntax:** 139 | 140 | ``` 141 | struct foo { 142 | ... 143 | }; 144 | 145 | union bar { 146 | ... 147 | }; 148 | ``` 149 | 150 | You can couple `struct` with `typedef` as usual. Unnamed `struct` and `union` 151 | is also possible. C rules are followed, so you can't refer to a non-`typedef` 152 | `struct` simply by name, you need to use the full `struct name` form. 153 | 154 | **Extension:** You can specify a transparent `struct` inside a `struct`, and 155 | its members will be accessible the same as other members. 156 | 157 | ``` 158 | struct foo { 159 | int x; 160 | struct { 161 | int y; 162 | }; 163 | int z; 164 | }; 165 | ``` 166 | 167 | Transparent `union` is also possible: 168 | 169 | ``` 170 | struct foo { 171 | union { 172 | ... 173 | }; 174 | }; 175 | ``` 176 | 177 | Same with the other way around. 178 | 179 | Opaque `struct` and `union` is also possible: 180 | 181 | ``` 182 | struct foo; 183 | ``` 184 | 185 | **Note:** Unnamed `struct`s or `union`s or `enum`s will get an implicit name 186 | assigned internally. You can not retrieve them by name directly afterwards, 187 | however you can still get handles to them via `cffi.typeof`, and use them 188 | together with e.g. parameterized types. 189 | 190 | Different `struct`s (and `union`s) are never equal, even if their members are. 191 | Therefore, creating two unnamed `struct`s will always result in distinct types. 192 | 193 | ### Enums 194 | 195 | **Syntax:** 196 | 197 | ``` 198 | enum foo { 199 | A, B, C, ... 200 | }; 201 | ``` 202 | 203 | Explicitly setting values is possible: 204 | 205 | ``` 206 | enum foo { 207 | A = 5, B, C, D = 1 << 10, ... 208 | }; 209 | ``` 210 | 211 | The following members will increment their value by one. 212 | 213 | **Note:** Currently all underlying type of all `enum` declarations is `int`. 214 | Therefore, `enum`s with large values are not yet supported. 215 | 216 | All `enum` members are exported into global scope, so you can access their 217 | values via `ffi.C.`. 218 | 219 | **Extension:** Opaque `enum`s are supported. 220 | 221 | ## Types 222 | 223 | All core integer types are supported: `bool`, `_Bool`, `char`, `signed char`, 224 | `unsigned char`, `short`, `unsigned short`, `int`, `unsigned int`, `long`, 225 | `unsigned long`, `long long`, `unsigned long long`. 226 | 227 | All core floating point types are supported: `float`, `double`, `long double`. 228 | 229 | The `bool` type is a distinct type, like in C++. `_Bool` is an alias to it. 230 | **This follows C++ semantics.** 231 | 232 | The `char` type is a distinct type with an unknown signedness (platform 233 | specific). The `signed char` and `unsigned char` types are distinct from 234 | plain `char`. 235 | 236 | Like in C, `unsigned` used alone is `unsigned int`, same with `signed` and 237 | `signed int`. 238 | 239 | **Note:** The `signed` and `unsigned` keywords are only allowed before the 240 | type name, i.e. no `int unsigned x;` like in C. 241 | 242 | **The following builtin types are also defined:** `va_list`, `__builtin_va_list`, 243 | `__gnuc_va_list` - these are all aliases to the same `va_list` type 244 | 245 | **The following aliases are implicitly defined:** 246 | 247 | - From `stddef.h`: `size_t`, `ptrdiff_t`, `wchar_t` 248 | - From `stdint.h`: `int8_t`, `int16_t`, `int32_t`, `int64_t`, `uint8_t`, 249 | `uint16_t`, `uint32_t`, `uint64_t` 250 | - From `uchar.h`: `char16_t`, `char32_t` 251 | - From `sys/types.h`: `ssize_t`, `time_t` 252 | 253 | **The following types are not yet supported:** `complex` / `_Complex` variants 254 | 255 | ### Array types 256 | 257 | Variable length arrays are supported with a special syntax `T[?]`. This is 258 | generally treated like an unbounded array within relevant type contexts (as 259 | it has no size by itself), but for FFI `ctype`s and instantiations, it is 260 | treated specially. In most cases, you will be using VLAs when instantiating 261 | types, e.g. via `cffi.typeof`. However, it is also possible to e.g. `typedef` 262 | a VLA and instantiate it later through its alias. 263 | 264 | During instantiation of VLAs, you will be required to provide a size, and 265 | the resulting `cdata` will have a runtime size, which you can retrieve via 266 | `cffi.sizeof` like with a normal static array. 267 | 268 | ### Struct/union types 269 | 270 | Just like in C, you can use anonymous or named `struct`s or `union`s as types, 271 | not just type declarations. 272 | 273 | ### C++ references 274 | 275 | As an extension, the FFI supports C++ style references (`T &`). They are 276 | implemented as pointers, but have different semantics. 277 | 278 | It is not possible to declare a pointer to a reference, nor it is possible to 279 | declarare a reference to a reference. Function references are possible with 280 | the usual syntax `T (&foo)(...)`, as are references to anything else. 281 | 282 | In most cases, references will automatically decay to their underlying values 283 | like in C++. For example, when using reference `cdata` in `cffi.cast` or 284 | `cffi.new`, they are always dereferenced and treated like their underlying 285 | type, same with passing them to function. If the target type is a reference 286 | type, a new reference to the underlying value will be taken. Internally, this 287 | may or may not happen; the FFI can sometimes choose to retain the reference 288 | as with a valid value, this is effectively the same. 289 | 290 | ## Expressions 291 | 292 | The FFI features a complete expression parser. The following expressions are 293 | supported: 294 | 295 | - Binary expressions `A op B` - these support the usual set of binary operators, 296 | `+ - * / % == != > < >= <= && || & | ^ << >>` 297 | - Unary expressions `op A` - with operators `+ - ! ~` 298 | - Ternary operator `C ? T : F` 299 | - Integer literals 300 | - The `true` and `false` boolean literals 301 | - The `sizeof` expression 302 | - The `alignof` expression (including `__alignof__` syntax) 303 | - Parenthesized expression `(E)` 304 | 305 | All standard C operator precedences are followed. 306 | 307 | **The following expressions are not yet supported:** 308 | 309 | - Name references to constants (e.g. `enum` constants) 310 | - `sizeof` with an expression (only type is supported) 311 | - String literals 312 | - Character literals 313 | - Floating point literals 314 | - Complex number literals 315 | 316 | Integer literals support decimal, hexadecimal, octal and binary (**C++ ext**) 317 | versions, including the standard suffixes for signedness and length. 318 | -------------------------------------------------------------------------------- /luarocks/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | # luarocks more like luasucks 6 | case "$CC" in 7 | *-cc) export CXX=${CC/-cc/-c++} ;; 8 | cc) export CXX=c++ ;; 9 | *-gcc) export CXX=${CC/-gcc/-g++} ;; 10 | gcc) export CXX=g++ ;; 11 | *clang*) export CXX=${CC/clang/clang++} ;; 12 | *) export CXX=$CC ;; 13 | esac 14 | 15 | # should be okay 16 | export CXXFLAGS="$CFLAGS" 17 | 18 | case "$1" in 19 | build) 20 | rm -rf build && mkdir build && cd build 21 | meson .. -Dbuildtype=release -Dlua_version=custom \ 22 | -Dtests=false -Dlua_install_path="$LIBDIR" \ 23 | --force-fallback-for=libffi 24 | ninja all 25 | ;; 26 | install) 27 | # we want just the module, no subproject stuff 28 | mkdir -p "$LIBDIR" 29 | cp build/cffi.so "$LIBDIR" 30 | mkdir -p "${PREFIX}"/doc 31 | cp -a docs/* "${PREFIX}"/doc 32 | rm -rf build 33 | ;; 34 | *) exit 1 ;; 35 | esac 36 | 37 | exit 0 38 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # Project definition 2 | 3 | project('cffi-lua', ['cpp'], 4 | version: '0.2.3', 5 | default_options: [ 6 | 'buildtype=debugoptimized', 'b_ndebug=if-release', 'cpp_std=c++14', 7 | 'warning_level=3', 'cpp_rtti=false', 'cpp_eh=none' 8 | ], 9 | meson_version: '>=0.56' 10 | ) 11 | 12 | # Extra compiler warnings for gcc/clang 13 | 14 | cxx = meson.get_compiler('cpp') 15 | 16 | extra_cxxflags = [] 17 | 18 | if get_option('buildtype') != 'plain' 19 | if cxx.has_argument('-Wshadow') 20 | extra_cxxflags += '-Wshadow' 21 | endif 22 | if cxx.has_argument('-Wold-style-cast') 23 | extra_cxxflags += '-Wold-style-cast' 24 | endif 25 | endif 26 | 27 | # Endianness specification is mandatory 28 | 29 | if host_machine.endian() == 'big' 30 | extra_cxxflags += '-DFFI_BIG_ENDIAN' 31 | else 32 | extra_cxxflags += '-DFFI_LITTLE_ENDIAN' 33 | endif 34 | 35 | # Vendor library path; used to find libs and also added to PATH for Windows 36 | 37 | deps_path = get_option('deps_dir') 38 | deps_libs = [ 39 | join_paths(meson.project_source_root(), deps_path), 40 | join_paths(meson.project_build_root(), deps_path) 41 | ] 42 | extra_inc = [] 43 | 44 | # Lua dependency checks 45 | 46 | luaver = get_option('lua_version') 47 | 48 | if luaver == 'luajit' 49 | lua_dep = dependency('luajit') 50 | elif luaver != 'auto' and luaver != 'custom' and luaver != 'vendor' 51 | lua_dep = dependency('lua' + luaver, required: false) 52 | if not lua_dep.found() 53 | lua_dep = dependency('lua-' + luaver, required: false) 54 | endif 55 | if not lua_dep.found() 56 | lua_dep = dependency('lua' + ''.join(luaver.split('.')), required: false) 57 | endif 58 | if not lua_dep.found() 59 | lua_dep = dependency('lua') 60 | endif 61 | if not lua_dep.version().startswith(luaver) 62 | error('required lua version not found (got @0@)' 63 | .format(lua_dep.version())) 64 | endif 65 | elif luaver == 'custom' 66 | lua_dep = dependency('', required: false) 67 | elif luaver == 'vendor' 68 | lua_dep = dependency('', required: false) 69 | extra_inc += include_directories(join_paths(deps_path, 'include')) 70 | else 71 | lua_dep = dependency('lua') 72 | endif 73 | 74 | # Libffi dependency checks 75 | 76 | ffiver = get_option('libffi') 77 | 78 | if ffiver == 'custom' 79 | ffi_dep = dependency('', required: false) 80 | elif ffiver == 'vendor' 81 | ffi_dep = cxx.find_library('ffi', dirs: deps_libs) 82 | extra_inc += include_directories(join_paths(deps_path, 'include')) 83 | else 84 | # use static lib if subproject 85 | ffi_dep = dependency('libffi', 86 | default_options: ['default_library=static', 'tests=false'] 87 | ) 88 | endif 89 | 90 | # Needed on Linux 91 | 92 | dl_lib = cxx.find_library('dl', required: false) 93 | 94 | # These are Windows only 95 | 96 | if get_option('shared_libffi') 97 | extra_cxxflags += '-DHAVE_LIBFFI_DLLIMPORT' 98 | endif 99 | 100 | # Module build definition 101 | 102 | luaver_maj = '5' 103 | luaver_num = cxx.compute_int( 104 | 'LUA_VERSION_NUM', prefix: '#include ', 105 | dependencies: lua_dep, include_directories: extra_inc 106 | ) 107 | luaver_min = luaver_num - 500 108 | luaver_str = '@0@.@1@'.format(luaver_maj, luaver_min) 109 | 110 | if luaver_min < 1 111 | error('Lua 5.1 or newer is required') 112 | endif 113 | 114 | # follow what lua does, i.e. .so everywhere except windows 115 | plugin_suffix = 'so' 116 | 117 | if host_machine.system() == 'windows' 118 | plugin_suffix = 'dll' 119 | endif 120 | 121 | cffi_src = [ 122 | 'src/util.cc', 123 | 'src/ffilib.cc', 124 | 'src/parser.cc', 125 | 'src/ast.cc', 126 | 'src/lib.cc', 127 | 'src/ffi.cc', 128 | 'src/main.cc' 129 | ] 130 | 131 | # on windows, we need to link to the dll, the dll has a specific name that 132 | # follows the lua version we depend on; on unix-likes we on the other hand 133 | # do not need the library at all, so skip it 134 | if host_machine.system() == 'windows' 135 | # msys2, etc 136 | lua_adep = cxx.find_library('lua', dirs: deps_libs, required: false) 137 | if not lua_adep.found() 138 | # lua 5.1 uses lua5.1.dll/lib 139 | lua_adep = cxx.find_library( 140 | 'lua@0@.@1@'.format(luaver_maj, luaver_min), 141 | dirs: deps_libs, required: false 142 | ) 143 | endif 144 | if not lua_adep.found() 145 | # lua 5.2 onwards uses lua5.2.dll/lib 146 | lua_adep = cxx.find_library( 147 | 'lua@0@@1@'.format(luaver_maj, luaver_min), dirs: deps_libs 148 | ) 149 | endif 150 | else 151 | lua_adep = lua_dep.partial_dependency(compile_args: true, includes: true) 152 | endif 153 | 154 | cffi_deps = [dl_lib, ffi_dep, lua_adep] 155 | 156 | if get_option('static') 157 | cffi = static_library('cffi-lua-@0@'.format(luaver_str), 158 | cffi_src, 159 | install: true, 160 | pic: true, 161 | dependencies: cffi_deps, 162 | include_directories: extra_inc, 163 | cpp_args: extra_cxxflags, 164 | gnu_symbol_visibility: 'hidden' 165 | ) 166 | 167 | cffi_dep = declare_dependency( 168 | link_with: cffi, 169 | include_directories: extra_inc 170 | ) 171 | else 172 | lua_modpath = get_option('lua_install_path') 173 | if lua_modpath == 'auto' 174 | lua_modpath = join_paths(get_option('libdir'), 'lua', '@0@') 175 | endif 176 | 177 | cffi = shared_module('cffi', 178 | cffi_src, 179 | install: true, 180 | install_dir: lua_modpath.format(luaver_str), 181 | name_prefix: '', 182 | name_suffix: plugin_suffix, 183 | dependencies: cffi_deps, 184 | cpp_args: ['-DCFFI_LUA_DLL'] + extra_cxxflags, 185 | include_directories: extra_inc, 186 | gnu_symbol_visibility: 'hidden' 187 | ) 188 | endif 189 | 190 | # Tests 191 | 192 | if meson.is_cross_build() and get_option('tests') 193 | build_tests = get_option('tests_cross') 194 | else 195 | build_tests = get_option('tests') 196 | endif 197 | 198 | if build_tests and not get_option('static') 199 | # get lua path for the runner 200 | lua_pathopt = get_option('lua_path') 201 | if lua_pathopt == 'auto' and luaver == 'vendor' 202 | lua_exe = find_program( 203 | join_paths(deps_path, 'lua@0@'.format(luaver_str)), 204 | join_paths(deps_path, 'lua@0@@1@'.format(luaver_maj, luaver_min)), 205 | join_paths(deps_path, 'lua'), 206 | required: true 207 | ) 208 | elif lua_pathopt == 'auto' 209 | lua_exe = find_program( 210 | 'lua@0@'.format(luaver_str), 211 | 'lua@0@@1@'.format(luaver_maj, luaver_min), 212 | 'lua', 213 | required: true 214 | ) 215 | else 216 | lua_exe = find_program(lua_pathopt, required: true) 217 | endif 218 | 219 | # check the lua version matches the library version 220 | # also checks if it's actually runnable (cross-compiling?) 221 | ret = run_command(lua_exe, [ 222 | '-e', 223 | 'io.write(_VERSION:match("5.+"))' 224 | ]) 225 | if ret.stdout() != luaver_str 226 | error('Lua executable does not match version (@0@ vs @1@)'.format( 227 | ret.stdout(), luaver_str 228 | )) 229 | endif 230 | 231 | subdir('tests') 232 | endif 233 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('lua_version', 2 | type: 'combo', 3 | choices: ['auto', 'luajit', 'custom', 'vendor', '5.1', '5.2', '5.3', '5.4'], 4 | description: 'The Lua version to use (custom == pass via CXXFLAGS/LDFLAGS)' 5 | ) 6 | 7 | option('libffi', 8 | type: 'combo', 9 | choices: ['auto', 'custom', 'vendor'], 10 | description: 'When using auto, pkg-config will be used to find libffi' 11 | ) 12 | 13 | option('lua_install_path', 14 | type: 'string', 15 | value: 'auto', 16 | description: 'The install path for the Lua module' 17 | ) 18 | 19 | option('shared_libffi', 20 | type: 'boolean', 21 | value: 'false', 22 | description: 'Assume import library for libffi (Windows only)' 23 | ) 24 | 25 | option('deps_dir', 26 | type: 'string', 27 | value: 'deps', 28 | description: 'The name of vendored dependencies directory' 29 | ) 30 | 31 | option('static', 32 | type: 'boolean', 33 | value: 'false', 34 | description: 'Build a static library, not a module' 35 | ) 36 | 37 | option('tests', 38 | type: 'boolean', 39 | value: 'true', 40 | description: 'Whether to build tests' 41 | ) 42 | 43 | option('tests_cross', 44 | type: 'boolean', 45 | value: 'false', 46 | description: 'Whether to build tests when cross-compiling' 47 | ) 48 | 49 | option('lua_path', 50 | type: 'string', 51 | value: 'auto', 52 | description: 'Path to lua executable for tests' 53 | ) -------------------------------------------------------------------------------- /src/ffi.hh: -------------------------------------------------------------------------------- 1 | #ifndef FFI_HH 2 | #define FFI_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "libffi.hh" 8 | 9 | #include "lua.hh" 10 | #include "lib.hh" 11 | #include "ast.hh" 12 | #include "util.hh" 13 | 14 | namespace ffi { 15 | 16 | enum metatype_flag { 17 | /* all versions */ 18 | METATYPE_FLAG_ADD = 1 << 0, 19 | METATYPE_FLAG_SUB = 1 << 1, 20 | METATYPE_FLAG_MUL = 1 << 2, 21 | METATYPE_FLAG_DIV = 1 << 3, 22 | METATYPE_FLAG_MOD = 1 << 4, 23 | METATYPE_FLAG_POW = 1 << 5, 24 | METATYPE_FLAG_UNM = 1 << 6, 25 | METATYPE_FLAG_CONCAT = 1 << 7, 26 | METATYPE_FLAG_LEN = 1 << 8, 27 | METATYPE_FLAG_EQ = 1 << 9, 28 | METATYPE_FLAG_LT = 1 << 10, 29 | METATYPE_FLAG_LE = 1 << 11, 30 | METATYPE_FLAG_INDEX = 1 << 12, 31 | METATYPE_FLAG_NEWINDEX = 1 << 13, 32 | METATYPE_FLAG_CALL = 1 << 14, 33 | METATYPE_FLAG_GC = 1 << 15, 34 | METATYPE_FLAG_TOSTRING = 1 << 16, 35 | 36 | /* only for ctypes */ 37 | METATYPE_FLAG_NEW = 1 << 17, 38 | 39 | #if LUA_VERSION_NUM > 501 40 | /* lua 5.2+ */ 41 | METATYPE_FLAG_PAIRS = 1 << 18, 42 | 43 | #if LUA_VERSION_NUM == 502 44 | /* lua 5.2 only */ 45 | METATYPE_FLAG_IPAIRS = 1 << 19, 46 | #endif 47 | 48 | #if LUA_VERSION_NUM > 502 49 | /* lua 5.3+ */ 50 | METATYPE_FLAG_IDIV = 1 << 20, 51 | METATYPE_FLAG_BAND = 1 << 21, 52 | METATYPE_FLAG_BOR = 1 << 22, 53 | METATYPE_FLAG_BXOR = 1 << 23, 54 | METATYPE_FLAG_BNOT = 1 << 24, 55 | METATYPE_FLAG_SHL = 1 << 25, 56 | METATYPE_FLAG_SHR = 1 << 26, 57 | 58 | METATYPE_FLAG_NAME = 1 << 27, 59 | #if LUA_VERSION_NUM > 503 60 | METATYPE_FLAG_CLOSE = 1 << 28, 61 | #endif /* LUA_VERSION_NUM > 503 */ 62 | #endif /* LUA_VERSION_NUM > 502 */ 63 | #endif /* LUA_VERSION_NUM > 501 */ 64 | }; 65 | 66 | static inline constexpr auto metafield_name(metatype_flag flag) { 67 | switch (flag) { 68 | case METATYPE_FLAG_ADD: return "__add"; 69 | case METATYPE_FLAG_SUB: return "__sub"; 70 | case METATYPE_FLAG_MUL: return "__mul"; 71 | case METATYPE_FLAG_DIV: return "__div"; 72 | case METATYPE_FLAG_MOD: return "__mod"; 73 | case METATYPE_FLAG_POW: return "__pow"; 74 | case METATYPE_FLAG_UNM: return "__unm"; 75 | case METATYPE_FLAG_CONCAT: return "__concat"; 76 | case METATYPE_FLAG_LEN: return "__len"; 77 | case METATYPE_FLAG_EQ: return "__eq"; 78 | case METATYPE_FLAG_LT: return "__lt"; 79 | case METATYPE_FLAG_LE: return "__le"; 80 | case METATYPE_FLAG_INDEX: return "__index"; 81 | case METATYPE_FLAG_NEWINDEX: return "__newindex"; 82 | case METATYPE_FLAG_CALL: return "__call"; 83 | case METATYPE_FLAG_GC: return "__gc"; 84 | case METATYPE_FLAG_TOSTRING: return "__tostring"; 85 | case METATYPE_FLAG_NEW: return "__new"; 86 | #if LUA_VERSION_NUM > 501 87 | case METATYPE_FLAG_PAIRS: return "__pairs"; 88 | #if LUA_VERSION_NUM == 502 89 | case METATYPE_FLAG_IPAIRS: return "__ipairs"; 90 | #endif 91 | #if LUA_VERSION_NUM > 502 92 | case METATYPE_FLAG_IDIV: return "__idiv"; 93 | case METATYPE_FLAG_BAND: return "__band"; 94 | case METATYPE_FLAG_BOR: return "__bor"; 95 | case METATYPE_FLAG_BXOR: return "__bxor"; 96 | case METATYPE_FLAG_BNOT: return "__bnot"; 97 | case METATYPE_FLAG_SHL: return "__shl"; 98 | case METATYPE_FLAG_SHR: return "__shr"; 99 | case METATYPE_FLAG_NAME: return "__name"; 100 | #if LUA_VERSION_NUM > 503 101 | case METATYPE_FLAG_CLOSE: return "__close"; 102 | #endif /* LUA_VERSION_NUM > 503 */ 103 | #endif /* LUA_VERSION_NUM > 502 */ 104 | #endif /* LUA_VERSION_NUM > 501 */ 105 | default: break; 106 | } 107 | return ""; 108 | } 109 | 110 | static_assert(( 111 | (sizeof(lua_Integer) <= sizeof(ffi::scalar_stor_t)) && 112 | (alignof(lua_Integer) <= alignof(ffi::scalar_stor_t)) && 113 | util::is_int::value 114 | ), "unsupported lua_Integer type"); 115 | 116 | /* lua_Number is supported either as a float or as an integer */ 117 | static_assert(( 118 | (sizeof(lua_Number) <= sizeof(ffi::scalar_stor_t)) && 119 | (alignof(lua_Number) <= alignof(ffi::scalar_stor_t)) && 120 | (util::is_float::value || util::is_int::value) 121 | ), "unsupported lua_Number type"); 122 | 123 | struct cdata { 124 | ast::c_type decl; 125 | int gc_ref; 126 | /* auxiliary data that can be used by different cdata 127 | * 128 | * vararg functions store the number of arguments they have storage 129 | * prepared for here to avoid reallocating every time 130 | */ 131 | int aux; 132 | 133 | template 134 | cdata(D &&tp): decl{util::forward(tp)} {} 135 | 136 | /* we always allocate enough userdata so that after the regular fields 137 | * of struct cdata, there is a data section, with enough space so that 138 | * the requested type can fit aligned to maximum scalar alignment we are 139 | * storing 140 | * 141 | * this is important, because lua_newuserdata may return misaligned pointers 142 | * (it only guarantees alignment of typically 8, while we typically need 16) 143 | * so we have to overallocate by a bit, then manually align the data 144 | */ 145 | void *as_ptr() { 146 | return util::ptr_align(this + 1); 147 | } 148 | 149 | template 150 | T &as() { 151 | return *static_cast(as_ptr()); 152 | } 153 | 154 | void *as_deref_ptr() { 155 | if (decl.is_ref()) { 156 | return as(); 157 | } 158 | return as_ptr(); 159 | } 160 | 161 | template 162 | T &as_deref() { 163 | return *static_cast(as_deref_ptr()); 164 | } 165 | 166 | void *address_of() { 167 | if (decl.ptr_like()) { 168 | return as(); 169 | } 170 | return as_deref_ptr(); 171 | } 172 | }; 173 | 174 | struct ctype { 175 | ast::c_type decl; 176 | int ct_tag; 177 | 178 | template 179 | ctype(D &&tp): decl{util::forward(tp)} {} 180 | }; 181 | 182 | inline constexpr std::size_t cdata_pad_size() { 183 | auto csz = sizeof(cdata); 184 | auto mod = (csz % alignof(lua::user_align_t)); 185 | /* size in multiples of lua userdata alignment */ 186 | if (mod) { 187 | csz += alignof(lua::user_align_t) - mod; 188 | } 189 | /* this should typically not happen, unless configured that way */ 190 | if (alignof(lua::user_align_t) >= alignof(util::max_aligned_t)) { 191 | return csz; 192 | } 193 | /* add the difference for internal alignment */ 194 | csz += (alignof(util::max_aligned_t) - alignof(lua::user_align_t)); 195 | /* this is what we will allocate, plus the data size */ 196 | return csz; 197 | } 198 | 199 | struct closure_data { 200 | ffi_cif cif; /* closure data needs its own cif */ 201 | int fref = LUA_REFNIL; 202 | lua_State *L = nullptr; 203 | ffi_closure *closure = nullptr; 204 | 205 | ~closure_data() { 206 | if (!closure) { 207 | return; 208 | } 209 | luaL_unref(L, LUA_REGISTRYINDEX, fref); 210 | ffi_closure_free(closure); 211 | } 212 | }; 213 | 214 | /* data used for function types */ 215 | struct fdata { 216 | void (*sym)(); 217 | closure_data *cd; /* only for callbacks, otherwise nullptr */ 218 | ffi_cif cif; 219 | ffi::scalar_stor_t rarg; 220 | 221 | ffi::scalar_stor_t *args() { 222 | return util::pun(this + 1); 223 | } 224 | }; 225 | 226 | static inline cdata &newcdata( 227 | lua_State *L, ast::c_type const &tp, std::size_t vals 228 | ) { 229 | auto ssz = cdata_pad_size() + vals; 230 | auto *cd = static_cast(lua_newuserdata(L, ssz)); 231 | new (cd) cdata{tp.copy()}; 232 | cd->gc_ref = LUA_REFNIL; 233 | cd->aux = 0; 234 | lua::mark_cdata(L); 235 | return *cd; 236 | } 237 | 238 | template 239 | static inline ctype &newctype(lua_State *L, A &&...args) { 240 | auto *cd = static_cast(lua_newuserdata(L, sizeof(ctype))); 241 | new (cd) ctype{ast::c_type{util::forward(args)...}}; 242 | cd->ct_tag = lua::CFFI_CTYPE_TAG; 243 | lua::mark_cdata(L); 244 | return *cd; 245 | } 246 | 247 | static inline bool iscdata(lua_State *L, int idx) { 248 | auto *p = static_cast(luaL_testudata(L, idx, lua::CFFI_CDATA_MT)); 249 | return p && (p->ct_tag != lua::CFFI_CTYPE_TAG); 250 | } 251 | 252 | static inline bool isctype(lua_State *L, int idx) { 253 | auto *p = static_cast(luaL_testudata(L, idx, lua::CFFI_CDATA_MT)); 254 | return p && (p->ct_tag == lua::CFFI_CTYPE_TAG); 255 | } 256 | 257 | static inline bool iscval(lua_State *L, int idx) { 258 | return luaL_testudata(L, idx, lua::CFFI_CDATA_MT); 259 | } 260 | 261 | static inline bool isctype(cdata const &cd) { 262 | return cd.gc_ref == lua::CFFI_CTYPE_TAG; 263 | } 264 | 265 | static inline cdata &checkcdata(lua_State *L, int idx) { 266 | auto ret = static_cast( 267 | luaL_checkudata(L, idx, lua::CFFI_CDATA_MT) 268 | ); 269 | if (isctype(*ret)) { 270 | lua::type_error(L, idx, "cdata"); 271 | } 272 | return *ret; 273 | } 274 | 275 | static inline cdata *testcval(lua_State *L, int idx) { 276 | return static_cast( 277 | luaL_testudata(L, idx, lua::CFFI_CDATA_MT) 278 | ); 279 | } 280 | 281 | static inline cdata *testcdata(lua_State *L, int idx) { 282 | auto ret = static_cast( 283 | luaL_testudata(L, idx, lua::CFFI_CDATA_MT) 284 | ); 285 | if (!ret || isctype(*ret)) { 286 | return nullptr; 287 | } 288 | return ret; 289 | } 290 | 291 | static inline cdata &tocdata(lua_State *L, int idx) { 292 | return *lua::touserdata(L, idx); 293 | } 294 | 295 | /* careful with this; use only if you're sure you have cdata at the index */ 296 | static inline std::size_t cdata_value_size(lua_State *L, int idx) { 297 | auto &cd = tocdata(L, idx); 298 | if (cd.decl.vla()) { 299 | /* VLAs only exist on lua side, they are always allocated by us, so 300 | * we can be sure they are contained within the lua-allocated block 301 | * 302 | * the VLA memory consists of the following: 303 | * - the cdata sequence with overallocation padding 304 | * - the section where the pointer to data is stored 305 | * - and finally the VLA memory itself 306 | * 307 | * that means we take the length of the userdata and remove everything 308 | * that is not the raw array data, and that is our final length 309 | */ 310 | return ( 311 | lua_rawlen(L, idx) - cdata_pad_size() - sizeof(ffi::scalar_stor_t) 312 | ); 313 | } else { 314 | /* otherwise the size is known, so fall back to that */ 315 | return cd.decl.alloc_size(); 316 | } 317 | } 318 | 319 | void destroy_cdata(lua_State *L, cdata &cd); 320 | void destroy_closure(lua_State *L, closure_data *cd); 321 | 322 | int call_cif(cdata &fud, lua_State *L, std::size_t largs); 323 | 324 | enum conv_rule { 325 | RULE_CONV = 0, 326 | RULE_PASS, 327 | RULE_CAST, 328 | RULE_RET 329 | }; 330 | 331 | /* this pushes a value from `value` on the Lua stack; its type 332 | * and necessary conversions are done based on the info in `tp` and `rule` 333 | * 334 | * `lossy` implies that numbers will always be converted to a lua number 335 | */ 336 | int to_lua( 337 | lua_State *L, ast::c_type const &tp, void const *value, int rule, 338 | bool ffi_ret, bool lossy = false 339 | ); 340 | 341 | /* a unified version of from_lua that combines together the complex aggregate 342 | * initialization logic and simple conversions from scalar types, resulting 343 | * in an all in one function that can take care of storing the C value of 344 | * a Lua value inside a piece of memory 345 | */ 346 | void from_lua(lua_State *L, ast::c_type const &decl, void *stor, int idx); 347 | 348 | void get_global(lua_State *L, lib::c_lib const *dl, const char *sname); 349 | void set_global(lua_State *L, lib::c_lib const *dl, char const *sname, int idx); 350 | 351 | void make_cdata(lua_State *L, ast::c_type const &decl, int rule, int idx); 352 | 353 | static inline bool metatype_getfield(lua_State *L, int mt, char const *fname) { 354 | luaL_getmetatable(L, lua::CFFI_CDATA_MT); 355 | lua_getfield(L, -1, "__ffi_metatypes"); 356 | lua_rawgeti(L, -1, mt); 357 | if (lua_istable(L, -1)) { 358 | lua_getfield(L, -1, fname); 359 | if (!lua_isnil(L, -1)) { 360 | lua_insert(L, -4); 361 | lua_pop(L, 3); 362 | return true; 363 | } else { 364 | lua_pop(L, 1); 365 | } 366 | } 367 | lua_pop(L, 3); 368 | return false; 369 | } 370 | 371 | template 372 | static inline bool test_arith(lua_State *L, int idx, T &out) { 373 | auto *cd = testcdata(L, idx); 374 | if (!cd) { 375 | if (util::is_int::value) { 376 | if (lua_type(L, idx) == LUA_TNUMBER) { 377 | out = T(lua_tointeger(L, idx)); 378 | return true; 379 | } else { 380 | return false; 381 | } 382 | } 383 | if (lua_type(L, idx) == LUA_TNUMBER) { 384 | out = T(lua_tointeger(L, idx)); 385 | return true; 386 | } 387 | return false; 388 | } 389 | auto gf = [](int itp, void *av, T &rv) { 390 | switch (itp) { 391 | case ast::C_BUILTIN_ENUM: 392 | /* TODO: large enums */ 393 | rv = T(*static_cast(av)); break; 394 | case ast::C_BUILTIN_BOOL: 395 | rv = T(*static_cast(av)); break; 396 | case ast::C_BUILTIN_CHAR: 397 | rv = T(*static_cast(av)); break; 398 | case ast::C_BUILTIN_SCHAR: 399 | rv = T(*static_cast(av)); break; 400 | case ast::C_BUILTIN_UCHAR: 401 | rv = T(*static_cast(av)); break; 402 | case ast::C_BUILTIN_SHORT: 403 | rv = T(*static_cast(av)); break; 404 | case ast::C_BUILTIN_USHORT: 405 | rv = T(*static_cast(av)); break; 406 | case ast::C_BUILTIN_INT: 407 | rv = T(*static_cast(av)); break; 408 | case ast::C_BUILTIN_UINT: 409 | rv = T(*static_cast(av)); break; 410 | case ast::C_BUILTIN_LONG: 411 | rv = T(*static_cast(av)); break; 412 | case ast::C_BUILTIN_ULONG: 413 | rv = T(*static_cast(av)); break; 414 | case ast::C_BUILTIN_LLONG: 415 | rv = T(*static_cast(av)); break; 416 | case ast::C_BUILTIN_ULLONG: 417 | rv = T(*static_cast(av)); break; 418 | case ast::C_BUILTIN_FLOAT: 419 | rv = T(*static_cast(av)); break; 420 | case ast::C_BUILTIN_DOUBLE: 421 | rv = T(*static_cast(av)); break; 422 | case ast::C_BUILTIN_LDOUBLE: 423 | rv = T(*static_cast(av)); break; 424 | default: 425 | return false; 426 | } 427 | return true; 428 | }; 429 | int tp = cd->decl.type(); 430 | if (cd->decl.is_ref()) { 431 | if (gf(tp, *static_cast(cd->as_ptr()), out)) { 432 | return true; 433 | } 434 | } else if (gf(tp, cd->as_ptr(), out)) { 435 | return true; 436 | } 437 | return false; 438 | } 439 | 440 | template 441 | static inline T check_arith(lua_State *L, int idx) { 442 | T outv{}; 443 | if (!test_arith(L, idx, outv)) { 444 | lua::type_error( 445 | L, idx, util::is_int::value ? "integer" : "number" 446 | ); 447 | } 448 | return outv; 449 | } 450 | 451 | static inline ast::c_expr_type check_arith_expr( 452 | lua_State *L, int idx, ast::c_value &iv 453 | ) { 454 | auto *cd = testcdata(L, idx); 455 | if (!cd) { 456 | /* some logic for conversions of lua numbers into cexprs */ 457 | #if LUA_VERSION_NUM >= 503 458 | static_assert( 459 | sizeof(lua_Integer) <= sizeof(long long), 460 | "invalid lua_Integer format" 461 | ); 462 | if (lua_isinteger(L, idx)) { 463 | if (util::is_signed::value) { 464 | if (sizeof(lua_Integer) <= sizeof(int)) { 465 | iv.i = int(lua_tointeger(L, idx)); 466 | return ast::c_expr_type::INT; 467 | } else if (sizeof(lua_Integer) <= sizeof(long)) { 468 | iv.l = long(lua_tointeger(L, idx)); 469 | return ast::c_expr_type::LONG; 470 | } else { 471 | using LL = long long; 472 | iv.ll = LL(lua_tointeger(L, idx)); 473 | return ast::c_expr_type::LLONG; 474 | } 475 | } else { 476 | if (sizeof(lua_Integer) <= sizeof(unsigned int)) { 477 | using U = unsigned int; 478 | iv.u = U(lua_tointeger(L, idx)); 479 | return ast::c_expr_type::UINT; 480 | } else if (sizeof(lua_Integer) <= sizeof(unsigned long)) { 481 | using UL = unsigned long; 482 | iv.ul = UL(lua_tointeger(L, idx)); 483 | return ast::c_expr_type::ULONG; 484 | } else { 485 | using ULL = unsigned long long; 486 | iv.ull = ULL(lua_tointeger(L, idx)); 487 | return ast::c_expr_type::ULLONG; 488 | } 489 | } 490 | } 491 | #endif 492 | static_assert( 493 | util::is_int::value 494 | ? (sizeof(lua_Number) <= sizeof(long long)) 495 | : (sizeof(lua_Number) <= sizeof(long double)), 496 | "invalid lua_Number format" 497 | ); 498 | auto n = luaL_checknumber(L, idx); 499 | if (util::is_int::value) { 500 | if (util::is_signed::value) { 501 | if (sizeof(lua_Number) <= sizeof(int)) { 502 | iv.i = int(n); 503 | return ast::c_expr_type::INT; 504 | } else if (sizeof(lua_Number) <= sizeof(long)) { 505 | iv.l = long(n); 506 | return ast::c_expr_type::LONG; 507 | } else { 508 | using LL = long long; 509 | iv.ll = LL(n); 510 | return ast::c_expr_type::LLONG; 511 | } 512 | } else { 513 | if (sizeof(lua_Number) <= sizeof(unsigned int)) { 514 | using U = unsigned int; 515 | iv.u = U(n); 516 | return ast::c_expr_type::UINT; 517 | } else if (sizeof(lua_Number) <= sizeof(unsigned long)) { 518 | using UL = unsigned long; 519 | iv.ul = UL(n); 520 | return ast::c_expr_type::ULONG; 521 | } else { 522 | using ULL = unsigned long long; 523 | iv.ull = ULL(n); 524 | return ast::c_expr_type::ULLONG; 525 | } 526 | } 527 | } else if (sizeof(lua_Number) <= sizeof(float)) { 528 | iv.f = float(n); 529 | return ast::c_expr_type::FLOAT; 530 | } else if (sizeof(lua_Number) <= sizeof(double)) { 531 | iv.d = double(n); 532 | return ast::c_expr_type::DOUBLE; 533 | } 534 | using LD = long double; 535 | iv.ld = LD(n); 536 | return ast::c_expr_type::LDOUBLE; 537 | } 538 | auto gf = [](int itp, void *av, ast::c_value &v) { 539 | switch (itp) { 540 | case ast::C_BUILTIN_ENUM: 541 | /* TODO: large enums */ 542 | v.i = *static_cast(av); 543 | return ast::c_expr_type::INT; 544 | case ast::C_BUILTIN_BOOL: 545 | v.i = *static_cast(av); 546 | return ast::c_expr_type::INT; 547 | case ast::C_BUILTIN_CHAR: 548 | v.i = *static_cast(av); 549 | return ast::c_expr_type::INT; 550 | case ast::C_BUILTIN_SCHAR: 551 | v.i = *static_cast(av); 552 | return ast::c_expr_type::INT; 553 | case ast::C_BUILTIN_UCHAR: 554 | v.i = *static_cast(av); 555 | return ast::c_expr_type::INT; 556 | case ast::C_BUILTIN_SHORT: 557 | v.i = *static_cast(av); 558 | return ast::c_expr_type::INT; 559 | case ast::C_BUILTIN_USHORT: 560 | v.i = *static_cast(av); 561 | return ast::c_expr_type::INT; 562 | case ast::C_BUILTIN_INT: 563 | v.i = *static_cast(av); 564 | return ast::c_expr_type::INT; 565 | case ast::C_BUILTIN_UINT: 566 | v.u = *static_cast(av); 567 | return ast::c_expr_type::UINT; 568 | case ast::C_BUILTIN_LONG: 569 | v.l = *static_cast(av); 570 | return ast::c_expr_type::LONG; 571 | case ast::C_BUILTIN_ULONG: 572 | v.ul = *static_cast(av); 573 | return ast::c_expr_type::ULONG; 574 | case ast::C_BUILTIN_LLONG: 575 | v.ll = *static_cast(av); 576 | return ast::c_expr_type::LLONG; 577 | case ast::C_BUILTIN_ULLONG: 578 | v.ull = *static_cast(av); 579 | return ast::c_expr_type::ULLONG; 580 | case ast::C_BUILTIN_FLOAT: 581 | v.f = *static_cast(av); 582 | return ast::c_expr_type::FLOAT; 583 | case ast::C_BUILTIN_DOUBLE: 584 | v.d = *static_cast(av); 585 | return ast::c_expr_type::DOUBLE; 586 | case ast::C_BUILTIN_LDOUBLE: 587 | v.ld = *static_cast(av); 588 | return ast::c_expr_type::LDOUBLE; 589 | default: 590 | return ast::c_expr_type::INVALID; 591 | } 592 | }; 593 | ast::c_expr_type ret; 594 | int tp = cd->decl.type(); 595 | if (cd->decl.is_ref()) { 596 | ret = gf(tp, *static_cast(cd->as_ptr()), iv); 597 | } else { 598 | ret = gf(tp, cd->as_ptr(), iv); 599 | } 600 | if (ret == ast::c_expr_type::INVALID) { 601 | luaL_checknumber(L, idx); 602 | } 603 | /* unreachable */ 604 | return ret; 605 | } 606 | 607 | static inline cdata &make_cdata_arith( 608 | lua_State *L, ast::c_expr_type et, ast::c_value const &cv 609 | ) { 610 | auto bt = ast::to_builtin_type(et); 611 | if (bt == ast::C_BUILTIN_INVALID) { 612 | luaL_error(L, "invalid value type"); 613 | } 614 | auto tp = ast::c_type{bt, 0}; 615 | auto as = tp.alloc_size(); 616 | auto &cd = newcdata(L, util::move(tp), as); 617 | std::memcpy(cd.as_ptr(), &cv, as); 618 | return cd; 619 | } 620 | 621 | static inline char const *lua_serialize(lua_State *L, int idx) { 622 | auto *cd = testcdata(L, idx); 623 | if (cd) { 624 | /* it's ok to mess up the lua stack, this is only used for errors */ 625 | cd->decl.serialize(L); 626 | return lua_tostring(L, -1); 627 | } 628 | return lua_typename(L, lua_type(L, idx)); 629 | } 630 | 631 | } /* namespace ffi */ 632 | 633 | #endif /* FFI_HH */ 634 | -------------------------------------------------------------------------------- /src/lib.cc: -------------------------------------------------------------------------------- 1 | #include "platform.hh" 2 | 3 | #include 4 | 5 | #ifdef FFI_USE_DLFCN 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #endif 12 | 13 | #include "lib.hh" 14 | 15 | #if FFI_OS == FFI_OS_WINDOWS 16 | #include 17 | #endif 18 | 19 | namespace lib { 20 | 21 | static int make_cache(lua_State *L) { 22 | lua_newtable(L); 23 | return luaL_ref(L, LUA_REGISTRYINDEX); 24 | } 25 | 26 | #ifdef FFI_USE_DLFCN 27 | 28 | #ifdef FFI_OS_CYGWIN 29 | # define FFI_DL_SOPREFIX "cyg" 30 | #else 31 | # define FFI_DL_SOPREFIX "lib" 32 | #endif 33 | 34 | #if FFI_OS == FFI_OS_OSX 35 | # define FFI_DL_SONAME "%s.dylib" 36 | #elif defined(FFI_OS_CYGWIN) 37 | # define FFI_DL_SONAME "%s.dll" 38 | #else 39 | # define FFI_DL_SONAME "%s.so" 40 | #endif 41 | 42 | #if defined(RTLD_DEFAULT) 43 | # define FFI_DL_DEFAULT RTLD_DEFAULT 44 | #elif FFI_OS == FFI_OS_BSD || FFI_OS == FFI_OS_OSX 45 | # define FFI_DL_DEFAULT reinterpret_cast(std::intptr_t(-2)) 46 | #else 47 | # define FFI_DL_DEFAULT nullptr 48 | #endif 49 | 50 | /* low level dlfcn handling */ 51 | 52 | static handle open(char const *path, bool global) { 53 | return dlopen(path, RTLD_LAZY | (global ? RTLD_GLOBAL : RTLD_LOCAL)); 54 | } 55 | 56 | void close(c_lib *cl, lua_State *L) { 57 | luaL_unref(L, LUA_REGISTRYINDEX, cl->cache); 58 | cl->cache = LUA_REFNIL; 59 | if (cl->h != FFI_DL_DEFAULT) { 60 | dlclose(cl->h); 61 | } 62 | cl->h = nullptr; 63 | } 64 | 65 | static void *get_sym(c_lib const *cl, char const *name) { 66 | return dlsym(cl->h, name); 67 | } 68 | 69 | /* library resolution */ 70 | 71 | static char const *resolve_name(lua_State *L, char const *name) { 72 | if (std::strchr(name, '/') 73 | #ifdef FFI_OS_CYGWIN 74 | || std::strchr(name, '\\') 75 | #endif 76 | ) { 77 | /* input is a path */ 78 | lua_pushstring(L, name); 79 | return lua_tostring(L, -1); 80 | } 81 | if (!std::strchr(name, '.')) { 82 | /* name without ext */ 83 | lua_pushfstring(L, FFI_DL_SONAME, name); 84 | } else { 85 | lua_pushstring(L, name); 86 | #ifdef FFI_OS_CYGWIN 87 | /* name with ext on cygwin can be used directly (no prefix) */ 88 | return lua_tostring(L, -1); 89 | #endif 90 | } 91 | if (!std::strncmp(name, FFI_DL_SOPREFIX, sizeof(FFI_DL_SOPREFIX) - 1)) { 92 | /* lib/cyg prefix found */ 93 | return lua_tostring(L, -1); 94 | } 95 | /* no prefix, so prepend it */ 96 | lua_pushliteral(L, FFI_DL_SOPREFIX); 97 | lua_insert(L, -2); 98 | lua_concat(L, 2); 99 | return lua_tostring(L, -1); 100 | } 101 | 102 | /* ldscript handling logic generally adapted from luajit... */ 103 | 104 | static bool check_ldscript(char const *buf, char const *&beg, char const *&end) { 105 | char const *p; 106 | if (( 107 | !std::strncmp(buf, "GROUP", 5) || !std::strncmp(buf, "INPUT", 5) 108 | ) && (p = std::strchr(buf, '('))) { 109 | while (*++p == ' ') {} 110 | char const *e = p; 111 | while (*e && (*e != ' ') && (*e != ')')) { 112 | ++e; 113 | } 114 | beg = p; 115 | end = e; 116 | return true; 117 | } 118 | return false; 119 | } 120 | 121 | static bool resolve_ldscript( 122 | lua_State *L, char const *nbeg, char const *nend 123 | ) { 124 | lua_pushlstring(L, nbeg, nend - nbeg); 125 | FILE *f = std::fopen(lua_tostring(L, -1), "r"); 126 | lua_pop(L, 1); 127 | if (!f) { 128 | return false; 129 | } 130 | char buf[256]; 131 | if (!std::fgets(buf, sizeof(buf), f)) { 132 | fclose(f); 133 | return false; 134 | } 135 | char const *pb, *pe; 136 | bool got = false; 137 | if (!std::strncmp(buf, "/* GNU ld script", 16)) { 138 | while (fgets(buf, sizeof(buf), f)) { 139 | got = check_ldscript(buf, pb, pe); 140 | if (got) { 141 | break; 142 | } 143 | } 144 | } else { 145 | got = check_ldscript(buf, pb, pe); 146 | } 147 | std::fclose(f); 148 | if (got) { 149 | lua_pushlstring(L, pb, pe - pb); 150 | } 151 | return got; 152 | } 153 | 154 | void load(c_lib *cl, char const *path, lua_State *L, bool global) { 155 | if (!path) { 156 | /* primary namespace */ 157 | cl->h = FFI_DL_DEFAULT; 158 | cl->cache = make_cache(L); 159 | lua::mark_lib(L); 160 | return; 161 | } 162 | handle h = open(resolve_name(L, path), global); 163 | lua_pop(L, 1); 164 | if (h) { 165 | lua::mark_lib(L); 166 | cl->h = h; 167 | cl->cache = make_cache(L); 168 | return; 169 | } 170 | char const *err = dlerror(), *e; 171 | if ( 172 | err && (*err == '/') && (e = std::strchr(err, ':')) && 173 | resolve_ldscript(L, err, e) 174 | ) { 175 | h = open(lua_tostring(L, -1), global); 176 | lua_pop(L, 1); 177 | if (h) { 178 | lua::mark_lib(L); 179 | cl->h = h; 180 | cl->cache = make_cache(L); 181 | return; 182 | } 183 | err = dlerror(); 184 | } 185 | luaL_error(L, err ? err : "dlopen() failed"); 186 | } 187 | 188 | bool is_c(c_lib const *cl) { 189 | return (cl->h == FFI_DL_DEFAULT); 190 | } 191 | 192 | #elif FFI_OS == FFI_OS_WINDOWS /* FFI_USE_DLFCN */ 193 | 194 | /* This is generally adapted from LuaJIT source code. 195 | * 196 | * Didn't bother with the UWP bits yet; that may be added later if anybody 197 | * actually needs that (Lua does not support dynamic modules with UWP though, 198 | * so only the library version would work) 199 | */ 200 | 201 | #define WIN32_LEAN_AND_MEAN 202 | #include 203 | 204 | #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 205 | #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4 206 | #define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2 207 | BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*); 208 | #endif 209 | 210 | #define FFI_DL_DEFAULT reinterpret_cast(std::intptr_t(-1)) 211 | 212 | enum { 213 | FFI_DL_HANDLE_EXE = 0, 214 | FFI_DL_HANDLE_DLL, 215 | FFI_DL_HANDLE_CRT, 216 | FFI_DL_HANDLE_KERNEL32, 217 | FFI_DL_HANDLE_USER32, 218 | FFI_DL_HANDLE_GDI32, 219 | FFI_DL_HANDLE_MAX 220 | }; 221 | 222 | static void *ffi_dl_handle[FFI_DL_HANDLE_MAX] = {0}; 223 | 224 | static void dl_error(lua_State *L, char const *fmt, char const *name) { 225 | auto err = GetLastError(); 226 | wchar_t wbuf[128]; 227 | char buf[256]; 228 | if (!FormatMessageW( 229 | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, 230 | nullptr, err, 0, wbuf, sizeof(wbuf) / sizeof(wchar_t), nullptr 231 | ) || !WideCharToMultiByte( 232 | CP_ACP, 0, wbuf, 128, buf, 256, nullptr, nullptr 233 | )) { 234 | buf[0] = '\0'; 235 | } 236 | luaL_error(L, fmt, name, buf); 237 | } 238 | 239 | static bool dl_need_ext(char const *s) { 240 | while (*s) { 241 | if ((*s == '/') || (*s == '\\') || (*s == '.')) { 242 | return false; 243 | } 244 | ++s; 245 | } 246 | return true; 247 | } 248 | 249 | static char const *dl_ext_name(lua_State *L, char const *name) { 250 | lua_pushstring(L, name); 251 | if (dl_need_ext(name)) { 252 | lua_pushliteral(L, ".dll"); 253 | lua_concat(L, 2); 254 | } 255 | return lua_tostring(L, -1); 256 | } 257 | 258 | void load(c_lib *cl, char const *path, lua_State *L, bool) { 259 | if (!path) { 260 | /* primary namespace */ 261 | cl->h = FFI_DL_DEFAULT; 262 | cl->cache = make_cache(L); 263 | lua::mark_lib(L); 264 | return; 265 | } 266 | auto olderr = GetLastError(); 267 | handle h = static_cast( 268 | LoadLibraryExA(dl_ext_name(L, path), nullptr, 0) 269 | ); 270 | lua_pop(L, 1); 271 | if (!h) { 272 | dl_error(L, "cannot load module '%s': %s", path); 273 | } 274 | SetLastError(olderr); 275 | cl->h = h; 276 | cl->cache = make_cache(L); 277 | lua::mark_lib(L); 278 | } 279 | 280 | void close(c_lib *cl, lua_State *L) { 281 | luaL_unref(L, LUA_REGISTRYINDEX, cl->cache); 282 | cl->cache = LUA_REFNIL; 283 | if (cl->h == FFI_DL_DEFAULT) { 284 | for (int i = FFI_DL_HANDLE_KERNEL32; i < FFI_DL_HANDLE_MAX; ++i) { 285 | void *p = ffi_dl_handle[i]; 286 | if (p) { 287 | ffi_dl_handle[i] = nullptr; 288 | FreeLibrary(static_cast(p)); 289 | } 290 | } 291 | } else if (cl->h) { 292 | FreeLibrary(static_cast(cl->h)); 293 | } 294 | } 295 | 296 | static void *get_sym(c_lib const *cl, char const *name) { 297 | if (cl->h != FFI_DL_DEFAULT) { 298 | return util::pun( 299 | GetProcAddress(static_cast(cl->h), name) 300 | ); 301 | } 302 | for (std::size_t i = 0; i < FFI_DL_HANDLE_MAX; ++i) { 303 | if (!ffi_dl_handle[i]) { 304 | HMODULE h = nullptr; 305 | char const *dlh = nullptr; 306 | switch (i) { 307 | case FFI_DL_HANDLE_EXE: 308 | GetModuleHandleExA( 309 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, nullptr, &h 310 | ); 311 | break; 312 | case FFI_DL_HANDLE_CRT: { 313 | dlh = util::pun(&_fmode); 314 | goto handle_dll; 315 | } 316 | case FFI_DL_HANDLE_DLL: 317 | dlh = util::pun(ffi_dl_handle); 318 | handle_dll: 319 | GetModuleHandleExA( 320 | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 321 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 322 | dlh, &h 323 | ); 324 | break; 325 | case FFI_DL_HANDLE_KERNEL32: 326 | h = LoadLibraryExA("kernel32.dll", nullptr, 0); 327 | break; 328 | case FFI_DL_HANDLE_USER32: 329 | h = LoadLibraryExA("user32.dll", nullptr, 0); 330 | break; 331 | case FFI_DL_HANDLE_GDI32: 332 | h = LoadLibraryExA("gdi32.dll", nullptr, 0); 333 | break; 334 | default: 335 | break; 336 | } 337 | if (!h) { 338 | continue; 339 | } 340 | ffi_dl_handle[i] = static_cast(h); 341 | } 342 | HMODULE h = static_cast(ffi_dl_handle[i]); 343 | auto paddr = GetProcAddress(h, name); 344 | if (paddr) { 345 | return util::pun(paddr); 346 | } 347 | } 348 | return nullptr; 349 | } 350 | 351 | bool is_c(c_lib const *cl) { 352 | return (cl->h == FFI_DL_DEFAULT); 353 | } 354 | 355 | #else 356 | 357 | void load(c_lib *, char const *, lua_State *L, bool) { 358 | luaL_error(L, "no support for dynamic library loading on this target"); 359 | return nullptr; 360 | } 361 | 362 | void close(c_lib *, lua_State *) { 363 | } 364 | 365 | static void *get_sym(c_lib const *, char const *) { 366 | return nullptr; 367 | } 368 | 369 | bool is_c(c_lib const *) { 370 | return true; 371 | } 372 | 373 | #endif /* FFI_USE_DLFCN, FFI_OS == FFI_OS_WINDOWS */ 374 | 375 | void *get_sym(c_lib const *cl, lua_State *L, char const *name) { 376 | lua_rawgeti(L, LUA_REGISTRYINDEX, cl->cache); 377 | lua_getfield(L, -1, name); 378 | if (lua_isnil(L, -1)) { 379 | lua_pop(L, 1); 380 | void *p = get_sym(cl, name); 381 | if (!p) { 382 | lua_pop(L, 1); 383 | luaL_error(L, "undefined symbol: %s", name); 384 | return nullptr; 385 | } 386 | lua_pushlightuserdata(L, p); 387 | lua_setfield(L, -2, name); 388 | lua_pop(L, 1); 389 | return p; 390 | } 391 | void *p = lua_touserdata(L, -1); 392 | lua_pop(L, 1); 393 | return p; 394 | } 395 | 396 | } /* namespace lib */ 397 | -------------------------------------------------------------------------------- /src/lib.hh: -------------------------------------------------------------------------------- 1 | #ifndef LIB_HH 2 | #define LIB_HH 3 | 4 | #include "lua.hh" 5 | 6 | namespace lib { 7 | 8 | using handle = void *; 9 | using func = void (*)(); 10 | 11 | struct c_lib { 12 | handle h; 13 | int cache; 14 | }; 15 | 16 | void load(c_lib *cl, char const *path, lua_State *L, bool global = false); 17 | 18 | void close(c_lib *cl, lua_State *L); 19 | 20 | void *get_sym(c_lib const *cl, lua_State *L, char const *name); 21 | 22 | bool is_c(c_lib const *cl); 23 | 24 | } /* namespace lib */ 25 | 26 | #endif /* LIB_HH */ 27 | -------------------------------------------------------------------------------- /src/libffi.hh: -------------------------------------------------------------------------------- 1 | /* The point of this header is to abstract away how libffi is included, 2 | * as there may be multiple ways to include its primary header, as well 3 | * as include some basic utilities that deal with libffi bits. 4 | */ 5 | 6 | #ifndef LIBFFI_HH 7 | #define LIBFFI_HH 8 | 9 | #include 10 | #include 11 | 12 | #include "platform.hh" 13 | #include "util.hh" 14 | 15 | /* Force static linkage against libffi on Windows unless overridden */ 16 | #if defined(FFI_WINDOWS_ABI) && !defined(HAVE_LIBFFI_DLLIMPORT) 17 | # define FFI_BUILDING 1 18 | #endif 19 | 20 | #if defined(FFI_DIAGNOSTIC_PRAGMA_CLANG) 21 | #pragma clang diagnostic push 22 | #pragma clang diagnostic ignored "-Wlanguage-extension-token" 23 | #endif 24 | 25 | #include 26 | 27 | #if defined(FFI_DIAGNOSTIC_PRAGMA_CLANG) 28 | #pragma clang diagnostic pop 29 | #endif 30 | 31 | namespace ffi { 32 | 33 | namespace detail { 34 | struct ffi_stor { 35 | alignas(alignof(util::max_aligned_t)) 36 | unsigned char data[sizeof(util::biggest_t)]; 37 | }; 38 | 39 | template 40 | static inline ffi_type *ffi_int() { 41 | bool is_signed = util::is_signed::value; 42 | switch (sizeof(T)) { 43 | case 8: 44 | return is_signed ? &ffi_type_sint64 : &ffi_type_uint64; 45 | case 4: 46 | return is_signed ? &ffi_type_sint32 : &ffi_type_uint32; 47 | case 2: 48 | return is_signed ? &ffi_type_sint16 : &ffi_type_uint16; 49 | case 1: 50 | return is_signed ? &ffi_type_sint8 : &ffi_type_uint8; 51 | default: 52 | break; 53 | } 54 | assert(false); 55 | return nullptr; 56 | } 57 | } /* namespace detail */ 58 | 59 | using scalar_stor_t = detail::ffi_stor; 60 | 61 | /* compile-time mappings from builtin types to libffi types 62 | * 63 | * this also allows catching bad types at compile time 64 | */ 65 | 66 | template struct ffi_traits; 67 | 68 | template<> struct ffi_traits { 69 | static ffi_type *type() { return &ffi_type_void; } 70 | }; 71 | 72 | template struct ffi_traits { 73 | static ffi_type *type() { return &ffi_type_pointer; } 74 | }; 75 | 76 | template struct ffi_traits { 77 | static ffi_type *type() { return &ffi_type_pointer; } 78 | }; 79 | 80 | template struct ffi_traits { 81 | static ffi_type *type() { return &ffi_type_pointer; } 82 | }; 83 | 84 | template<> struct ffi_traits { 85 | static ffi_type *type() { return &ffi_type_uchar; } 86 | }; 87 | 88 | template<> struct ffi_traits { 89 | static ffi_type *type() { return detail::ffi_int(); } 90 | }; 91 | 92 | template<> struct ffi_traits { 93 | static ffi_type *type() { return detail::ffi_int(); } 94 | }; 95 | 96 | template<> struct ffi_traits { 97 | static ffi_type *type() { return detail::ffi_int(); } 98 | }; 99 | 100 | template<> struct ffi_traits { 101 | static ffi_type *type() { return detail::ffi_int(); } 102 | }; 103 | 104 | template<> struct ffi_traits { 105 | static ffi_type *type() { return detail::ffi_int(); } 106 | }; 107 | 108 | template<> struct ffi_traits { 109 | static ffi_type *type() { return detail::ffi_int(); } 110 | }; 111 | 112 | template<> struct ffi_traits { 113 | static ffi_type *type() { return detail::ffi_int(); } 114 | }; 115 | 116 | template<> struct ffi_traits { 117 | static ffi_type *type() { return detail::ffi_int(); } 118 | }; 119 | 120 | template<> struct ffi_traits { 121 | static ffi_type *type() { return detail::ffi_int(); } 122 | }; 123 | 124 | template<> struct ffi_traits { 125 | static ffi_type *type() { return detail::ffi_int(); } 126 | }; 127 | 128 | template<> struct ffi_traits { 129 | static ffi_type *type() { return detail::ffi_int(); } 130 | }; 131 | 132 | template<> struct ffi_traits { 133 | static ffi_type *type() { return detail::ffi_int(); } 134 | }; 135 | 136 | template<> struct ffi_traits { 137 | static ffi_type *type() { return detail::ffi_int(); } 138 | }; 139 | 140 | template<> struct ffi_traits { 141 | static ffi_type *type() { return detail::ffi_int(); } 142 | }; 143 | 144 | template<> struct ffi_traits { 145 | static ffi_type *type() { return &ffi_type_float; } 146 | }; 147 | 148 | template<> struct ffi_traits { 149 | static ffi_type *type() { return &ffi_type_double; } 150 | }; 151 | 152 | template<> struct ffi_traits { 153 | static ffi_type *type() { return &ffi_type_longdouble; } 154 | }; 155 | 156 | template 157 | struct ffi_traits: ffi_traits {}; 158 | 159 | template 160 | struct ffi_traits: ffi_traits {}; 161 | 162 | template 163 | struct ffi_traits: ffi_traits {}; 164 | 165 | } /* namespace ffi */ 166 | 167 | #endif /* LIBFFI_HH */ 168 | -------------------------------------------------------------------------------- /src/lua.cc: -------------------------------------------------------------------------------- 1 | #include "lua.hh" 2 | 3 | namespace lua { 4 | 5 | } /* namespace lua */ 6 | -------------------------------------------------------------------------------- /src/lua.hh: -------------------------------------------------------------------------------- 1 | #ifndef LUA_HH 2 | #define LUA_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "platform.hh" 8 | #include "util.hh" 9 | 10 | #if defined(FFI_DIAGNOSTIC_PRAGMA_CLANG) 11 | #pragma clang diagnostic push 12 | #pragma clang diagnostic ignored "-Wold-style-cast" 13 | #elif defined(FFI_DIAGNOSTIC_PRAGMA_GCC) 14 | #pragma GCC diagnostic push 15 | #pragma GCC diagnostic ignored "-Wold-style-cast" 16 | #endif 17 | 18 | /* force lua API to be marked with dllimport */ 19 | #if defined(FFI_WINDOWS_ABI) 20 | #define LUA_BUILD_AS_DLL 21 | #endif 22 | 23 | #include 24 | 25 | #if defined(FFI_DIAGNOSTIC_PRAGMA_CLANG) 26 | #pragma clang diagnostic pop 27 | #elif defined(FFI_DIAGNOSTIC_PRAGMA_GCC) 28 | #pragma GCC diagnostic pop 29 | #endif 30 | 31 | #if LUA_VERSION_NUM < 501 32 | 33 | #error This Lua version is not supported. 34 | 35 | #elif LUA_VERSION_NUM == 501 36 | 37 | /* lua 5.1 compat bits 38 | * 39 | * defines are used in case e.g. luajit is used which has these funcs 40 | */ 41 | 42 | static inline void luaL_setmetatable52(lua_State *L, char const *tname) { 43 | luaL_getmetatable(L, tname); 44 | lua_setmetatable(L, -2); 45 | } 46 | 47 | #ifdef luaL_setmetatable 48 | #undef luaL_setmetatable 49 | #endif 50 | #define luaL_setmetatable luaL_setmetatable52 51 | 52 | static inline void *luaL_testudata52(lua_State *L, int ud, char const *tname) { 53 | void *p = lua_touserdata(L, ud); 54 | if (!p || !lua_getmetatable(L, ud)) { 55 | return nullptr; 56 | } 57 | lua_getfield(L, LUA_REGISTRYINDEX, tname); 58 | if (lua_rawequal(L, -1, -2)) { 59 | lua_pop(L, 2); 60 | return p; 61 | } 62 | lua_pop(L, 2); 63 | return nullptr; 64 | } 65 | 66 | #ifdef luaL_testudata 67 | #undef luaL_testudata 68 | #endif 69 | #define luaL_testudata luaL_testudata52 70 | 71 | static inline std::size_t lua_rawlen52(lua_State *L, int index) { 72 | return lua_objlen(L, index); 73 | } 74 | 75 | #ifdef lua_rawlen 76 | #undef lua_rawlen 77 | #endif 78 | #define lua_rawlen lua_rawlen52 79 | 80 | static inline void luaL_newlib52(lua_State *L, luaL_Reg const l[]) { 81 | lua_newtable(L); 82 | luaL_register(L, nullptr, l); 83 | } 84 | 85 | #ifdef luaL_newlib 86 | #undef luaL_newlib 87 | #endif 88 | #define luaL_newlib luaL_newlib52 89 | 90 | #endif /* LUA_VERSION_NUM == 501 */ 91 | 92 | #if LUA_VERSION_NUM < 503 93 | 94 | #ifdef lua_isinteger 95 | #undef lua_isinteger 96 | #endif 97 | #define lua_isinteger(L, idx) int(0) 98 | 99 | #endif /* LUA_VERSION_NUM == 503 */ 100 | 101 | namespace lua { 102 | 103 | static constexpr int CFFI_CTYPE_TAG = -128; 104 | static constexpr char const CFFI_CDATA_MT[] = "cffi_cdata_handle"; 105 | static constexpr char const CFFI_LIB_MT[] = "cffi_lib_handle"; 106 | static constexpr char const CFFI_DECL_STOR[] = "cffi_decl_stor"; 107 | static constexpr char const CFFI_PARSER_STATE[] = "cffi_parser_state"; 108 | 109 | template 110 | static T *touserdata(lua_State *L, int index) { 111 | return static_cast(lua_touserdata(L, index)); 112 | } 113 | 114 | static inline int type_error(lua_State *L, int narg, char const *tname) { 115 | lua_pushfstring( 116 | L, "%s expected, got %s", tname, lua_typename(L, lua_type(L, narg)) 117 | ); 118 | luaL_argcheck(L, false, narg, lua_tostring(L, -1)); 119 | return 0; 120 | } 121 | 122 | static inline void mark_cdata(lua_State *L) { 123 | luaL_setmetatable(L, CFFI_CDATA_MT); 124 | } 125 | 126 | static inline void mark_lib(lua_State *L) { 127 | luaL_setmetatable(L, CFFI_LIB_MT); 128 | } 129 | 130 | #if LUA_VERSION_NUM < 503 131 | /* 5.2 and older uses a simpler (unexposed) alignment */ 132 | union user_align_t { void *p; double d; long l; }; 133 | #elif LUA_VERSION_NUM < 504 134 | /* 5.3 does not expose this, so mirror its guts */ 135 | union user_align_t { lua_Number n; lua_Integer i; void *p; double d; long l; }; 136 | #else 137 | /* 5.4+ has the configured alignment in luaconf */ 138 | union user_align_t { LUAI_MAXALIGN; }; 139 | #endif 140 | 141 | } /* namespace lua */ 142 | 143 | #define LUA_BUG_MSG(L, msg) \ 144 | lua_pushfstring(L, "%s:%s: bug: %s", __FILE__, __LINE__, msg) 145 | 146 | #endif /* LUA_HH */ 147 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | /* This file exists as it may be compiled in multiple versions depending on 2 | * how the entry point is exported. This is particularly true on Windows, where 3 | * when compiling as DLL, the 'luaopen_cffi' symbol must be dllexport, while 4 | * for a static lib it must be unmarked. 5 | * 6 | * On Unix-like platforms, all symbols are hidden by default if supported by 7 | * the compiler, with 'luaopen_cffi' being the sole visible symbol, this is 8 | * not dependent on how we're compiling it. 9 | */ 10 | 11 | #include "lua.hh" 12 | 13 | #if defined(__CYGWIN__) || (defined(_WIN32) && !defined(_XBOX_VER)) 14 | # ifdef CFFI_LUA_DLL 15 | # define CFFI_LUA_EXPORT __declspec(dllexport) 16 | # else 17 | # define CFFI_LUA_EXPORT 18 | # endif 19 | #else 20 | # if defined(__GNUC__) && (__GNUC__ >= 4) 21 | # define CFFI_LUA_EXPORT __attribute__((visibility("default"))) 22 | # else 23 | # define CFFI_LUA_EXPORT 24 | # endif 25 | #endif 26 | 27 | void ffi_module_open(lua_State *L); 28 | 29 | extern "C" CFFI_LUA_EXPORT int luaopen_cffi(lua_State *L) { 30 | ffi_module_open(L); 31 | return 1; 32 | } 33 | -------------------------------------------------------------------------------- /src/parser.hh: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_HH 2 | #define PARSER_HH 3 | 4 | #include "lua.hh" 5 | #include "ast.hh" 6 | 7 | namespace parser { 8 | 9 | void init(lua_State *L); 10 | 11 | void parse( 12 | lua_State *L, char const *input, char const *iend = nullptr, int paridx = -1 13 | ); 14 | 15 | ast::c_type parse_type( 16 | lua_State *L, char const *input, char const *iend = nullptr, int paridx = -1 17 | ); 18 | 19 | ast::c_expr_type parse_number( 20 | lua_State *L, ast::c_value &v, char const *input, char const *iend = nullptr 21 | ); 22 | 23 | } /* namespace parser */ 24 | 25 | #endif /* PARSER_HH */ 26 | -------------------------------------------------------------------------------- /src/platform.hh: -------------------------------------------------------------------------------- 1 | /* This contains assorted platform constants, defined in one place. 2 | * 3 | * The primary preference is config.h, generated by the buildsystem. There 4 | * are also fallbacks in place using different approaches, in order to 5 | * decouple the code from build system config as much as possible; the 6 | * goal is to make it buildable without any pre-configuration 7 | */ 8 | 9 | #ifndef PLATFORM_HH 10 | #define PLATFORM_HH 11 | 12 | /* OS; defined to be luajit compatible 13 | * 14 | * If undetected, it will still work, but the OS will be "Other" 15 | */ 16 | 17 | #define FFI_OS_OTHER 0 18 | #define FFI_OS_WINDOWS 1 19 | #define FFI_OS_LINUX 2 20 | #define FFI_OS_OSX 3 21 | #define FFI_OS_BSD 4 22 | #define FFI_OS_POSIX 5 23 | 24 | #if defined(_WIN32) && !defined(_XBOX_VER) 25 | # define FFI_OS FFI_OS_WINDOWS 26 | # define FFI_OS_NAME "Windows" 27 | #elif defined(__linux__) 28 | # define FFI_OS FFI_OS_LINUX 29 | # define FFI_OS_NAME "Linux" 30 | #elif defined(__MACH__) && defined(__APPLE__) 31 | # define FFI_OS FFI_OS_OSX 32 | # define FFI_OS_NAME "OSX" 33 | #elif (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ 34 | defined(__NetBSD__) || defined(__OpenBSD__) || \ 35 | defined(__DragonFly__)) && !defined(__ORBIS__) 36 | # define FFI_OS FFI_OS_BSD 37 | # define FFI_OS_NAME "BSD" 38 | #elif (defined(__sun__) && defined(__svr4__)) || defined(__HAIKU__) || \ 39 | defined(__CYGWIN__) 40 | # define FFI_OS FFI_OS_POSIX 41 | # define FFI_OS_NAME "POSIX" 42 | # ifdef __CYGWIN__ 43 | # define FFI_OS_CYGWIN 1 44 | # endif 45 | #else 46 | # define FFI_OS FFI_OS_OTHER 47 | # define FFI_OS_NAME "Other" 48 | #endif 49 | 50 | #if !defined(FFI_BIG_ENDIAN) && !defined(FFI_LITTLE_ENDIAN) 51 | # error "Unknown machine endianness" 52 | #elif defined(FFI_BIG_ENDIAN) && defined(FFI_LITTLE_ENDIAN) 53 | # error "Choose just one endianness" 54 | #endif 55 | 56 | /* Arch; defined to be luajit compatible 57 | * 58 | * IF undetected, it will still work, but the arch will be "unknown" 59 | */ 60 | 61 | #define FFI_ARCH_UNKNOWN 0 62 | #define FFI_ARCH_X86 1 63 | #define FFI_ARCH_X64 2 64 | #define FFI_ARCH_ARM 3 65 | #define FFI_ARCH_ARM64 4 66 | #define FFI_ARCH_PPC 5 67 | #define FFI_ARCH_PPC64 6 68 | #define FFI_ARCH_MIPS32 7 69 | #define FFI_ARCH_MIPS64 8 70 | /* these architectures are not defined in luajit */ 71 | #define FFI_ARCH_ALPHA 9 72 | #define FFI_ARCH_HPPA 10 73 | #define FFI_ARCH_IA64 11 74 | #define FFI_ARCH_M68K 12 75 | #define FFI_ARCH_MBLAZE 13 76 | #define FFI_ARCH_OR1K 14 77 | #define FFI_ARCH_RV32 15 78 | #define FFI_ARCH_RV64 16 79 | #define FFI_ARCH_SH4 17 80 | #define FFI_ARCH_SPARC 18 81 | #define FFI_ARCH_SPARC64 19 82 | #define FFI_ARCH_S390 20 83 | 84 | #define FFI_CPU(arch) (FFI_ARCH == FFI_ARCH_##arch) 85 | 86 | #if defined(__i386) || defined(__i386__) || defined(_M_IX86) 87 | # define FFI_ARCH FFI_ARCH_X86 88 | # define FFI_ARCH_NAME "x86" 89 | #elif defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || \ 90 | defined(_M_AMD64) 91 | # define FFI_ARCH FFI_ARCH_X64 92 | # define FFI_ARCH_NAME "x64" 93 | #elif defined(__arm__) || defined(__arm) || defined(__ARM__) || defined(__ARM) 94 | # define FFI_ARCH FFI_ARCH_ARM 95 | # if defined(FFI_BIG_ENDIAN) 96 | # define FFI_ARCH_NAME "armeb" 97 | # else 98 | # define FFI_ARCH_NAME "arm" 99 | # endif 100 | # ifdef __SOFTFP__ 101 | # define FFI_ARCH_HAS_FPU 0 102 | # endif 103 | # if !__ARM_PCS_VFP 104 | # define FFI_ARCH_SOFTFP 1 105 | # endif 106 | #elif defined(__aarch64__) 107 | # define FFI_ARCH FFI_ARCH_ARM64 108 | # if defined(FFI_BIG_ENDIAN) 109 | # define FFI_ARCH_NAME "arm64be" 110 | # else 111 | # define FFI_ARCH_NAME "arm64" 112 | # endif 113 | #elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || \ 114 | defined(__powerpc64) || defined(__ppc64) || defined(__PPC64) || \ 115 | defined(__POWERPC64__) || defined(__POWERPC64) || defined(_M_PPC64) 116 | # define FFI_ARCH FFI_ARCH_PPC64 117 | # if defined(_CALL_ELF) && (_CALL_ELF == 2) 118 | # define FFI_ARCH_PPC64_ELFV2 1 119 | # endif 120 | # if defined(FFI_LITTLE_ENDIAN) 121 | # define FFI_ARCH_NAME "ppc64le" 122 | # else 123 | # define FFI_ARCH_NAME "ppc64" 124 | # endif 125 | #elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || \ 126 | defined(__powerpc) || defined(__ppc) || defined(__PPC) || \ 127 | defined(__POWERPC__) || defined(__POWERPC) || defined(_M_PPC) 128 | # define FFI_ARCH FFI_ARCH_PPC 129 | # if defined(FFI_LITTLE_ENDIAN) 130 | # define FFI_ARCH_NAME "ppcle" 131 | # else 132 | # define FFI_ARCH_NAME "ppc" 133 | # endif 134 | #elif defined(__mips64__) || defined(__mips64) || defined(__MIPS64__) || \ 135 | defined(__MIPS64) 136 | # define FFI_ARCH FFI_ARCH_MIPS64 137 | # if __mips_isa_rev >= 6 138 | # if defined(FFI_LITTLE_ENDIAN) 139 | # define FFI_ARCH_NAME "mips64r6el" 140 | # else 141 | # define FFI_ARCH_NAME "mips64r6" 142 | # endif 143 | # else 144 | # if defined(FFI_LITTLE_ENDIAN) 145 | # define FFI_ARCH_NAME "mips64el" 146 | # else 147 | # define FFI_ARCH_NAME "mips64" 148 | # endif 149 | # endif 150 | #elif defined(__mips__) || defined(__mips) || defined(__MIPS__) || \ 151 | defined(__MIPS) 152 | # define FFI_ARCH FFI_ARCH_MIPS32 153 | # if __mips_isa_rev >= 6 154 | # if defined(FFI_LITTLE_ENDIAN) 155 | # define FFI_ARCH_NAME "mips32r6el" 156 | # else 157 | # define FFI_ARCH_NAME "mips32r6" 158 | # endif 159 | # else 160 | # if defined(FFI_LITTLE_ENDIAN) 161 | # define FFI_ARCH_NAME "mipsel" 162 | # else 163 | # define FFI_ARCH_NAME "mips" 164 | # endif 165 | # endif 166 | # ifdef __mips_soft_float 167 | # define FFI_ARCH_SOFTFP 1 168 | # define FFI_ARCH_HAS_FPU 0 169 | # else 170 | # define FFI_ARCH_SOFTFP 0 171 | # define FFI_ARCH_HAS_FPU 1 172 | # endif 173 | #elif defined(__alpha__) || defined(__alpha) 174 | # define FFI_ARCH FFI_ARCH_ALPHA 175 | # define FFI_ARCH_NAME "alpha" 176 | #elif defined(__hppa__) || defined(__HPPA__) || defined(__hppa) 177 | # define FFI_ARCH FFI_ARCH_HPPA 178 | # define FFI_ARCH_NAME "hppa" 179 | #elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || \ 180 | defined(_M_IA64) || defined(__itanium__) 181 | # define FFI_ARCH FFI_ARCH_IA64 182 | # define FFI_ARCH_NAME "ia64" 183 | #elif defined(__m68k__) || defined(__MC68K__) 184 | # define FFI_ARCH FFI_ARCH_M68K 185 | # define FFI_ARCH_NAME "m68k" 186 | #elif defined(__MICROBLAZE__) 187 | # define FFI_ARCH FFI_ARCH_MBLAZE 188 | # if defined(__MICROBLAZEEL__) 189 | # define FFI_ARCH_NAME "microblazeel" 190 | # else 191 | # define FFI_ARCH_NAME "microblaze" 192 | # endif 193 | #elif defined(__OR1K__) 194 | # define FFI_ARCH FFI_ARCH_OR1K 195 | # define FFI_ARCH_NAME "or1k" 196 | #elif defined(__riscv) || defined(__riscv__) 197 | # if __riscv_xlen == 32 198 | # define FFI_ARCH FFI_ARCH_RV32 199 | # define FFI_ARCH_NAME "riscv32" 200 | # else 201 | # define FFI_ARCH FFI_ARCH_RV64 202 | # define FFI_ARCH_NAME "riscv64" 203 | # endif 204 | # ifdef __riscv_float_abi_soft 205 | # define FFI_ARCH_SOFTFP 1 206 | # define FFI_ARCH_HAS_FPU 0 207 | # else 208 | # define FFI_ARCH_SOFTFP 0 209 | # define FFI_ARCH_HAS_FPU 1 210 | # endif 211 | #elif defined(__sh__) && defined(__SH4__) 212 | # define FFI_ARCH FFI_ARCH_SH4 213 | # if defined(FFI_BIG_ENDIAN) 214 | # define FFI_ARCH_NAME "sh4eb" 215 | # else 216 | # define FFI_ARCH_NAME "sh4" 217 | # endif 218 | #elif defined(__sparc__) || defined(__sparc) 219 | # if defined(__sparc_v9__) || defined(__sparcv9) || defined(__arch64__) 220 | # define FFI_ARCH FFI_ARCH_SPARC64 221 | # define FFI_ARCH_NAME "sparc64" 222 | # else 223 | # define FFI_ARCH FFI_ARCH_SPARC 224 | # define FFI_ARCH_NAME "sparc" 225 | # endif 226 | #elif defined(__s390__) || defined(__s390x__) || defined(__zarch__) 227 | # define FFI_ARCH FFI_ARCH_S390 228 | # if defined(__s390x__) 229 | # define FFI_ARCH_NAME "s390x" 230 | # else 231 | # define FFI_ARCH_NAME "s390" 232 | # endif 233 | #else 234 | # define FFI_ARCH FFI_ARCH_UNKNOWN 235 | # define FFI_ARCH_NAME "unknown" 236 | #endif 237 | 238 | #if FFI_ARCH == FFI_ARCH_ARM && defined(__ARM_EABI__) 239 | # define FFI_ARM_EABI 1 240 | #endif 241 | 242 | #if FFI_ARCH == FFI_ARCH_PPC && defined(__NO_FPRS__) && !defined(_SOFT_FLOAT) 243 | # define FFI_PPC_SPE 1 244 | #endif 245 | 246 | #ifndef FFI_ARCH_HAS_FPU 247 | #if defined(_SOFT_FLOAT) || defined(_SOFT_DOUBLE) 248 | # define FFI_ARCH_HAS_FPU 0 249 | #else 250 | # define FFI_ARCH_HAS_FPU 1 251 | #endif 252 | #endif 253 | 254 | #ifndef FFI_ARCH_SOFTFP 255 | #if defined(_SOFT_FLOAT) || defined(_SOFT_DOUBLE) 256 | # define FFI_ARCH_SOFTFP 1 257 | #else 258 | # define FFI_ARCH_SOFTFP 0 259 | #endif 260 | #endif 261 | 262 | #if FFI_OS == FFI_OS_WINDOWS || defined(FFI_OS_CYGWIN) 263 | # define FFI_WINDOWS_ABI 1 264 | #endif 265 | 266 | #ifdef _UWP 267 | # define FFI_WINDOWS_UWP 1 268 | # error "UWP support not yet implemented" 269 | #endif 270 | 271 | #if FFI_OS != FFI_OS_WINDOWS 272 | # define FFI_USE_DLFCN 1 273 | #endif 274 | 275 | /* abi-specific features */ 276 | 277 | /* passing unions by value: 278 | * 279 | * - all 32-bit x86 except windows fastcall passes values on the stack 280 | * - windows fastcall may pass some in regs but always the same ones 281 | * - windows x64 ABI doesn't care about union contents for passing 282 | * - arm and aarch64 - composite types go in GPRs or on the stack 283 | * - ppc and ppc64 - composite types go in GPRs or on the stack 284 | * - mips - unions are like structs, structs go in GPRs or on the stack 285 | * 286 | * every other ABI is for now forbidden from passing unions by value since 287 | * it is not known whether it is safe to do so; usually this would need some 288 | * manual handling as the type of register used for passing may depend on the 289 | * type being passed (i.e. same-size same-alignment unions with different 290 | * fields may use different registers) 291 | * 292 | * aarch64 and ppc64le have a concept of homogenous aggregates, which means 293 | * unions may occasionally go in FPRs/VRRs; this is handled explicitly in 294 | * our implementation 295 | * 296 | * ppc64 and aarch64 currently disabled - buggy 297 | */ 298 | #if FFI_CPU(X86) || FFI_CPU(ARM) || FFI_CPU(PPC) || \ 299 | FFI_CPU(MIPS32) || FFI_CPU(MIPS64) 300 | # define FFI_ABI_UNIONVAL 1 301 | #elif defined(FFI_WINDOWS_ABI) && (FFI_ARCH == FFI_ARCH_X64) 302 | # define FFI_ABI_UNIONVAL 1 303 | #endif 304 | 305 | /* some compiler bits */ 306 | 307 | #if defined(__GNUC__) 308 | #if (__GNUC__ >= 5) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) 309 | #define FFI_DIAGNOSTIC_PRAGMA_GCC 1 310 | #endif 311 | #define WARN_UNUSED_RET __attribute__((warn_unused_result)) 312 | #define RESTRICT __restrict__ 313 | #else 314 | #define WARN_UNUSED_RET 315 | #define RESTRICT 316 | #endif 317 | 318 | #if defined(__clang__) 319 | #define FFI_DIAGNOSTIC_PRAGMA_CLANG 1 320 | #endif 321 | 322 | /* MSVC warnings we don't care about/are irrelevant to us */ 323 | 324 | #ifdef _MSC_VER 325 | /* conditional expression is constant */ 326 | #pragma warning(disable: 4127) 327 | /* unsafe CRT, used only once */ 328 | #pragma warning(disable: 4996) 329 | #endif 330 | 331 | /* MSVC and clang */ 332 | 333 | #define _CRT_SECURE_NO_WARNINGS 1 334 | 335 | #endif /* PLATFORM_HH */ 336 | -------------------------------------------------------------------------------- /src/util.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "util.hh" 5 | 6 | void *operator new(std::size_t n) { 7 | void *p = malloc(n); 8 | if (!p) { 9 | abort(); /* FIXME: do not abort */ 10 | } 11 | return p; 12 | } 13 | 14 | void *operator new[](std::size_t n) { 15 | void *p = malloc(n); 16 | if (!p) { 17 | abort(); 18 | } 19 | return p; 20 | } 21 | 22 | void operator delete(void *p) { 23 | free(p); 24 | } 25 | 26 | void operator delete[](void *p) { 27 | free(p); 28 | } 29 | 30 | void operator delete(void *p, std::size_t) { 31 | free(p); 32 | } 33 | 34 | void operator delete[](void *p, std::size_t) { 35 | free(p); 36 | } 37 | 38 | namespace util { 39 | 40 | std::size_t write_i(char *buf, std::size_t bufsize, long long v) { 41 | if (v < 0) { 42 | if (!bufsize) { 43 | return write_u( 44 | buf, bufsize, static_cast(-v) 45 | ) + 1; 46 | } 47 | *buf = '-'; 48 | return write_u( 49 | buf + 1, bufsize - 1, static_cast(-v) 50 | ) + 1; 51 | } 52 | return write_u(buf, bufsize, static_cast(v)); 53 | } 54 | 55 | std::size_t write_u(char *bufp, std::size_t bufsize, unsigned long long v) { 56 | char buf[sizeof(unsigned long long) * CHAR_BIT]; 57 | std::size_t ndig = 0; 58 | if (!v) { 59 | buf[0] = '0'; 60 | ndig = 1; 61 | } else { 62 | for (; v; v /= 10) { 63 | buf[ndig++] = char(v % 10) + '0'; 64 | } 65 | } 66 | if (bufsize < (ndig + 1)) { 67 | return ndig; 68 | } 69 | for (std::size_t i = 0; i < ndig; ++i) { 70 | *bufp++ = buf[ndig - i - 1]; 71 | } 72 | *bufp++ = '\0'; 73 | return ndig; 74 | } 75 | 76 | } /* namespace util */ 77 | -------------------------------------------------------------------------------- /subprojects/libffi.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory = libffi 3 | url = https://github.com/xclaesse/libffi.git 4 | revision = meson-port 5 | 6 | [provide] 7 | libffi = ffi_dep 8 | -------------------------------------------------------------------------------- /tests/abi.lua: -------------------------------------------------------------------------------- 1 | -- ffi.os, ffi.arch is not tested as that will vary by platform 2 | -- same with other ffi.abi parameters, etc 3 | 4 | local ffi = require("cffi") 5 | 6 | if ffi.abi("be") then 7 | assert(not ffi.abi("le")) 8 | else 9 | assert(ffi.abi("le")) 10 | assert(not ffi.abi("be")) 11 | end 12 | 13 | if ffi.abi("64bit") then 14 | assert(ffi.sizeof("void *") == 8) 15 | elseif ffi.abi("32bit") then 16 | assert(ffi.sizeof("void *") == 4) 17 | else 18 | skip_test() 19 | end 20 | 21 | assert(ffi.sizeof("char") == 1) 22 | assert(ffi.sizeof("short") == 2) 23 | 24 | ffi.cdef [[ 25 | union foo { 26 | struct { uint8_t a; uint8_t b; }; 27 | uint16_t v; 28 | }; 29 | ]] 30 | 31 | local x = ffi.new("union foo") 32 | x.a = 0xAA 33 | x.b = 0xFF 34 | 35 | if ffi.abi("be") then 36 | assert(x.v == 0xAAFF) 37 | else 38 | assert(x.v == 0xFFAA) 39 | end 40 | -------------------------------------------------------------------------------- /tests/callbacks.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | -- named args 4 | local called = false 5 | local cb = ffi.cast("void (*)(char const *arg1, int arg2)", function(arg1, arg2) 6 | assert(ffi.string(arg1) == "hello world") 7 | assert(ffi.tonumber(arg2) == 5) 8 | called = true 9 | end) 10 | 11 | cb("hello world", 5) 12 | assert(called) 13 | 14 | -- setting different callback 15 | local called2 = false 16 | cb:set(function() called2 = true end) 17 | 18 | cb("foo", 10) 19 | assert(called2) 20 | 21 | cb:free() 22 | 23 | -- unnamed args 24 | local called3 = false 25 | local cb2 = ffi.cast("void (*)(int, double)", function(a, b) 26 | assert(a == 5) 27 | assert(b == 3.14) 28 | called3 = true 29 | end) 30 | 31 | cb2(5, 3.14) 32 | assert(called3) 33 | 34 | cb2:free() 35 | 36 | -- funcs in structs 37 | 38 | ffi.cdef [[ 39 | struct test { 40 | void (*cb)(); 41 | }; 42 | ]] 43 | 44 | local st = ffi.new("struct test") 45 | 46 | local called, called2 = false, false 47 | 48 | -- this also works, but it will leak memory! 49 | --st.cb = function() called = true end 50 | --assert(not called) 51 | --st.cb() 52 | --assert(called) 53 | 54 | local cb3 = ffi.cast("void (*)()", function() called2 = true end) 55 | st.cb = cb3 56 | assert(not called2) 57 | st.cb() 58 | assert(called2) 59 | cb3:free() 60 | -------------------------------------------------------------------------------- /tests/callconv.lua: -------------------------------------------------------------------------------- 1 | -- calling convention specifiers are ignored except on 32-bit x86 windows 2 | 3 | local ffi = require("cffi") 4 | 5 | local L = require("testlib") 6 | 7 | ffi.cdef [[ 8 | int __stdcall test_stdcall(int, int); 9 | int test_fastcall1(int, int) __attribute__((fastcall)) __asm__("test_fastcall"); 10 | __attribute__((fastcall)) int test_fastcall2(int, int) __asm__("test_fastcall"); 11 | int __fastcall test_fastcall3(int, int) __asm__("test_fastcall"); 12 | int __attribute__((fastcall)) test_fastcall4(int, int) __asm__("test_fastcall"); 13 | ]] 14 | 15 | local r = L.test_stdcall(5, 10) 16 | assert(r == 15) 17 | 18 | local r = L.test_fastcall1(5, 10) 19 | assert(r == 15) 20 | 21 | local r = L.test_fastcall2(5, 10) 22 | assert(r == 15) 23 | 24 | local r = L.test_fastcall3(5, 10) 25 | assert(r == 15) 26 | 27 | local r = L.test_fastcall4(5, 10) 28 | assert(r == 15) 29 | 30 | local fp1 = ffi.cast("int (__fastcall *)(int, int)", L.test_fastcall1) 31 | local fp2 = ffi.cast("int (*)(int, int) __attribute__((fastcall))", L.test_fastcall1) 32 | 33 | assert(fp1(15, 20) == 35) 34 | assert(fp2(15, 20) == 35) 35 | -------------------------------------------------------------------------------- /tests/cast.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | -- strings are convertible to char pointers 4 | 5 | local foo = "hello world" 6 | local foop = ffi.cast("const char *", foo) 7 | 8 | assert(ffi.string(foop) == "hello world") 9 | 10 | -- pointer<->number conversions 11 | 12 | local up = ffi.cast("uintptr_t", foop) 13 | local op = ffi.cast("const char *", up) 14 | 15 | assert(ffi.string(op) == "hello world") 16 | assert(op == foop) 17 | 18 | -- passing pointers as arrays is ok 19 | 20 | local x = ffi.new("int[2]", {5, 10}) 21 | local xp = ffi.cast("int *", x) 22 | 23 | local tap = ffi.cast("void (*)(int p[2])", function(p) 24 | assert((p[0] == 5) and (p[1] == 10)) 25 | end) 26 | 27 | tap(x) 28 | tap(xp) 29 | 30 | tap:free() 31 | -------------------------------------------------------------------------------- /tests/cexpr.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | ffi.cdef [[ 4 | typedef enum { 5 | A = 1 << 0, 6 | B = 1 << 1, 7 | AB = A | B, 8 | C = 1 << 2, 9 | D = 1 << 3, 10 | E = 1 << 4, 11 | F = 1 << 5, 12 | G = 1 << 6, 13 | AC = A | C, 14 | ABC = AB | C, 15 | ALL = ABC | D | E | F | G 16 | } Test; 17 | ]] 18 | 19 | assert(ffi.C.A == 1) 20 | assert(ffi.C.B == 2) 21 | assert(ffi.C.AB == 3) 22 | assert(ffi.C.C == 4) 23 | assert(ffi.C.D == 8) 24 | assert(ffi.C.E == 16) 25 | assert(ffi.C.F == 32) 26 | assert(ffi.C.G == 64) 27 | assert(ffi.C.AC == 5) 28 | assert(ffi.C.ABC == 7) 29 | assert(ffi.C.ALL == 127) 30 | -------------------------------------------------------------------------------- /tests/copy_fill.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | local buf = ffi.new("char[256]") 4 | assert(ffi.sizeof(buf) == 256) 5 | 6 | ffi.copy(buf, "hello world") 7 | assert(ffi.string(buf) == "hello world") 8 | assert(ffi.string(buf + 6) == "world") 9 | 10 | ffi.fill(buf, ffi.sizeof(buf)) 11 | assert(ffi.string(buf) == "") 12 | 13 | ffi.fill(buf, 8, string.byte("A")) 14 | assert(ffi.string(buf) == "AAAAAAAA") 15 | 16 | ffi.fill(buf, 32) 17 | ffi.copy(buf, "hello world", 5) 18 | assert(ffi.string(buf) == "hello") 19 | 20 | -- https://github.com/q66/cffi-lua/issues/10 21 | -- make sure passing through lua strings works 22 | assert(ffi.string("hello world") == "hello world") 23 | assert(ffi.string("hello world", 4) == "hell") 24 | -------------------------------------------------------------------------------- /tests/dump_string.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | -- simple cases 4 | 5 | local s = "hello world" 6 | 7 | assert(ffi.string(s) == s) 8 | assert(ffi.string(ffi.cast("char const *", s)) == s) 9 | 10 | -- byte array dump + pointer to it 11 | 12 | local a = ffi.new("uint8_t [3]", {0x61, 0x62, 0x63}) 13 | local p = ffi.cast("const uint8_t *", a) 14 | 15 | assert(ffi.string(a, ffi.sizeof(a)) == "abc") 16 | assert(ffi.string(p, 3) == "abc") 17 | 18 | -- scalar values are not dumpable 19 | 20 | local a = ffi.new("uint32_t", 0x61626364) 21 | 22 | assert(not pcall(ffi.string, a, ffi.sizeof(a))) 23 | 24 | -- struct type dump by value 25 | 26 | ffi.cdef [[ 27 | struct foo { 28 | uint8_t a; 29 | uint8_t b; 30 | uint8_t c; 31 | }; 32 | ]] 33 | 34 | local s = ffi.new("struct foo", 0x61, 0x62, 0x63) 35 | local p =ffi.cast("struct foo const *", s) 36 | 37 | assert(ffi.string(s, ffi.sizeof(s)) == "abc") 38 | assert(ffi.string(p, ffi.sizeof(s)) == "abc") 39 | 40 | -- flexible arrays should be treated like strings 41 | 42 | ffi.cdef [[ 43 | struct bar { 44 | uint8_t x; 45 | char s[]; 46 | }; 47 | ]] 48 | 49 | local b = ffi.new("char [5]", 0x10, 0x61, 0x62, 0x63, 0x00) 50 | local p = ffi.cast("struct bar const *", b) 51 | 52 | assert(p.x == 16) 53 | assert(ffi.string(p.s, 2) == "ab") 54 | assert(ffi.string(p.s) == "abc") 55 | -------------------------------------------------------------------------------- /tests/globals.lua: -------------------------------------------------------------------------------- 1 | local ffi = require('cffi') 2 | 3 | ffi.cdef [[ 4 | extern char const test_string[]; 5 | extern int const test_ints[3]; 6 | ]] 7 | 8 | local L = require("testlib") 9 | 10 | assert(L.test_string[0] == string.byte('f')) 11 | assert(L.test_string[1] == string.byte('o')) 12 | assert(L.test_string[2] == string.byte('o')) 13 | 14 | assert(L.test_ints[0] == 42) 15 | assert(L.test_ints[1] == 43) 16 | assert(L.test_ints[2] == 44) 17 | 18 | -- must be references 19 | assert(tostring(ffi.typeof(L.test_string)) == "ctype") 20 | assert(tostring(ffi.typeof(L.test_ints)) == "ctype") 21 | -------------------------------------------------------------------------------- /tests/istype.lua: -------------------------------------------------------------------------------- 1 | local ffi = require('cffi') 2 | 3 | ffi.cdef [[ 4 | struct foo { 5 | double v; 6 | }; 7 | 8 | struct test { 9 | struct foo v; 10 | double arr[3]; 11 | }; 12 | ]] 13 | 14 | local x = ffi.new('struct test') 15 | 16 | -- struct fields 17 | assert(ffi.istype("double[3]", x.arr)) 18 | assert(ffi.istype("double (&)[3]", x.arr)) 19 | assert(not ffi.istype("int[3]", x.arr)) 20 | assert(ffi.istype("struct foo", x.v)) 21 | assert(ffi.istype("struct foo &", x.v)) 22 | assert(not ffi.istype("struct foo[]", x.v)) 23 | 24 | -- references must be ignored 25 | assert(ffi.istype("double [3]", ffi.typeof("double (&)[3]"))) 26 | assert(ffi.istype("double (&)[3]", ffi.typeof("double [3]"))) 27 | assert(ffi.istype("int", ffi.typeof("int &"))) 28 | assert(ffi.istype("int &", ffi.typeof("int"))) 29 | assert(ffi.istype("float &", ffi.typeof("float"))) 30 | 31 | -- non-equal types 32 | assert(not ffi.istype("int", ffi.typeof("float"))) 33 | assert(not ffi.istype("int &", ffi.typeof("float"))) 34 | assert(not ffi.istype("int &", ffi.typeof("float &"))) 35 | 36 | -- second argument must be a cval 37 | assert(not ffi.istype("int", "int")) 38 | assert(not ffi.istype("int", true)) 39 | 40 | -- alternative syntaxes 41 | assert(ffi.istype("long", ffi.typeof("long int"))) 42 | assert(ffi.istype("unsigned long", ffi.typeof("unsigned long int"))) 43 | assert(ffi.istype("long int", ffi.typeof("long"))) 44 | assert(ffi.istype("unsigned long int", ffi.typeof("unsigned long"))) 45 | assert(ffi.istype("short", ffi.typeof("short int"))) 46 | assert(ffi.istype("unsigned short", ffi.typeof("unsigned short int"))) 47 | assert(ffi.istype("short int", ffi.typeof("short"))) 48 | assert(ffi.istype("unsigned short int", ffi.typeof("unsigned short"))) 49 | 50 | -- types in typeof must be terminated 51 | local ret, msg = pcall(ffi.typeof, "long int bla") 52 | assert(not ret) 53 | assert(msg == "'' expected near ''") 54 | 55 | local ret, msg = pcall(ffi.typeof, "long int int") 56 | assert(not ret) 57 | assert(msg == "'' expected near 'int'") 58 | -------------------------------------------------------------------------------- /tests/meson.build: -------------------------------------------------------------------------------- 1 | # Test suite definitions 2 | 3 | testlib = shared_module('testlib', ['testlib.cc'], 4 | install: false, 5 | cpp_args: extra_cxxflags 6 | ) 7 | 8 | test_cases = [ 9 | # test_name test_file expected_fail minver 10 | ['simple example', 'simple', false, 501], 11 | ['abi example', 'abi', false, 501], 12 | ['variadic calls', 'variadic', false, 501], 13 | ['fundamental type passing', 'simple_pass', false, 501], 14 | ['structs, arrays, unions', 'struct_array', false, 501], 15 | ['struct passing', 'struct_pass', false, 501], 16 | ['structs, unions array fields', 'struct_union_array_fields', false, 501], 17 | ['unions by value', 'unionval', false, 501], 18 | ['global variables', 'globals', false, 501], 19 | ['memory-related utilities', 'copy_fill', false, 501], 20 | ['memory serialization', 'dump_string', false, 501], 21 | ['callbacks', 'callbacks', false, 501], 22 | ['table initializers', 'table_init', false, 501], 23 | ['parameterized types', 'parameterized', false, 501], 24 | ['scalar types', 'scalar', false, 501], 25 | ['symbol redirection', 'redir', false, 501], 26 | ['calling conventions', 'callconv', false, 501], 27 | ['constant expressions', 'cexpr', false, 501], 28 | ['redefinitions', 'redef', false, 501], 29 | ['casting rules', 'cast', false, 501], 30 | ['type checks', 'istype', false, 501], 31 | ['metatype', 'metatype', false, 501], 32 | ['metatype (5.4)', 'metatype54', false, 504], 33 | ] 34 | 35 | # We put the deps path in PATH because that's where our Lua dll file is 36 | # located and we need the Lua executable to be able to find this on Windows 37 | 38 | penv = environment() 39 | penv.append('PATH', deps_path) 40 | penv.append('CFFI_PATH', meson.project_build_root()) 41 | penv.append('TESTS_PATH', meson.current_source_dir()) 42 | penv.append('TESTLIB_PATH', testlib.full_path()) 43 | 44 | foreach tcase: test_cases 45 | if luaver_num < tcase[3] 46 | continue 47 | endif 48 | test(tcase[0], lua_exe, 49 | args: [ 50 | join_paths(meson.current_source_dir(), 'runner.lua'), 51 | join_paths(meson.current_source_dir(), tcase[1] + '.lua') 52 | ], 53 | should_fail: tcase[2], depends: cffi, 54 | env: penv 55 | ) 56 | endforeach 57 | -------------------------------------------------------------------------------- /tests/metatype.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | ffi.cdef [[ 4 | typedef struct foo { 5 | int x; 6 | int y; 7 | } foo; 8 | ]] 9 | 10 | local new_called = 0 11 | 12 | local foo = ffi.metatype("foo", { 13 | __new = function(self, x, y) 14 | new_called = new_called + 1 15 | return ffi.new("foo", x, y) 16 | end, 17 | 18 | __index = { 19 | named_ctor = function(x, y) 20 | return ffi.new("foo", x, y) 21 | end, 22 | 23 | sum = function(self) 24 | return self.x + self.y 25 | end 26 | }, 27 | 28 | __len = function(self) return self.x end, 29 | __unm = function(self) return self.y end, 30 | }) 31 | 32 | assert(new_called == 0) 33 | 34 | local x = foo(5, 10) 35 | assert(x.x == 5) 36 | assert(x.y == 10) 37 | assert(x:sum() == 15) 38 | assert(#x == 5) 39 | assert(-x == 10) 40 | 41 | assert(new_called == 1) 42 | 43 | local x = foo(5, 10) 44 | assert(new_called == 2) 45 | 46 | local x = foo.named_ctor(500, 1000) 47 | assert(x.x == 500) 48 | assert(x.y == 1000) 49 | assert(x:sum() == 1500) 50 | 51 | assert(new_called == 2) 52 | -------------------------------------------------------------------------------- /tests/metatype54.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | ffi.cdef [[ 4 | typedef struct foo { 5 | int x; 6 | } foo; 7 | ]] 8 | 9 | local closed_count = 0 10 | 11 | local foo = ffi.metatype("foo", { 12 | __close = function() 13 | closed_count = closed_count + 1 14 | end, 15 | __name = "foo" 16 | }) 17 | 18 | local x = foo(5) 19 | assert(closed_count == 0) 20 | assert(tostring(x):match("^foo: ")) 21 | 22 | do 23 | local x = foo(5) 24 | end 25 | assert(closed_count == 1) 26 | 27 | do 28 | local x = foo(5) 29 | local y = foo(5) 30 | do 31 | local z = foo(5) 32 | end 33 | assert(closed_count == 2) 34 | end 35 | 36 | assert(closed_count == 4) 37 | -------------------------------------------------------------------------------- /tests/parameterized.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | -- parameterized struct 4 | 5 | local t1_t = ffi.typeof("int") 6 | local t2_t = ffi.typeof("short") 7 | local t3_t = ffi.typeof("char const *") 8 | 9 | ffi.cdef([[ 10 | typedef struct $ { 11 | $ $; 12 | $ $, $; 13 | $ $; 14 | } $; 15 | ]], "pstruct", t1_t, "test1", t2_t, "x", "y", t3_t, "test2", "pstruct") 16 | 17 | local x = ffi.new("pstruct") 18 | x.test1 = 1337 19 | local t2 = "test string" 20 | x.test2 = t2 21 | x.x = 5 22 | x.y = 10 23 | 24 | assert(ffi.tonumber(x.test1) == 1337) 25 | assert(ffi.string(x.test2) == "test string") 26 | assert(ffi.tonumber(x.x) == 5) 27 | assert(ffi.tonumber(x.y) == 10) 28 | 29 | assert(ffi.sizeof("pstruct") == ffi.sizeof( 30 | "struct { int x; short a, b; void *y; }" 31 | )) 32 | 33 | -- parameterized enum 34 | 35 | ffi.cdef([[ 36 | enum { 37 | $ = $, $ 38 | }; 39 | ]], "FOO", 1337, "BAR") 40 | 41 | assert(ffi.C.FOO == 1337) 42 | assert(ffi.C.BAR == 1338) 43 | 44 | -- https://github.com/q66/cffi-lua/issues/6 45 | 46 | assert(tostring(ffi.typeof("$ *", ffi.typeof("int"))) == "ctype") 47 | -------------------------------------------------------------------------------- /tests/redef.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | -- it's okay to redefine funcs (and extern vars) 4 | -- the first definition always stays 5 | -- type matching is not checked, as luajit doesn't either 6 | ffi.cdef [[ 7 | void *malloc(size_t n); 8 | int malloc(float n); 9 | 10 | void free(void *p); 11 | void free(int p); 12 | ]] 13 | 14 | local p = ffi.gc(ffi.C.malloc(4), ffi.C.free) 15 | assert(ffi.istype("void *", p)) 16 | 17 | ffi.cdef [[ 18 | // try an empty typedef while at it, struct will get registered 19 | typedef struct foo { int x; }; 20 | ]] 21 | 22 | -- redefining structs etc. is invalid 23 | local ret, msg = pcall(ffi.cdef, [[ 24 | struct foo { int x; }; 25 | ]]) 26 | 27 | assert(not ret) 28 | assert(msg == "input:1: 'struct foo' redefined") 29 | 30 | -- typedef redefinitions are also okay, first one applies 31 | ffi.cdef [[ 32 | struct foo typedef foo; 33 | typedef struct bar foo; 34 | ]] 35 | 36 | assert(tostring(ffi.typeof("foo")) == "ctype") 37 | 38 | -- https://github.com/q66/cffi-lua/issues/11 39 | -- make sure non-conflicting names are picked for structs even 40 | -- when nested (as outer struct is still being parsed and thus 41 | -- is not present in the declaration store yet) 42 | 43 | ffi.cdef [[ 44 | typedef struct { 45 | struct { 46 | uint32_t a; 47 | } inner1; 48 | } foo_t; 49 | 50 | typedef struct { 51 | struct { 52 | uint32_t b; 53 | } inner2; 54 | } bar_t; 55 | ]] 56 | 57 | -- https://github.com/q66/cffi-lua/issues/12 58 | -- make sure requested names are not repeated across commits 59 | 60 | ffi.cdef [[ 61 | enum {FOO = 1}; 62 | enum {BAR = 2}; 63 | ]] 64 | 65 | ffi.cdef [[ 66 | enum {QUX = 3}; 67 | ]] 68 | -------------------------------------------------------------------------------- /tests/redir.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | local L = require("testlib") 3 | 4 | ffi.cdef [[ 5 | int foo(char *buf, size_t n, char const *fmt, ...) __asm__("test_snprintf"); 6 | ]] 7 | 8 | local buf = ffi.new("char[256]") 9 | local ret = L.foo(ffi.cast("char *", buf), 256, "%s", "test") 10 | assert(ret == 4) 11 | assert(ffi.string(buf) == "test") 12 | -------------------------------------------------------------------------------- /tests/runner.lua: -------------------------------------------------------------------------------- 1 | -- cffi-lua test runner 2 | -- this will set up the environment so that tests can be run 3 | 4 | assert(arg and (#arg >= 1), "no arguments given") 5 | 6 | -- set up package.path so that require() works 7 | 8 | local tests_path = os.getenv("TESTS_PATH") 9 | 10 | if not tests_path or (#tests_path == 0) then 11 | if arg[0] then 12 | tests_path = arg[0]:match("(.+)[\\/]") 13 | end 14 | end 15 | 16 | assert(tests_path and (#tests_path > 0), "couldn't find tests directory") 17 | 18 | local dirsep = "/" 19 | local psep = ";" 20 | local pmatch = "?" 21 | 22 | if package.config then 23 | dirsep, psep, pmatch = package.config:match("([^\n]+)\n([^\n]+)\n([^\n]+)") 24 | elseif package.path:match("\\") then 25 | -- heuristics for 5.1 and windows 26 | disep = "\\" 27 | end 28 | 29 | local tpn = tests_path:gsub("\\/", dirsep) 30 | if tpn:match(".$") ~= dirsep then 31 | tpn = tpn .. dirsep 32 | end 33 | package.path = tpn .. "?.lua" 34 | 35 | -- set up package.cpath 36 | 37 | local tl_path = os.getenv("CFFI_PATH") 38 | 39 | if tl_path and (#tl_path > 0) then 40 | tl_path = tl_path:gsub("\\/", dirsep) 41 | if tl_path:gmatch(".$") ~= dirsep then 42 | tl_path = tl_path .. dirsep 43 | end 44 | if package.cpath:match("%.dll") then 45 | package.cpath = tl_path .. "?.dll" 46 | else 47 | package.cpath = tl_path .. "?.so" 48 | end 49 | end 50 | 51 | -- set up luajit compatibility 52 | 53 | if jit then 54 | package.preload["cffi"] = package.preload["ffi"] 55 | end 56 | 57 | -- test lib 58 | 59 | skip_test = function() 60 | os.exit(77) 61 | end 62 | 63 | -- run the testcase 64 | 65 | dofile(arg[1]) 66 | -------------------------------------------------------------------------------- /tests/scalar.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | -- eval 4 | 5 | local na = ffi.eval("0xFF") 6 | local nb = ffi.eval("50ULL") 7 | local nc = ffi.eval("50U") 8 | local nd = ffi.eval("150LL") 9 | 10 | assert(ffi.tonumber(na) == 255) 11 | assert(ffi.tonumber(nb) == 50) 12 | assert(ffi.tonumber(nc) == 50) 13 | assert(ffi.tonumber(nd) == 150) 14 | 15 | assert(ffi.typeof(na) == ffi.typeof("int")) 16 | assert(ffi.typeof(nb) == ffi.typeof("unsigned long long")) 17 | assert(ffi.typeof(nc) == ffi.typeof("unsigned int")) 18 | assert(ffi.typeof(nd) == ffi.typeof("long long")) 19 | 20 | -- basic arithmetic 21 | 22 | local a = ffi.new("int", 150) 23 | local b = ffi.new("short", 300) 24 | local c = -ffi.cast("int", 150) 25 | 26 | assert(ffi.tonumber(a + b + c) == 300) 27 | 28 | -- pointer addition 29 | 30 | local a = ffi.cast("int *", 0) 31 | local b = a + 3 32 | local c = 3 + a 33 | 34 | assert(ffi.tonumber(ffi.cast("size_t", b)) == 3 * ffi.sizeof("int")) 35 | assert(ffi.tonumber(ffi.cast("size_t", c)) == 3 * ffi.sizeof("int")) 36 | 37 | -- pointer difference 38 | 39 | local a = ffi.cast("int *", 12) 40 | local b = ffi.cast("int *", 4) 41 | 42 | assert((a - b) == ((12 - 4) / ffi.sizeof("int"))) 43 | 44 | -- arrays can be treated the same 45 | 46 | local a = ffi.new("int[4]") 47 | local b = a + 3 48 | 49 | assert((b - a) == 3) 50 | 51 | -- pow 52 | 53 | assert(ffi.tonumber(ffi.cast("int", 2) ^ ffi.cast("int", 4)) == 16) 54 | 55 | -- nullptr 56 | 57 | assert(ffi.cast("int *", 0) == ffi.nullptr) 58 | assert(ffi.cast("int *", 5) ~= ffi.nullptr) 59 | 60 | -- equality 61 | 62 | assert(ffi.cast("int", 150) == ffi.cast("int", 150)) 63 | assert(ffi.cast("int", 200) ~= ffi.cast("int", 150)) 64 | -- different from luajit 65 | assert(ffi.cast("int", 150) ~= 150) 66 | 67 | -- typdefs 68 | 69 | ffi.cdef [[ 70 | typedef int type1_t; 71 | typedef unsigned long type2_t; 72 | typedef signed type3_t; 73 | typedef time_t type4_t; 74 | /* redeclarations should be ignored */ 75 | typedef long time_t; 76 | ]] 77 | 78 | assert(ffi.cast("int", 150) == ffi.cast("type1_t", 150)) 79 | assert(ffi.cast("unsigned long", 150) == ffi.cast("type2_t", 150)) 80 | assert(ffi.cast("signed", 150) == ffi.cast("type3_t", 150)) 81 | assert(ffi.cast("time_t", 150) == ffi.cast("type4_t", 150)) 82 | -------------------------------------------------------------------------------- /tests/simple.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | local L = require("testlib") 3 | 4 | ffi.cdef [[ 5 | size_t test_strlen(char const *str); 6 | size_t test_strlen_void(void const *str) __asm__("test_strlen"); 7 | ]] 8 | 9 | local ret = ffi.tonumber(L.test_strlen("hello world")) 10 | assert(ret == 11) 11 | 12 | local ret = ffi.tonumber(L.test_strlen_void("hello world")) 13 | assert(ret == 11) 14 | -------------------------------------------------------------------------------- /tests/simple_pass.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | local L = require("testlib") 3 | 4 | -- pattern to fill the memory with; long enough to cover every case 5 | local pattern = string.char(0x3C, 0xF, 0xF0, 0xC3):rep(4) 6 | 7 | local val_test = function(tname, fname) 8 | ffi.cdef(("struct { %s v; } test_%s(%s v);"):format(tname, fname, tname)) 9 | local v = ffi.new(tname) 10 | local sz = ffi.sizeof(tname) 11 | local a = ffi.addressof(v) 12 | ffi.copy(ffi.cast("void *", a), pattern, sz) 13 | -- pass and check if it's still the same 14 | local r = L["test_" .. fname](v) 15 | assert(ffi.string(a, sz) == ffi.string(r, sz)) 16 | end 17 | 18 | val_test("unsigned char", "uchar") 19 | val_test("unsigned short", "ushort") 20 | val_test("unsigned int", "uint") 21 | val_test("unsigned long", "ulong") 22 | val_test("unsigned long long", "ullong") 23 | -- this case breaks on m68k with optimized builds; the result will be empty 24 | -- it is not reproducible when returning the float type directly, and it is 25 | -- also not reproducible with integer types 26 | val_test("float", "float") 27 | val_test("double", "double") 28 | 29 | -- libffi seemingly has a bug where long double in a struct is not passed 30 | -- correctly on x86_64 on Linux (and Windows gcc), should not be ours so 31 | -- don't trigger it 32 | -- 33 | -- it's broken on m68k too 34 | if ffi.arch ~= "x64" and ffi.arch ~= "m68k" then 35 | val_test("long double", "ldouble") 36 | end 37 | 38 | ffi.cdef [[ 39 | float test_raw_float(float v); 40 | char test_raw_char(char c); 41 | int test_raw_int(int v); 42 | ]] 43 | 44 | assert(L.test_raw_float(15) == 15) 45 | assert(L.test_raw_char(15) == 15) 46 | assert(L.test_raw_int(15) == 15) 47 | -------------------------------------------------------------------------------- /tests/struct_array.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | ffi.cdef [[ 4 | struct foo { 5 | int x; 6 | size_t pad1, pad2; 7 | struct { 8 | char y; 9 | size_t pad3, pad4; 10 | short z; 11 | }; 12 | size_t pad5; 13 | size_t pad6; 14 | char const *w; 15 | }; 16 | 17 | struct flex { 18 | int x; 19 | double y[]; 20 | }; 21 | 22 | union bar { 23 | struct { 24 | unsigned char x, y; 25 | }; 26 | unsigned short z; 27 | }; 28 | 29 | struct baz { 30 | int x, y; 31 | }; 32 | 33 | struct sbaz { 34 | struct baz x, y, z; 35 | }; 36 | 37 | struct schara { 38 | char foo[4]; 39 | }; 40 | 41 | struct multi { 42 | int x[3][4][5]; 43 | }; 44 | 45 | struct arr { 46 | char buf[16]; 47 | char buf2[]; 48 | }; 49 | ]] 50 | 51 | -- struct 52 | 53 | local x = ffi.new("struct foo") 54 | 55 | local s = "hello" 56 | x.x = 150 57 | x.y = 30 58 | x.z = 25 59 | x.w = s 60 | 61 | assert(x.x == 150) 62 | assert(x.y == 30) 63 | assert(x.z == 25) 64 | assert(ffi.string(x.w) == "hello") 65 | 66 | local ox = ffi.offsetof("struct foo", "x") 67 | local oy = ffi.offsetof("struct foo", "y") 68 | local oz = ffi.offsetof("struct foo", "z") 69 | local ow = ffi.offsetof("struct foo", "w") 70 | 71 | assert(ox == 0) 72 | assert(oy > ox) 73 | assert(oz > oy) 74 | assert(ow > oz) 75 | 76 | -- simple array 77 | 78 | local x = ffi.new("int[3]") 79 | assert(ffi.sizeof(x) == ffi.sizeof("int") * 3) 80 | 81 | assert(x[0] == 0) 82 | assert(x[1] == 0) 83 | assert(x[2] == 0) 84 | 85 | x[0] = 5 86 | x[1] = 10 87 | x[2] = 15 88 | 89 | assert(x[0] == 5) 90 | assert(x[1] == 10) 91 | assert(x[2] == 15) 92 | 93 | -- array with initializer 94 | 95 | local x = ffi.new("int[3]", 5) 96 | assert(ffi.sizeof(x) == ffi.sizeof("int") * 3) 97 | 98 | assert(x[0] == 5) 99 | assert(x[1] == 5) 100 | assert(x[2] == 5) 101 | 102 | -- flexible array members 103 | 104 | local x = ffi.new("struct flex", 3); 105 | x.x = 5 106 | x.y[0] = 10 107 | x.y[1] = 15 108 | x.y[2] = 20 109 | 110 | assert(x.x == 5) 111 | assert(x.y[0] == 10) 112 | assert(x.y[1] == 15) 113 | assert(x.y[2] == 20) 114 | 115 | -- union 116 | 117 | local x = ffi.new("union bar") 118 | x.x = 5 119 | x.y = 10 120 | 121 | assert(x.x == 5) 122 | assert(x.y == 10) 123 | if ffi.abi("le") then 124 | assert(x.z == 0xA05) 125 | else 126 | assert(x.z == 0x50A) 127 | end 128 | 129 | -- array of structs 130 | 131 | local x = ffi.new("struct baz[4]"); 132 | assert(ffi.sizeof(x) == ffi.sizeof("int") * 8) 133 | 134 | local st = ffi.typeof("struct baz") 135 | x[0] = st(10, 15) 136 | 137 | -- test with constant source 138 | local y = ffi.cast("struct baz const *", x) 139 | x[2] = y[0] 140 | 141 | -- table initialization of member 142 | x[1] = { 35, 45 } 143 | 144 | assert(x[0].x == 10) 145 | assert(x[0].y == 15) 146 | assert(x[1].x == 35) 147 | assert(x[1].y == 45) 148 | assert(x[2].x == 10) 149 | assert(x[2].y == 15) 150 | assert(x[3].x == 0) 151 | assert(x[3].y == 0) 152 | 153 | -- test assignment to constant source 154 | local b, err = pcall(function() 155 | y[0] = st(100, 200) 156 | end) 157 | assert(not b) 158 | assert(err:match(".+: attempt to write to constant location")) 159 | 160 | -- struct of structs assignment 161 | 162 | local x = ffi.new("struct sbaz") 163 | x.x = st(20, 25) 164 | x.z = x.x 165 | x.y = { 35, 45 } 166 | 167 | assert(x.x.x == 20) 168 | assert(x.x.y == 25) 169 | assert(x.y.x == 35) 170 | assert(x.y.y == 45) 171 | assert(x.z.x == 20) 172 | assert(x.z.y == 25) 173 | 174 | -- array in string 175 | 176 | local x = ffi.new("struct schara") 177 | x.foo = "abc" 178 | assert(ffi.string(x.foo) == "abc") 179 | 180 | x.foo = "abcd1234" 181 | assert(ffi.string(x.foo) == "abcd") 182 | 183 | -- multidimensional array in struct 184 | 185 | local x = ffi.new("struct multi") 186 | assert(ffi.sizeof(x) == ffi.sizeof("struct multi")) 187 | assert(ffi.sizeof(x) == ffi.sizeof("int") * 3 * 4 * 5) 188 | assert(ffi.sizeof(x) == ffi.sizeof("int [3][4][5]")) 189 | 190 | -- pointer arithmetic with member arrays 191 | 192 | local x = ffi.new("struct arr", 16) 193 | ffi.copy(x.buf, "hello world") 194 | ffi.copy(x.buf2, "flex flex") 195 | assert(ffi.string(x.buf) == "hello world") 196 | assert(ffi.string(x.buf2) == "flex flex") 197 | assert(ffi.string(x.buf + 6) == "world") 198 | assert(ffi.string(x.buf2 + 5) == "flex") 199 | assert(x.buf[4] == (x.buf + 4)[0]) 200 | assert(x.buf2[4] == (x.buf2 + 4)[0]) 201 | assert(x.buf[4] == string.byte("o")) 202 | -------------------------------------------------------------------------------- /tests/struct_pass.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | ffi.cdef [[ 4 | typedef struct pass { 5 | int a, b; 6 | } pass; 7 | 8 | pass test_struct_val(pass a, pass b); 9 | ]] 10 | 11 | local L = require("testlib") 12 | 13 | local a = ffi.new("pass", 5, 10) 14 | local b = ffi.new("pass", 50, 100) 15 | 16 | local c = L.test_struct_val(a, b) 17 | 18 | assert(c.a == 55) 19 | assert(c.b == 110) 20 | -------------------------------------------------------------------------------- /tests/struct_union_array_fields.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | local field_size = function(tp, t2) 4 | local sz = ffi.sizeof(tp) 5 | if not t2 then 6 | return sz 7 | end 8 | local al = ffi.alignof(t2) 9 | return math.floor((sz + al - 1) / al) * al 10 | end 11 | 12 | local expected_sz 13 | local exepcted_al 14 | 15 | ffi.cdef [[ 16 | struct foo { 17 | uint8_t x; 18 | uint32_t y; 19 | }; 20 | 21 | struct bar { 22 | uint8_t x[3]; 23 | }; 24 | 25 | struct baz { 26 | uint32_t x; 27 | uint8_t y[4]; 28 | }; 29 | 30 | struct baz_flex { 31 | uint32_t x; 32 | uint8_t y[2]; 33 | char z[]; 34 | }; 35 | 36 | /* flex members may add padding but do not add their own size */ 37 | struct pad_flex { 38 | uint8_t x; 39 | uint64_t y[]; 40 | }; 41 | 42 | struct qux { 43 | uint32_t x; 44 | /* on most platforms, pad 4 */ 45 | uint64_t y[2]; 46 | uint32_t z[2]; 47 | }; 48 | 49 | struct quux { 50 | uint32_t x; 51 | struct iquux { 52 | uint8_t y[4]; 53 | } y[2]; 54 | }; 55 | 56 | struct quuux { 57 | uint32_t x; 58 | struct bar y; 59 | struct quux z; 60 | }; 61 | 62 | union ufoo_u { 63 | uint32_t x; 64 | uint64_t y[2]; 65 | uint32_t z[2]; 66 | }; 67 | 68 | struct ufoo { 69 | union { 70 | uint32_t x; 71 | uint64_t y[2]; 72 | uint32_t z[2]; 73 | }; 74 | }; 75 | 76 | union ubar_u { 77 | uint8_t x[5]; 78 | int16_t y; 79 | }; 80 | 81 | struct ubar { 82 | union { 83 | uint8_t x[5]; 84 | int16_t y; 85 | }; 86 | }; 87 | ]] 88 | 89 | -- struct foo 90 | 91 | expected_sz = ( 92 | field_size("uint8_t", "uint32_t") + 93 | field_size("uint32_t") 94 | ) 95 | assert(ffi.sizeof("struct foo") == expected_sz) 96 | 97 | -- struct bar 98 | 99 | expected_sz = field_size("uint8_t") * 3 100 | assert(ffi.sizeof("struct bar") == expected_sz) 101 | 102 | -- struct baz 103 | 104 | expected_sz = ( 105 | field_size("uint32_t", "uint8_t") + 106 | field_size("uint8_t") * 4 107 | ) 108 | assert(ffi.sizeof("struct baz") == expected_sz) 109 | 110 | -- struct baz_flex 111 | 112 | expected_sz = ( 113 | field_size("uint32_t", "uint8_t") + 114 | field_size("uint8_t") * 2 115 | ) 116 | -- pad to multiple of alignment of uint32_t 117 | local uint32_al = ffi.alignof("uint32_t") 118 | expected_sz = math.floor((expected_sz + uint32_al - 1) / uint32_al) * uint32_al 119 | assert(ffi.sizeof("struct baz_flex") == expected_sz) 120 | 121 | -- struct pad_flex 122 | 123 | expected_sz = ( 124 | field_size("uint8_t", "uint64_t") 125 | ) 126 | assert(ffi.sizeof("struct pad_flex") == expected_sz) 127 | 128 | -- struct qux 129 | 130 | expected_sz = ( 131 | field_size("uint32_t", "uint64_t") + 132 | field_size("uint64_t") * 2 + 133 | field_size("uint32_t[2]", "uint64_t") 134 | ) 135 | assert(ffi.sizeof("struct qux") == expected_sz) 136 | 137 | -- struct quux 138 | 139 | expected_sz = ( 140 | field_size("uint32_t", "struct { uint8_t x[4]; }") + 141 | field_size("struct { uint8_t x[4]; }") * 2 142 | ) 143 | assert(ffi.sizeof("struct quux") == expected_sz) 144 | 145 | -- struct quuux 146 | 147 | expected_sz = ( 148 | field_size("uint32_t", "struct bar") + 149 | field_size("struct bar", "struct quux") + 150 | field_size("struct quux") 151 | ) 152 | assert(ffi.sizeof("struct quuux") == expected_sz) 153 | 154 | -- union ufoo_u, struct ufoo 155 | 156 | expected_sz = math.max( 157 | field_size("uint32_t"), 158 | field_size("uint64_t[2]"), 159 | field_size("uint32_t[2]") 160 | ) 161 | expected_al = math.max( 162 | ffi.alignof("uint32_t"), 163 | ffi.alignof("uint64_t") 164 | ) 165 | assert(ffi.sizeof("union ufoo_u") == expected_sz) 166 | assert(ffi.alignof("union ufoo_u") == expected_al) 167 | assert(ffi.sizeof("struct ufoo") == expected_sz) 168 | assert(ffi.alignof("struct ufoo") == expected_al) 169 | assert((expected_sz % expected_al) == 0) 170 | 171 | -- union ubar_u, struct ubar 172 | 173 | local int16_al = ffi.alignof("int16_t") 174 | 175 | expected_sz = math.max( 176 | math.floor((field_size("uint8_t[5]") + int16_al - 1) / int16_al) * int16_al, 177 | field_size("int16_t") 178 | ) 179 | expected_al = math.max( 180 | ffi.alignof("uint8_t"), 181 | int16_al 182 | ) 183 | print(ffi.sizeof("union ubar_u"), expected_sz) 184 | assert(ffi.sizeof("union ubar_u") == expected_sz) 185 | assert(ffi.alignof("union ubar_u") == expected_al) 186 | assert(ffi.sizeof("struct ubar") == expected_sz) 187 | assert(ffi.alignof("struct ubar") == expected_al) 188 | assert((expected_sz % expected_al) == 0) 189 | 190 | -------------------------------------------------------------------------------- /tests/table_init.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | ffi.cdef [[ 4 | struct flex { 5 | int x; 6 | double y[]; 7 | }; 8 | 9 | struct vlaflex { 10 | int x; 11 | double y[?]; 12 | }; 13 | 14 | struct buf { 15 | uint32_t x; 16 | uint32_t y; 17 | uint8_t buf[16]; 18 | }; 19 | 20 | struct sinit { 21 | int x; 22 | float y; 23 | double z; 24 | }; 25 | 26 | struct sstr { 27 | int x; 28 | }; 29 | 30 | struct dsinit { 31 | struct sstr a; 32 | struct sstr b; 33 | }; 34 | 35 | union uinit { 36 | char const *s; 37 | size_t a; 38 | }; 39 | 40 | union uinit2 { 41 | int a; 42 | double b; 43 | }; 44 | ]] 45 | 46 | local x 47 | 48 | x = ffi.new("int[3]", 5) 49 | assert(x[0] == 5) 50 | assert(x[1] == 5) 51 | assert(x[2] == 5) 52 | assert(ffi.sizeof(x) == 3 * ffi.sizeof("int")) 53 | 54 | x = ffi.new("int[?]", 3, 5) 55 | assert(x[0] == 5) 56 | assert(x[1] == 0) 57 | assert(x[2] == 0) 58 | assert(ffi.sizeof(x) == 3 * ffi.sizeof("int")) 59 | 60 | x = ffi.new("int[3]", { 5, 10, 15 }) 61 | assert(x[0] == 5) 62 | assert(x[1] == 10) 63 | assert(x[2] == 15) 64 | assert(ffi.sizeof(x) == 3 * ffi.sizeof("int")) 65 | 66 | x = ffi.new("int[?]", 3, { 5, 10, 15 }) 67 | assert(x[0] == 5) 68 | assert(x[1] == 10) 69 | assert(x[2] == 15) 70 | assert(ffi.sizeof(x) == 3 * ffi.sizeof("int")) 71 | 72 | x = ffi.new("int[3]", 5, 10, 15) 73 | assert(x[0] == 5) 74 | assert(x[1] == 10) 75 | assert(x[2] == 15) 76 | assert(ffi.sizeof(x) == 3 * ffi.sizeof("int")) 77 | 78 | x = ffi.new("int[?]", 3, 5, 10, 15) 79 | assert(x[0] == 5) 80 | assert(x[1] == 10) 81 | assert(x[2] == 15) 82 | assert(ffi.sizeof(x) == 3 * ffi.sizeof("int")) 83 | 84 | x = ffi.new("struct sinit", { 5, 3.14, 6.28 }) 85 | assert(x.x == 5) 86 | assert(x.y == ffi.tonumber(ffi.new("float", 3.14))) 87 | assert(x.z == 6.28) 88 | 89 | x = ffi.new("struct sinit", 5, 3.14, 6.28) 90 | assert(x.x == 5) 91 | assert(x.y == ffi.tonumber(ffi.new("float", 3.14))) 92 | assert(x.z == 6.28) 93 | 94 | x = ffi.new("struct sinit", { x = 5, y = 3.14, z = 6.28 }) 95 | assert(x.x == 5) 96 | assert(x.y == ffi.tonumber(ffi.new("float", 3.14))) 97 | assert(x.z == 6.28) 98 | 99 | x = ffi.new("struct dsinit", {5}, {10}) 100 | assert(x.a.x == 5) 101 | assert(x.b.x == 10) 102 | 103 | x = ffi.new("struct dsinit", {{5}, {10}}) 104 | assert(x.a.x == 5) 105 | assert(x.b.x == 10) 106 | 107 | x = ffi.new("struct flex", 2, { 5, 10, 15 }) 108 | assert(x.x == 5) 109 | assert(x.y[0] == 10) 110 | assert(x.y[1] == 15) 111 | 112 | x = ffi.new("struct flex", 2, { x = 5, y = { 10, 15 } }) 113 | assert(x.x == 5) 114 | assert(x.y[0] == 10) 115 | assert(x.y[1] == 15) 116 | 117 | x = ffi.new("struct vlaflex", 2, { 5, 10, 15 }) 118 | assert(x.x == 5) 119 | assert(x.y[0] == 10) 120 | assert(x.y[1] == 15) 121 | 122 | x = ffi.new("struct vlaflex", 2, { x = 5, y = { 10, 15 } }) 123 | assert(x.x == 5) 124 | assert(x.y[0] == 10) 125 | assert(x.y[1] == 15) 126 | 127 | x = ffi.new("struct buf", { x = 5, y = 10 }); 128 | assert(x.x == 5) 129 | assert(x.y == 10) 130 | ffi.copy(x.buf, "hello world") 131 | assert(x.buf[0] == string.byte("h")) 132 | assert(x.buf[1] == string.byte("e")) 133 | assert(ffi.string(x.buf) == "hello world") 134 | 135 | local str = "hello world" 136 | x = ffi.new("union uinit", { str }) 137 | assert(ffi.string(x.s) == "hello world") 138 | assert(x.s == ffi.cast("void *", x.a)) 139 | assert(x.s ~= x.a) 140 | 141 | x = ffi.new("union uinit", { s = str }) 142 | assert(ffi.string(x.s) == "hello world") 143 | assert(x.s == ffi.cast("void *", x.a)) 144 | assert(x.s ~= x.a) 145 | 146 | -- test if union initialization by name works 147 | x = ffi.new("union uinit2", { a = 5 }) 148 | assert(x.a == 5) 149 | 150 | -- test union init of first field 151 | x = ffi.new("union uinit2", 5) 152 | assert(x.a == 5) 153 | 154 | -- should be properly truncated 155 | x = ffi.new("union uinit2", { a = 3.14 }) 156 | assert(x.a == 3) 157 | 158 | -- test initialization of second field 159 | x = ffi.new("union uinit2", { b = 3.14 }) 160 | assert(x.b == 3.14) 161 | -------------------------------------------------------------------------------- /tests/testlib.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define TEST_STDCALL 7 | #define TEST_FASTCALL 8 | 9 | #if defined(_WIN32) || defined(__CYGWIN__) 10 | # if defined(__i386) || defined(__i386__) || defined(_M_IX86) 11 | # undef TEST_STDCALL 12 | # undef TEST_FASTCALL 13 | # define TEST_STDCALL __stdcall 14 | # define TEST_FASTCALL __fastcall 15 | # endif 16 | # define DLL_EXPORT __declspec(dllexport) 17 | #else 18 | # if defined(__GNUC__) && (__GNUC__ >= 4) 19 | # define DLL_EXPORT __attribute__((visibility("default"))) 20 | # else 21 | # define DLL_EXPORT 22 | # endif 23 | #endif 24 | 25 | extern "C" DLL_EXPORT 26 | char const test_string[] = "foobar"; 27 | 28 | extern "C" DLL_EXPORT 29 | int const test_ints[3] = {42, 43, 44}; 30 | 31 | extern "C" DLL_EXPORT 32 | size_t test_strlen(char const *str) { 33 | return strlen(str); 34 | } 35 | 36 | extern "C" DLL_EXPORT 37 | int test_snprintf(char *buf, size_t n, char const *fmt, ...) { 38 | va_list args; 39 | va_start(args, fmt); 40 | int ret = vsnprintf(buf, n, fmt, args); 41 | va_end(args); 42 | return ret; 43 | } 44 | 45 | extern "C" DLL_EXPORT 46 | int TEST_STDCALL test_stdcall(int a, int b) { 47 | return a + b; 48 | } 49 | 50 | extern "C" DLL_EXPORT 51 | int TEST_FASTCALL test_fastcall(int a, int b) { 52 | return a + b; 53 | } 54 | 55 | struct test_struct { 56 | int a, b; 57 | }; 58 | 59 | extern "C" DLL_EXPORT 60 | test_struct test_struct_val(test_struct a, test_struct b) { 61 | return test_struct{a.a + b.a, a.b + b.b}; 62 | } 63 | 64 | /* fundamental type passing tests */ 65 | 66 | /* need to return a struct to read it in lua as bytes */ 67 | #define TYPE_TEST(tname, fname) \ 68 | struct test_struct_##fname { tname x; }; \ 69 | extern "C" DLL_EXPORT test_struct_##fname test_##fname(tname v) { \ 70 | test_struct_##fname r; \ 71 | std::memcpy(&r, &v, sizeof(r)); \ 72 | return r; \ 73 | } 74 | 75 | TYPE_TEST(unsigned char, uchar) 76 | TYPE_TEST(unsigned short, ushort) 77 | TYPE_TEST(unsigned int, uint) 78 | TYPE_TEST(unsigned long, ulong) 79 | TYPE_TEST(unsigned long long, ullong) 80 | TYPE_TEST(float, float) 81 | TYPE_TEST(double, double) 82 | TYPE_TEST(long double, ldouble) 83 | 84 | /* some individual tests without using a record */ 85 | 86 | extern "C" DLL_EXPORT float test_raw_float(float v) { return v; } 87 | extern "C" DLL_EXPORT char test_raw_char(char c) { return c; } 88 | extern "C" DLL_EXPORT int test_raw_int(int v) { return v; } 89 | 90 | /* union value passing tests */ 91 | 92 | #define UNION_TEST(uname, ubody) \ 93 | union uname ubody; \ 94 | extern "C" DLL_EXPORT uname test_##uname(uname v) { \ 95 | return v; \ 96 | } 97 | 98 | UNION_TEST(u1, { signed char a; }) 99 | UNION_TEST(u2, { unsigned short a; }) 100 | UNION_TEST(u3, { int a; }) 101 | UNION_TEST(u4, { long long a; }) 102 | UNION_TEST(u5, { float a; }) 103 | UNION_TEST(u6, { double a; }) 104 | UNION_TEST(u7, { long double a; }) 105 | 106 | UNION_TEST(us1, { struct { signed char a; } x; }) 107 | UNION_TEST(us2, { struct { unsigned short a; } x; }) 108 | UNION_TEST(us3, { struct { int a; } x; }) 109 | UNION_TEST(us4, { struct { long long a; } x; }) 110 | UNION_TEST(us5, { struct { float a; } x; }) 111 | UNION_TEST(us6, { struct { double a; } x; }) 112 | UNION_TEST(us7, { struct { long double a; } x; }) 113 | 114 | UNION_TEST(ud1, { signed char a; int b; }) 115 | UNION_TEST(ud2, { unsigned short a; long b; }) 116 | UNION_TEST(ud3, { int a; signed char b; }) 117 | UNION_TEST(ud4, { long long a; float b; }) 118 | UNION_TEST(ud5, { float a; long long b; }) 119 | UNION_TEST(ud6, { double a; short b; }) 120 | UNION_TEST(ud7, { long double a; long b; }) 121 | 122 | UNION_TEST(ut1, { signed char a; struct { int x; float y; long z; } b; }) 123 | UNION_TEST(ut2, { unsigned short a; struct { char x; double y; long double z; } b; }) 124 | UNION_TEST(ut3, { int a; struct { int x; int y; int z; } b; }) 125 | UNION_TEST(ut4, { long long a; struct { short x; long y; float z; } b; }) 126 | UNION_TEST(ut5, { float a; struct { float x; float y; float z; float w; } b; }) 127 | UNION_TEST(ut6, { double a; struct { double x; int y; long long z; } b; }) 128 | UNION_TEST(ut7, { long double a; struct { long double x; long double y; long double z; } b; }) 129 | 130 | UNION_TEST(uh1, { struct { double x; double y; } a; struct { double x; double y; double z; } b; }) 131 | UNION_TEST(uh2, { double a; struct { double x; double y; } b; }) 132 | UNION_TEST(uh3, { struct { double x; struct { double y; double z; } w; } a; struct { double x; double y; double z; } b; }) 133 | UNION_TEST(uh4, { struct { double x; struct { double y; double z; } w; } a; struct { double x; } b; }) 134 | 135 | UNION_TEST(ui1, { struct { int x; int y; } a; struct { int x; int y; int z; } b; }) 136 | UNION_TEST(ui2, { int a; struct { int x; int y; } b; }) 137 | UNION_TEST(ui3, { struct { int x; struct { int y; int z; } w; } a; struct { int x; int y; int z; } b; }) 138 | UNION_TEST(ui4, { struct { int x; struct { int y; int z; } w; } a; struct { int x; } b; }) 139 | -------------------------------------------------------------------------------- /tests/testlib.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | local tlp = os.getenv("TESTLIB_PATH") 4 | if not tlp or (#tlp == 0) then 5 | skip_test() 6 | end 7 | 8 | return ffi.load(tlp) 9 | -------------------------------------------------------------------------------- /tests/unionval.lua: -------------------------------------------------------------------------------- 1 | -- this tests exists to expose ABI issues with passing unions by value 2 | -- for each test union, it is allocated locally on Lua side, filled with 3 | -- a byte pattern, then passed by value to C; C simply returns it (again 4 | -- by value) and then we do a byte comparison on both 5 | 6 | local ffi = require("cffi") 7 | 8 | if not ffi.abi("unionval") then 9 | skip_test() 10 | end 11 | 12 | local L = require("testlib") 13 | 14 | -- pattern to fill the memory with; long enough to cover every case 15 | local pattern = string.char(0x3C, 0xF, 0xF0, 0xC3):rep(32) 16 | 17 | local union_test = function(uname, ubody) 18 | ffi.cdef(("typedef union %s { %s } %s; %s test_%s(%s v);"):format( 19 | uname, ubody, uname, 20 | uname, uname, uname 21 | )) 22 | local v = ffi.new(uname) 23 | local sz = ffi.sizeof(uname) 24 | ffi.copy(ffi.cast("void *", v), pattern, sz) 25 | -- pass and check if it's still the same 26 | local r = L["test_" .. uname](v) 27 | assert(ffi.string(v, sz) == ffi.string(r, sz)) 28 | end 29 | 30 | math.randomseed(0) 31 | 32 | union_test("u1", [[ signed char a; ]]) 33 | union_test("u2", [[ unsigned short a; ]]) 34 | union_test("u3", [[ int a; ]]) 35 | union_test("u4", [[ long long a; ]]) 36 | union_test("u5", [[ float a; ]]) 37 | union_test("u6", [[ double a; ]]) 38 | --union_test("u7", [[ long double a; ]]) 39 | 40 | union_test("us1", [[ struct { signed char a; } x; ]]) 41 | union_test("us2", [[ struct { unsigned short a; } x; ]]) 42 | union_test("us3", [[ struct { int a; } x; ]]) 43 | union_test("us4", [[ struct { long long a; } x; ]]) 44 | union_test("us5", [[ struct { float a; } x; ]]) 45 | union_test("us6", [[ struct { double a; } x; ]]) 46 | --union_test("us7", [[ struct { long double a; } x; ]]) 47 | 48 | union_test("ud1", [[ signed char a; int b; ]]) 49 | union_test("ud2", [[ unsigned short a; long b; ]]) 50 | union_test("ud3", [[ int a; signed char b; ]]) 51 | union_test("ud4", [[ long long a; float b; ]]) 52 | union_test("ud5", [[ float a; long long b; ]]) 53 | union_test("ud6", [[ double a; short b; ]]) 54 | --union_test("ud7", [[ long double a; long b; ]]) 55 | 56 | union_test("ut1", [[ signed char a; struct { int x; float y; long z; } b; ]]) 57 | union_test("ut2", [[ unsigned short a; struct { char x; double y; float z; } b; ]]) 58 | union_test("ut3", [[ int a; struct { int x; int y; int z; } b; ]]) 59 | union_test("ut4", [[ long long a; struct { short x; long y; float z; } b; ]]) 60 | union_test("ut5", [[ float a; struct { float x; float y; float z; float w; } b; ]]) 61 | union_test("ut6", [[ double a; struct { double x; int y; long long z; } b; ]]) 62 | --union_test("ut7", [[ long double a; struct { long double x; long double y; long double z; } b; ]]) 63 | 64 | union_test("uh1", [[ struct { double x; double y; } a; struct { double x; double y; double z; } b; ]]) 65 | union_test("uh2", [[ double a; struct { double x; double y; } b; ]]) 66 | union_test("uh3", [[ struct { double x; struct { double y; double z; } w; } a; struct { double x; double y; double z; } b; ]]) 67 | union_test("uh4", [[ struct { double x; struct { double y; double z; } w; } a; struct { double x; } b; ]]) 68 | 69 | union_test("ui1", [[ struct { int x; int y; } a; struct { int x; int y; int z; } b; ]]) 70 | union_test("ui2", [[ int a; struct { int x; int y; } b; ]]) 71 | union_test("ui3", [[ struct { int x; struct { int y; int z; } w; } a; struct { int x; int y; int z; } b; ]]) 72 | union_test("ui4", [[ struct { int x; struct { int y; int z; } w; } a; struct { int x; } b; ]]) 73 | -------------------------------------------------------------------------------- /tests/variadic.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("cffi") 2 | 3 | ffi.cdef [[ 4 | int test_snprintf(char *buf, size_t n, char const *fmt, ...); 5 | ]] 6 | 7 | local L = require("testlib") 8 | 9 | local buf = ffi.new("char[256]") 10 | local bufs = ffi.sizeof(buf) 11 | assert(bufs == 256) 12 | 13 | local ret = L.test_snprintf(ffi.cast("char *", buf), bufs, "%s", "test") 14 | assert(ret == 4) 15 | assert(ffi.string(buf) == "test") 16 | local ret = L.test_snprintf(buf, bufs, "%d", ffi.new("int", 123456)) 17 | assert(ret == 6) 18 | assert(ffi.string(buf) == "123456") 19 | 20 | local ret = L.test_snprintf(buf, bufs, "%s %g", "hello", 3.14) 21 | assert(ret == 10) 22 | assert(ffi.string(buf) == "hello 3.14") 23 | --------------------------------------------------------------------------------