├── bindings ├── python │ ├── LICENSE.txt │ ├── pydffi │ │ ├── __init__.py │ │ └── purectypes_generator │ │ │ ├── __init__.py │ │ │ └── generator.py │ ├── tests │ │ ├── test_varargs.py │ │ ├── test_new_types.py │ │ ├── test_ptrs.py │ │ ├── test_wrapper.py │ │ ├── test_symbol.py │ │ ├── CMakeLists.txt │ │ ├── test_errors.py │ │ ├── test_cdef_types.py │ │ ├── test_includes.py │ │ ├── test_unions.py │ │ ├── test_cxx.py │ │ ├── test_structs_portable_format.py │ │ ├── test_anon_struct.py │ │ ├── test_lasterror.py │ │ ├── common.py │ │ ├── test_basicobjs.py │ │ ├── test_bool.py │ │ ├── test_ignored.py │ │ ├── test_str.py │ │ ├── test_funcs_prop.py │ │ ├── test_array_ptr.py │ │ ├── test_new_structs.py │ │ ├── test_enum_.py │ │ ├── test_views.py │ │ ├── test_constness.py │ │ ├── test_structs_format.py │ │ ├── test_multiple_cu.py │ │ ├── test_mdarray.py │ │ ├── test_func_ptr.py │ │ ├── test_buffers.py │ │ ├── test_array.py │ │ ├── test_cast.py │ │ ├── test_structs.py │ │ ├── test_mem_refs.py │ │ └── test_purectypes_gen.py │ ├── CMakeLists.txt │ ├── errors.h │ └── dispatcher.h └── CMakeLists.txt ├── tests ├── lib.c ├── run_lit.py ├── dockers │ └── multiarch │ │ ├── build.sh │ │ └── Dockerfile ├── includes │ └── add.h ├── lit.site.cfg.in ├── dlopen.cpp ├── array.cpp ├── stdint.cpp ├── compile.cpp ├── multiple_defs.cpp ├── inline.cpp ├── enum.cpp ├── compile_cxx.cpp ├── compile_error.cpp ├── anon_union.cpp ├── decl_cxx.cpp ├── attrs.cpp ├── includes.cpp ├── asm_redirect.cpp ├── bool.cpp ├── anon_members.cpp ├── lasterror.cpp ├── CMakeLists.txt ├── typedef.cpp ├── system_headers.cpp ├── struct.cpp ├── anon_struct.cpp ├── union.cpp ├── lit.cfg ├── varargs.cpp ├── func_ptr.cpp ├── cconv.cpp └── decl.cpp ├── logo.jpg ├── tools ├── merge_libs_osx.sh └── merge_libs_gnu.sh ├── third-party ├── pybind11 │ ├── common.h │ ├── eigen.h │ ├── detail │ │ ├── typeid.h │ │ └── descr.h │ ├── complex.h │ ├── options.h │ ├── stl │ │ └── filesystem.h │ ├── eval.h │ └── functional.h ├── cc-clang.patch └── cc-llvm.patch ├── ci ├── install_cmake_manylinux.sh ├── build_cmake.sh ├── build_llvm_manylinux.sh └── build_wheels_manylinux.sh ├── TODO ├── include └── dffi │ ├── config.h.in │ ├── mdarray.h │ ├── ctypes.h │ ├── iterator_extras.h │ ├── exports.h │ ├── compat.h │ ├── cc.h │ ├── native_func.h │ └── iterator_range.h ├── examples ├── readdir.py ├── CMakeLists.txt ├── lit.site.cfg.in ├── archive.py ├── fftw.py └── lit.cfg ├── lib ├── dffi_api_win.inc ├── dffi_api_linux.inc ├── dffi_api_osx.inc ├── dffi_impl_clang_res.cpp ├── dffi_llvm_wrapper.cpp ├── types_printer.h ├── dffictx.cpp ├── cconv.cpp └── anon_member_inliner.cpp ├── .github └── workflows │ ├── linux.yml │ ├── osx.yml.dis │ └── win.yml.dis ├── CMakeClangRes.txt └── CHANGELOG /bindings/python/LICENSE.txt: -------------------------------------------------------------------------------- 1 | ../../LICENSE.txt -------------------------------------------------------------------------------- /tests/lib.c: -------------------------------------------------------------------------------- 1 | int foo(int a, int b) { 2 | return a+b; 3 | } 4 | -------------------------------------------------------------------------------- /bindings/python/pydffi/__init__.py: -------------------------------------------------------------------------------- 1 | from pydffi.backend import * 2 | -------------------------------------------------------------------------------- /logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aguinet/dragonffi/HEAD/logo.jpg -------------------------------------------------------------------------------- /bindings/python/pydffi/purectypes_generator/__init__.py: -------------------------------------------------------------------------------- 1 | from .generator import GenPureCType 2 | -------------------------------------------------------------------------------- /tests/run_lit.py: -------------------------------------------------------------------------------- 1 | from lit.main import main 2 | import sys 3 | 4 | if __name__ == '__main__': 5 | sys.exit(main()) 6 | -------------------------------------------------------------------------------- /tools/merge_libs_osx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | 4 | OUT=$1 5 | echo "Generating $OUT..." 6 | shift 7 | /usr/bin/libtool -static -o "$OUT" $* 8 | -------------------------------------------------------------------------------- /tests/dockers/multiarch/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | 4 | set -ex 5 | DIR=$(dirname $(realpath $0)) 6 | 7 | sudo docker build -t dffi_multiarch "$DIR" 8 | -------------------------------------------------------------------------------- /third-party/pybind11/common.h: -------------------------------------------------------------------------------- 1 | #include "detail/common.h" 2 | #warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." 3 | -------------------------------------------------------------------------------- /bindings/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | option(PYTHON_BINDINGS "Compile python bindings with cmake" ON) 2 | if (PYTHON_BINDINGS) 3 | add_subdirectory(python) 4 | endif() 5 | -------------------------------------------------------------------------------- /tools/merge_libs_gnu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | 4 | OUT=$1 5 | echo "Generating $OUT..." 6 | shift 7 | ( 8 | echo create $OUT 9 | for l in $*; do 10 | echo addlib $l 11 | done 12 | echo save 13 | echo end 14 | ) | ar -M 15 | -------------------------------------------------------------------------------- /ci/install_cmake_manylinux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | 4 | set -ex 5 | 6 | function cmake_install { 7 | yum install -y cmake 8 | } 9 | 10 | if [ ! -f "$CMAKE_BIN" ]; then 11 | cmake_install 12 | export CMAKE_BIN=/usr/bin/cmake 13 | fi 14 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | features: 2 | * bitfields 3 | * int128_t support in pybind11 4 | * atomic types 5 | 6 | fixes: 7 | * TODOs! 8 | * tests structures with custom alignments/pack 9 | 10 | CI: 11 | * appveyor: integrate it 12 | 13 | ideas: 14 | * shellcode generation (JITPacker) 15 | -------------------------------------------------------------------------------- /include/dffi/config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef DFFI_CONFIG_H 2 | #define DFFI_CONFIG_H 3 | 4 | #cmakedefine HAVE_INT128_T 5 | #cmakedefine HAVE_COMPLEX_T 6 | 7 | #cmakedefine LLVM_BUILD_DEBUG 8 | 9 | #ifdef HAVE_INT128_T 10 | #define DFFI_SUPPORT_I128 11 | #endif 12 | 13 | #ifdef HAVE_COMPLEX_T 14 | #define DFFI_SUPPORT_COMPLEX 15 | #endif 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /third-party/pybind11/eigen.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "eigen/matrix.h" 13 | -------------------------------------------------------------------------------- /ci/build_cmake.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | 4 | set -ex 5 | 6 | CMAKE_VERSION="3.18.3" 7 | CMAKE_FILE="cmake-${CMAKE_VERSION}.tar.gz" 8 | cd /tmp 9 | curl -sSL https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_FILE} -O 10 | echo "2c89f4e30af4914fd6fb5d00f863629812ada848eee4e2d29ec7e456d7fa32e5 ${CMAKE_FILE}" >cmake.sha 11 | sha256sum -c cmake.sha 12 | tar -xf "${CMAKE_FILE}" 13 | cd cmake* 14 | INST_DIR="/pwd/cmake-${CMAKE_VERSION}" 15 | ./bootstrap --prefix=$INST_DIR --parallel=$(nproc) -- -DCMAKE_USE_OPENSSL=OFF 16 | make install -j$(nproc) 17 | -------------------------------------------------------------------------------- /examples/readdir.py: -------------------------------------------------------------------------------- 1 | # REQUIRES: linux 2 | # RUN: %python "%s" "%S" |%FileCheck "%s" 3 | 4 | # CHECK: readdir.py 5 | 6 | import pydffi 7 | import sys 8 | 9 | D = pydffi.FFI() 10 | CU = D.cdef("#include ") 11 | 12 | dir_ = CU.funcs.opendir(sys.argv[1]) 13 | if not dir_: 14 | print("error reading directory") 15 | sys.exit(1) 16 | readdir = CU.funcs.readdir 17 | while True: 18 | dirent = readdir(dir_) 19 | if not dirent: 20 | break 21 | name = dirent.obj.d_name 22 | name = pydffi.cast(pydffi.ptr(name), D.CharPtrTy) 23 | print(name.cstr.tobytes()) 24 | CU.funcs.closedir(dir_) 25 | -------------------------------------------------------------------------------- /tests/dockers/multiarch/Dockerfile: -------------------------------------------------------------------------------- 1 | # manylinux1 docker 2 | FROM debian:stretch 3 | 4 | RUN apt-get update && apt-get install -y wget gnupg && \ 5 | echo "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-6.0 main" >/etc/apt/sources.list.d/llvm.list && \ 6 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ 7 | dpkg --add-architecture arm64 && apt-get update && \ 8 | apt-get update && apt-get install -y xz-utils patch build-essential cmake clang-6.0 libpython-dev libpython3-dev g++ libstdc++-6-dev:arm64 ninja-build binutils-aarch64-linux-gnu qemu-user python-pip && \ 9 | pip install lit 10 | -------------------------------------------------------------------------------- /lib/dffi_api_win.inc: -------------------------------------------------------------------------------- 1 | void* dffi::DynamicLibrary::baseAddress() const 2 | { 3 | assert(valid()); 4 | // Value returned by LoadLibrary is already the base address! 5 | return Data_; 6 | } 7 | 8 | // LastError handling 9 | #include 10 | 11 | // TODO: verify the initial value 12 | static thread_local DWORD dffi_lasterror = 0; 13 | 14 | void dffi::NativeFunc::swapLastError() 15 | { 16 | const DWORD tmp = dffi_lasterror; 17 | dffi_lasterror = GetLastError(); 18 | SetLastError(tmp); 19 | } 20 | 21 | dffi::NativeFunc::LastErrorTy dffi::NativeFunc::getLastError() { 22 | return dffi_lasterror; 23 | } 24 | 25 | void dffi::NativeFunc::setLastError(LastErrorTy Err) { 26 | dffi_lasterror = Err; 27 | } 28 | -------------------------------------------------------------------------------- /tests/includes/add.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | int add(int a, int b) 16 | { 17 | return a+b; 18 | } 19 | -------------------------------------------------------------------------------- /include/dffi/mdarray.h: -------------------------------------------------------------------------------- 1 | #ifndef DFFI_MDARRAY_H 2 | #define DFFI_MDARRAY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace dffi { 10 | 11 | struct DFFI_API MDArray 12 | { 13 | using ShapeTy = std::vector; 14 | 15 | MDArray(ShapeTy Shape, QualType BTy); 16 | MDArray(); 17 | 18 | MDArray(MDArray const&); 19 | MDArray(MDArray&&); 20 | 21 | static MDArray fromArrayTy(ArrayType const& ATy); 22 | 23 | inline bool isValid() const { return Shape_.size() > 0 && BTy_; } 24 | inline QualType getElementType() const { return BTy_; } 25 | inline ShapeTy const& getShape() const { return Shape_; } 26 | 27 | private: 28 | ShapeTy Shape_; 29 | QualType BTy_; 30 | }; 31 | 32 | } // dffi 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /lib/dffi_api_linux.inc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void* dffi::DynamicLibrary::baseAddress() const 6 | { 7 | assert(valid()); 8 | link_map* lm; 9 | if (dlinfo(Data_, RTLD_DI_LINKMAP, &lm) != 0) { 10 | return nullptr; 11 | } 12 | return (void*)lm->l_addr; 13 | } 14 | 15 | // LastError handling 16 | #include 17 | 18 | // The initial value of errno at program startup is zero. 19 | // (https://www.gnu.org/software/libc/manual/html_node/Checking-for-Errors.html). 20 | static thread_local int dffi_errno = 0; 21 | 22 | void dffi::NativeFunc::swapLastError() 23 | { 24 | std::swap(dffi_errno, errno); 25 | } 26 | 27 | dffi::NativeFunc::LastErrorTy dffi::NativeFunc::getLastError() { 28 | return dffi_errno; 29 | } 30 | 31 | void dffi::NativeFunc::setLastError(LastErrorTy Err) { 32 | dffi_errno = Err; 33 | } 34 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (BUILD_TESTS) 2 | set(EXAMPLES_DIR "${PROJECT_SOURCE_DIR}/examples") 3 | if (PYTHON_BINDINGS) 4 | set(EXAMPLES 5 | "${EXAMPLES_DIR}/archive.py" 6 | "${EXAMPLES_DIR}/fftw.py" 7 | "${EXAMPLES_DIR}/readdir.py" 8 | ) 9 | 10 | set(PYDFFI_LIB_DIR $) 11 | configure_file("lit.site.cfg.in" "${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg" ESCAPE_QUOTES @ONLY) 12 | 13 | # NOTE: added --max-time to timeout lit after specified seconds. 14 | # Default timeout is one week 15 | add_custom_target(check_python_examples 16 | COMMAND ${CMAKE_COMMAND} -E env PYDFFI_DIR=${PYDFFI_LIB_DIR} "${PYTHON_EXECUTABLE}" "${LIT_RUNNER}" "${CMAKE_CURRENT_BINARY_DIR}" -v --max-time=900 17 | DEPENDS ${EXAMPLES} 18 | ) 19 | 20 | add_dependencies(check check_python_examples) 21 | endif() 22 | 23 | endif() 24 | -------------------------------------------------------------------------------- /ci/build_llvm_manylinux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | 4 | SCRIPT_DIR=$(dirname "$(readlink -f "$0")") 5 | . "$SCRIPT_DIR/install_cmake_manylinux.sh" 6 | 7 | cd $1 8 | 9 | if [ $(uname -m) == "aarch64" ]; then 10 | TARGET="AArch64" 11 | else 12 | TARGET="X86" 13 | fi 14 | 15 | mkdir build 16 | mkdir install 17 | cd build 18 | 19 | $CMAKE_BIN ../llvm/ \ 20 | -DLLVM_BUILD_EXAMPLES=OFF \ 21 | -DCLANG_ENABLE_ARCMT=OFF \ 22 | -DCLANG_ENABLE_STATIC_ANALYZER=OFF \ 23 | -DLLVM_BUILD_TOOLS=ON \ 24 | -DCLANG_BUILD_TOOLS=OFF \ 25 | -DLLVM_INSTALL_UTILS=ON \ 26 | -DLLVM_TARGETS_TO_BUILD=$TARGET \ 27 | -DLLVM_ENABLE_PROJECTS=clang \ 28 | -DLLVM_ENABLE_BINDINGS=OFF \ 29 | -DCMAKE_BUILD_TYPE=Release \ 30 | -DCMAKE_INSTALL_PREFIX=$PWD/../install \ 31 | -DLLVM_ENABLE_THREADS=OFF \ 32 | -DLLVM_ENABLE_ZLIB=OFF \ 33 | -DLLVM_ENABLE_TERMINFO=OFF \ 34 | -DPython3_EXECUTABLE=/opt/python/cp38-cp38/bin/python \ 35 | $CMAKE_OPTS 36 | 37 | make install -j$(nproc) 38 | -------------------------------------------------------------------------------- /examples/lit.site.cfg.in: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import platform 16 | import os 17 | 18 | config.llvm_bindir = "@LLVM_BINDIR@" 19 | config.dffi_lib_dir = "@CMAKE_BINARY_DIR@" 20 | config.exe_suffix = "@CMAKE_EXECUTABLE_SUFFIX@" 21 | config.python_bin = "@PYTHON_EXECUTABLE@" 22 | 23 | lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") 24 | -------------------------------------------------------------------------------- /tests/lit.site.cfg.in: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import platform 16 | import os 17 | 18 | config.build_dir = "@CMAKE_CURRENT_BINARY_DIR@" 19 | config.llvm_bindir = "@LLVM_BINDIR@" 20 | config.exe_suffix = "@CMAKE_EXECUTABLE_SUFFIX@" 21 | config.dffi_lib_dir = "@CMAKE_BINARY_DIR@" 22 | 23 | lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") 24 | -------------------------------------------------------------------------------- /ci/build_wheels_manylinux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Usage: script.sh /path/to/dragonffi /path/to/llvm python_versions 3 | 4 | set -ex 5 | 6 | SCRIPT_DIR=$(dirname "$(readlink -f "$0")") 7 | . "$SCRIPT_DIR/install_cmake_manylinux.sh" 8 | 9 | # Use own cmake instead of the system one 10 | export PATH=$(dirname "$CMAKE_BIN"):$PATH 11 | 12 | DFFI=$1 13 | shift 14 | LLVM=$1 15 | shift 16 | 17 | LLVM=$(readlink -f "$LLVM") 18 | export LLVM_CONFIG="$LLVM/bin/llvm-config" 19 | export MAKEFLAGS="-j$(nproc)" 20 | 21 | cd "$DFFI/bindings/python" 22 | for PY in $*; do 23 | /opt/python/$PY/bin/pip install setuptools 24 | /opt/python/$PY/bin/python ./setup.py bdist_wheel 25 | rm -rf build 26 | done 27 | for f in dist/*.whl; do 28 | auditwheel repair $f 29 | done 30 | 31 | # Tests 32 | cd "$DFFI" 33 | for PY in $*; do 34 | /opt/python/$PY/bin/pip install bindings/python/wheelhouse/*-$PY-*.whl six purectypes==0.2 35 | /opt/python/$PY/bin/python -m unittest discover bindings/python/tests 36 | done 37 | -------------------------------------------------------------------------------- /bindings/python/tests/test_varargs.py: -------------------------------------------------------------------------------- 1 | # RUN: "%python" "%s" | "%FileCheck" "%s" 2 | # 3 | 4 | import pydffi 5 | from common import getFFI 6 | 7 | F = getFFI() 8 | CU = F.cdef(''' 9 | #include 10 | #include 11 | #include 12 | 13 | void print(const char* prefix, ...) 14 | { 15 | va_list args; 16 | va_start(args, prefix); 17 | while (1) { 18 | int16_t v = va_arg(args, int16_t); 19 | if (v == 0) break; 20 | printf("%s: %d\\n", prefix, v); 21 | } 22 | va_end(args); 23 | } 24 | ''') 25 | 26 | print_ = getattr(CU.funcs, "print") 27 | # CHECK: pref: 1 28 | print_("pref", F.Int16Ty(1), F.Int16Ty(0)) 29 | # CHECK: pref: -1 30 | print_("pref", F.Int16Ty(-1), F.Int16Ty(0)) 31 | # CHECK: pref: 1 32 | # CHECK: pref: -1 33 | print_("pref", F.Int16Ty(1), F.Int16Ty(0)) 34 | print_("pref", F.Int16Ty(-1), F.Int16Ty(0)) 35 | # CHECK: pref: 1 36 | # CHECK: pref: -1 37 | print_("pref", F.Int16Ty(1), F.Int16Ty(0)) 38 | print_("pref", F.Int16Ty(-1), F.Int16Ty(0)) 39 | -------------------------------------------------------------------------------- /bindings/python/tests/test_new_types.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | 18 | from common import DFFITest 19 | 20 | class NewTypesTest(DFFITest): 21 | def test_newtypes(self): 22 | # Basic objs 23 | self.assertEqual(self.FFI.Int32Ty(4).value, 4) 24 | self.assertEqual(self.FFI.Int32Ty(self.FFI.Int32Ty(4)).value, 4) 25 | 26 | if __name__ == '__main__': 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /bindings/python/tests/test_ptrs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | import sys 18 | import struct 19 | 20 | from common import DFFITest 21 | 22 | class PtrsTest(DFFITest): 23 | def test_ptrs(self): 24 | F = self.FFI 25 | O = F.UInt32Ty(4) 26 | P = pydffi.ptr(O) 27 | 28 | NewP = F.UInt32PtrTy(int(P.value)) 29 | self.assertEqual(NewP.obj, O) 30 | 31 | if __name__ == '__main__': 32 | unittest.main() 33 | -------------------------------------------------------------------------------- /examples/archive.py: -------------------------------------------------------------------------------- 1 | # REQUIRES: linux && libarchive 2 | # RUN: tar cf %t.tar %s 3 | # RUN: %python "%s" "%t.tar" | %FileCheck "%s" 4 | 5 | # CHECK: archive.py 6 | 7 | import pydffi 8 | import ctypes.util 9 | import sys 10 | 11 | pydffi.dlopen(ctypes.util.find_library("archive")) 12 | D = pydffi.FFI() 13 | CU=D.cdef(''' 14 | #include 15 | #include 16 | ''') 17 | 18 | funcs = CU.funcs 19 | archive_read_next_header = funcs.archive_read_next_header 20 | archive_entry_pathname_utf8 = funcs.archive_entry_pathname_utf8 21 | archive_read_data_skip = funcs.archive_read_data_skip 22 | 23 | a = funcs.archive_read_new() 24 | funcs.archive_read_support_filter_all(a) 25 | funcs.archive_read_support_format_all(a) 26 | r = funcs.archive_read_open_filename(a, sys.argv[1], 10240) 27 | if r != 0: 28 | raise RuntimeError("unable to open archive") 29 | entry = pydffi.ptr(CU.types.archive_entry)() 30 | while archive_read_next_header(a, pydffi.ptr(entry)) == 0: 31 | pathname = archive_entry_pathname_utf8(entry) 32 | print(pathname.cstr.tobytes().decode("utf8")) 33 | archive_read_data_skip(a) 34 | funcs.archive_read_free(a) 35 | -------------------------------------------------------------------------------- /include/dffi/ctypes.h: -------------------------------------------------------------------------------- 1 | #ifndef DFFI_CTYPES 2 | #define DFFI_CTYPES 3 | 4 | #include 5 | 6 | // Nothing states in the C++ standard that C basic types are the same than C++ 7 | // basic types. We thus typedef them using extern "C" here! 8 | 9 | extern "C" { 10 | #include 11 | typedef bool c_bool; 12 | typedef char c_char; 13 | typedef signed char c_signed_char; 14 | typedef unsigned char c_unsigned_char; 15 | typedef short c_short; 16 | typedef unsigned short c_unsigned_short; 17 | typedef int c_int; 18 | typedef unsigned int c_unsigned_int; 19 | typedef long c_long; 20 | typedef unsigned long c_unsigned_long; 21 | typedef long long c_long_long; 22 | typedef unsigned long long c_unsigned_long_long; 23 | typedef float c_float; 24 | typedef double c_double; 25 | typedef long double c_long_double; 26 | #ifdef DFFI_SUPPORT_I128 27 | typedef __uint128_t c___uint128_t; 28 | typedef __int128_t c___int128_t; 29 | #endif 30 | #ifdef DFFI_SUPPORT_COMPLEX 31 | typedef _Complex float c_complex_float; 32 | typedef _Complex double c_complex_double; 33 | typedef _Complex long double c_complex_long_double; 34 | #endif 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /tests/dlopen.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clang "%S/lib.c" -shared -O1 -o "%t.so" %TEST_dlopen_LDFLAGS 2 | // RUN: "%build_dir/dlopen%exeext" "%t.so" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | using namespace dffi; 12 | 13 | static bool test(const char* Path) { 14 | const auto DL = DFFI::dlopen(Path); 15 | void* BaseAddr = DL.baseAddress(); 16 | // Check first 4 bytes are the same as Path 17 | std::array Ref,Buf; 18 | memcpy(&Ref[0], BaseAddr, sizeof(Ref)); 19 | 20 | FILE* F = fopen(Path, "rb"); 21 | if (!F) { 22 | fprintf(stderr, "Unable to open %s\n", Path); 23 | return false; 24 | } 25 | const auto Read = fread(&Buf, sizeof(Buf), 1, F); 26 | fclose(F); 27 | if (Read != 1) { 28 | fprintf(stderr, "Unable to read header of %s\n", Path); 29 | return false; 30 | } 31 | printf("Base address: %p\n", BaseAddr); 32 | return Ref == Buf; 33 | } 34 | 35 | int main(int argc, char** argv) 36 | { 37 | if (argc < 1) { 38 | fprintf(stderr, "Usage: %s lib_path\n", argv[0]); 39 | return 1; 40 | } 41 | return test(argv[1]) ? 0:1; 42 | } 43 | -------------------------------------------------------------------------------- /bindings/python/tests/test_wrapper.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | 18 | from common import DFFITest 19 | 20 | class WrapperTest(DFFITest): 21 | def test_wrapper(self): 22 | CU = self.FFI.compile(''' 23 | struct A { 24 | int a; 25 | int b; 26 | }; 27 | struct A init_A(int a, int b) { 28 | struct A ret = {a,b}; 29 | return ret; 30 | } 31 | ''') 32 | FTy = pydffi.typeof(CU.funcs.init_A) 33 | print(FTy.getWrapperLLVMStr("wrap")) 34 | 35 | if __name__ == '__main__': 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /bindings/python/tests/test_symbol.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | import sys 18 | import struct 19 | 20 | from common import DFFITest 21 | 22 | class SymbolTest(DFFITest): 23 | def test_symbol(self): 24 | addr = 0xAABBCCDD 25 | pydffi.addSymbol("my_custom_symbol", 0xAABBCCDD) 26 | F = self.FFI 27 | CU = F.cdef(''' 28 | extern void my_custom_symbol(int); 29 | ''') 30 | self.assertEqual(pydffi.ptr(CU.funcs.my_custom_symbol).value, addr) 31 | 32 | if __name__ == '__main__': 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /bindings/python/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | set(PYDFFI_LIB_DIR $) 16 | set(DFFI_LIB_PATH $) 17 | add_custom_target(check_python 18 | COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${PYDFFI_LIB_DIR} "${PYTHON_EXECUTABLE}" -m unittest discover "${CMAKE_CURRENT_SOURCE_DIR}" 19 | ) 20 | if (WIN32) 21 | add_custom_target(copy_dll 22 | COMMAND ${CMAKE_COMMAND} -E copy ${DFFI_LIB_PATH} ${PYDFFI_LIB_DIR} 23 | ) 24 | add_dependencies(check_python copy_dll) 25 | endif() 26 | add_dependencies(check_python pydffi) 27 | add_dependencies(check check_python) 28 | -------------------------------------------------------------------------------- /bindings/python/tests/test_errors.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # RUN: "%python" "%s" 16 | # 17 | 18 | import pydffi 19 | import unittest 20 | 21 | from common import DFFITest 22 | 23 | class ErrorsTest(DFFITest): 24 | def test_errors(self): 25 | FFI = self.FFI 26 | 27 | with self.assertRaises(Exception): 28 | FFI.compile("this is invalid code") 29 | 30 | with self.assertRaises(Exception): 31 | FFI.compile("this is invalid code again") 32 | 33 | FFI.compile("int foo(int a, int b) { return a+b; }") 34 | 35 | if __name__ == '__main__': 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /bindings/python/tests/test_cdef_types.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # RUN: "%python" "%s" 16 | # 17 | 18 | import unittest 19 | import pydffi 20 | 21 | from common import DFFITest 22 | 23 | class CDefTypesTest(DFFITest): 24 | def test_cdef_types(self): 25 | CU = self.FFI.cdef(''' 26 | #include 27 | typedef int32_t MyInt; 28 | typedef struct { 29 | int a; 30 | int b; 31 | } A; 32 | ''') 33 | 34 | self.assertEqual(CU.types.MyInt, self.FFI.Int32Ty) 35 | self.assertTrue(isinstance(CU.types.A, pydffi.StructType)) 36 | 37 | 38 | if __name__ == '__main__': 39 | unittest.main() 40 | -------------------------------------------------------------------------------- /bindings/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | set(PythonInterp_FIND_VERSION ${PYTHON_VERSION}) 16 | find_package(PythonInterp REQUIRED) 17 | find_package(PythonLibs ${PYTHON_VERSION} REQUIRED) 18 | include_directories(${PYTHON_INCLUDE_DIRS}) 19 | 20 | add_library(pydffi 21 | SHARED 22 | cobj.cpp 23 | pydffi.cpp 24 | ) 25 | set_target_properties(pydffi PROPERTIES PREFIX "") 26 | if (APPLE) 27 | set_target_properties(pydffi PROPERTIES SUFFIX ".so") 28 | elseif(WIN32) 29 | set_target_properties(pydffi PROPERTIES SUFFIX ".pyd") 30 | endif() 31 | target_link_libraries(pydffi 32 | PRIVATE 33 | ${DFFI_LIBRARY} 34 | ${PYTHON_LIBRARIES}) 35 | 36 | add_subdirectory(tests) 37 | -------------------------------------------------------------------------------- /examples/fftw.py: -------------------------------------------------------------------------------- 1 | # REQUIRES: linux && libfftw3 2 | # RUN: %python "%s" 3 | 4 | import math 5 | import ctypes.util 6 | import pydffi 7 | 8 | pydffi.dlopen(ctypes.util.find_library("fftw3")) 9 | FFI = pydffi.FFI() 10 | FFT = FFI.cdef("#include ") 11 | 12 | # Adapted from https://github.com/undees/fftw-example/blob/master/fftw_example.c 13 | NUM_POINTS = 64 14 | fftw_complex = FFT.types.fftw_complex 15 | signal = FFI.arrayType(fftw_complex, NUM_POINTS)(); 16 | result = FFI.arrayType(fftw_complex, NUM_POINTS)(); 17 | 18 | def acquire_from_somewhere(signal): 19 | for i in range(NUM_POINTS): 20 | theta = float(i) / float(NUM_POINTS) * math.pi; 21 | 22 | signal[i][0] = 1.0 * math.cos(10.0 * theta) + \ 23 | 0.5 * math.cos(25.0 * theta); 24 | 25 | signal[i][1] = 1.0 * math.sin(10.0 * theta) + \ 26 | 0.5 * math.sin(25.0 * theta); 27 | 28 | def do_something_with(result): 29 | for i in range(NUM_POINTS): 30 | mag = math.sqrt(result[i][0] * result[i][0] + \ 31 | result[i][1] * result[i][1]); 32 | print("%0.4f" % mag); 33 | 34 | 35 | plan = FFT.funcs.fftw_plan_dft_1d(NUM_POINTS, signal, result, -1, 1<<6) 36 | acquire_from_somewhere(signal) 37 | FFT.funcs.fftw_execute(plan) 38 | do_something_with(result) 39 | -------------------------------------------------------------------------------- /bindings/python/tests/test_includes.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | import os 18 | 19 | from common import DFFITest 20 | 21 | class IncludesTest(DFFITest): 22 | def test_includes(self): 23 | tests_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", "tests", "includes") 24 | F = pydffi.FFI(includeDirs=[tests_dir]) 25 | CU = F.compile(''' 26 | #include "add.h" 27 | ''') 28 | self.assertEqual(CU.funcs.add(4,5), 9) 29 | 30 | with self.assertRaises(pydffi.CompileError): 31 | CU = self.FFI.compile(''' 32 | #include "add.h" 33 | ''') 34 | 35 | if __name__ == '__main__': 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /bindings/python/tests/test_unions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | 18 | from common import DFFITest 19 | 20 | class UnionsTest(DFFITest): 21 | def test_unions(self): 22 | D=self.FFI 23 | CU = D.compile(''' 24 | union A 25 | { 26 | short a; 27 | int b; 28 | }; 29 | 30 | short get_short(union A a) { return a.a; } 31 | int get_int(union A a) { return a.b; } 32 | ''') 33 | 34 | a = CU.types.A() 35 | a.a = 10 36 | self.assertEqual(CU.funcs.get_short(a), 10) 37 | 38 | a = CU.types.A("b", 12345678) 39 | self.assertEqual(CU.funcs.get_int(a), 12345678) 40 | 41 | if __name__ == '__main__': 42 | unittest.main() 43 | -------------------------------------------------------------------------------- /bindings/python/tests/test_cxx.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | import struct 18 | import sys 19 | 20 | from common import DFFITest 21 | 22 | class CXXTest(DFFITest): 23 | def __init__(self, *args, **kwargs): 24 | super(CXXTest, self).__init__(*args, **kwargs) 25 | self.options = {'CXX': pydffi.CXXMode.Std11} 26 | 27 | def test_cxx(self): 28 | FFI = self.FFI 29 | CU = FFI.compile(''' 30 | template 31 | static T foo(T a, T b) { return a+b; } 32 | extern "C" int foo_int(int a, int b) { return foo(a,b); } 33 | ''') 34 | self.assertEqual(CU.funcs.foo_int(4,5).value, 9) 35 | 36 | if __name__ == '__main__': 37 | unittest.main() 38 | -------------------------------------------------------------------------------- /tests/array.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/array%exeext" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main(int argc, char** argv) 23 | { 24 | DFFI::initialize(); 25 | 26 | CCOpts Opts; 27 | Opts.OptLevel = 2; 28 | 29 | DFFI Jit(Opts); 30 | 31 | std::string Err; 32 | auto CU = Jit.compile(R"( 33 | struct A 34 | { 35 | char buf[256]; 36 | }; 37 | struct A init() { 38 | struct A ret; 39 | strcpy(&ret.buf[0], "hello!"); 40 | return ret; 41 | } 42 | void print(struct A* a) { 43 | puts(a->buf); 44 | } 45 | )", Err); 46 | if (!CU) { 47 | std::cerr << Err << std::endl; 48 | return 1; 49 | } 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Tests Linux 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-22.04 13 | strategy: 14 | matrix: 15 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | - name: Install system dependencies 24 | run: | 25 | sudo apt-get update && sudo apt-get install -y clang-13 llvm-13-tools llvm-13-dev libclang-13-dev ninja-build cmake libarchive-dev libfftw3-dev 26 | python -m pip install lit 27 | python -m pip install purectypes==0.2 six 28 | mkdir build 29 | - name: CMake 30 | working-directory: build 31 | run: | 32 | cmake \ 33 | -DBUILD_SHARED_LIBS=ON \ 34 | -DPYTHON_BINDINGS=ON \ 35 | -DBUILD_TESTS=ON \ 36 | -DLLVM_CONFIG=$(which llvm-config-13) \ 37 | -DCMAKE_BUILD_TYPE=debug \ 38 | -G Ninja .. 39 | - name: Build 40 | working-directory: build 41 | run: | 42 | ninja 43 | - name: Tests 44 | working-directory: build 45 | run: | 46 | ninja check 47 | -------------------------------------------------------------------------------- /tests/stdint.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/stdint%exeext" 16 | 17 | #include 18 | 19 | #include 20 | 21 | using namespace dffi; 22 | 23 | int main() 24 | { 25 | DFFI::initialize(); 26 | 27 | CCOpts Opts; 28 | Opts.OptLevel = 2; 29 | 30 | DFFI Jit(Opts); 31 | 32 | std::string Err; 33 | auto CU = Jit.compile(R"( 34 | #include 35 | uint32_t foo(uint32_t a) { 36 | return a+1; 37 | } 38 | )", Err); 39 | if (!CU) { 40 | std::cerr << Err << std::endl; 41 | return 1; 42 | } 43 | 44 | auto const* STy = CU.getType("uint32_t"); 45 | if (!STy) { 46 | std::cerr << "unable to find type 'uint32_t'!" << std::endl; 47 | return 1; 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /bindings/python/tests/test_structs_portable_format.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | import struct 18 | 19 | from common import DFFITest 20 | 21 | class StructsPortableFormatTest(DFFITest): 22 | def test_structs_portable_format(self): 23 | CU = self.FFI.cdef(''' 24 | #include 25 | #include 26 | typedef struct { 27 | bool valid; 28 | void* a; 29 | unsigned short len; 30 | size_t v; 31 | } A; 32 | ''') 33 | 34 | a = CU.types.A(valid=1,len=0xBBAA,v=0xDDCCBBAA) 35 | av = pydffi.view_as_bytes(a) 36 | self.assertEqual(struct.unpack(CU.types.A.format, av), struct.unpack(CU.types.A.portable_format, av)) 37 | 38 | if __name__ == '__main__': 39 | unittest.main() 40 | -------------------------------------------------------------------------------- /bindings/python/tests/test_anon_struct.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | 18 | from common import DFFITest 19 | 20 | class AnonStructTest(DFFITest): 21 | def test_anon_struct(self): 22 | CU = self.FFI.compile(''' 23 | struct A { 24 | struct { 25 | int a; 26 | int b; 27 | } s; 28 | }; 29 | 30 | void dump(struct A a) { 31 | printf("s.a=%d, s.b=%d\\n", a.s.a, a.s.b); 32 | } 33 | ''') 34 | 35 | A = CU.types.A() 36 | Obj = A.s 37 | Ty = pydffi.typeof(Obj) 38 | fields = [f.name for f in iter(Ty)] 39 | self.assertEqual(set(fields), set(("a","b"))) 40 | 41 | if __name__ == '__main__': 42 | unittest.main() 43 | -------------------------------------------------------------------------------- /bindings/python/tests/test_lasterror.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Copyright 2021 Adrien Guinet 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import unittest 17 | import pydffi 18 | import platform 19 | 20 | from common import DFFITest 21 | 22 | class LastErrorTest(DFFITest): 23 | def test_lasterror(self): 24 | J=self.FFI 25 | 26 | if platform.system() == 'Windows': 27 | code = ''' 28 | #include 29 | void seterrno(int val) { 30 | SetLastError(val); 31 | } 32 | ''' 33 | else: 34 | code = ''' 35 | #include 36 | void seterrno(int val) { 37 | errno = val; 38 | } 39 | ''' 40 | CU = J.compile(code, useLastError=True) 41 | CU.funcs.seterrno(10) 42 | self.assertEqual(pydffi.getLastError(), 10) 43 | 44 | if __name__ == '__main__': 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /include/dffi/iterator_extras.h: -------------------------------------------------------------------------------- 1 | #ifndef DFFI_ITERATORS_H 2 | #define DFFI_ITERATORS_H 3 | 4 | #include 5 | 6 | namespace dffi { 7 | 8 | // Adapted from LLVM's STLExtras.h 9 | 10 | // mapped_iterator - This is a simple iterator adapter that causes a function to 11 | // be applied whenever operator* is invoked on the iterator. 12 | 13 | template ()(*std::declval()))> 16 | class mapped_iterator 17 | : public iterator_adaptor_base< 18 | mapped_iterator, ItTy, 19 | typename std::iterator_traits::iterator_category, 20 | typename std::remove_reference::type> { 21 | public: 22 | mapped_iterator(ItTy U, FuncTy F) 23 | : mapped_iterator::iterator_adaptor_base(std::move(U)), F(std::move(F)) {} 24 | 25 | ItTy getCurrent() { return this->I; } 26 | 27 | FuncReturnTy operator*() { return F(*this->I); } 28 | 29 | private: 30 | FuncTy F; 31 | }; 32 | 33 | // map_iterator - Provide a convenient way to create mapped_iterators, just like 34 | // make_pair is useful for creating pairs... 35 | template 36 | inline mapped_iterator map_iterator(ItTy I, FuncTy F) { 37 | return mapped_iterator(std::move(I), std::move(F)); 38 | } 39 | 40 | } // dffi 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /bindings/python/tests/common.py: -------------------------------------------------------------------------------- 1 | import pydffi 2 | import unittest 3 | import six 4 | import platform 5 | import subprocess 6 | 7 | try: 8 | import purectypes 9 | purectypes.generators.pydffi 10 | has_purectypes = True 11 | except (ImportError, AttributeError): 12 | has_purectypes = False 13 | 14 | def getFFI(options = None): 15 | if options is None: 16 | options = {} 17 | if platform.system() == "Darwin": 18 | options["sysroot"] = subprocess.check_output(["xcrun","--show-sdk-path"]).strip() 19 | return pydffi.FFI(**options) 20 | 21 | class DFFITest(unittest.TestCase): 22 | def __init__(self, *args, **kwargs): 23 | super(DFFITest, self).__init__(*args, **kwargs) 24 | self.options = {} 25 | 26 | def setUp(self): 27 | self.FFI = getFFI(self.options) 28 | 29 | def cstr_from_array(self, ar): 30 | return pydffi.cast(pydffi.ptr(ar), self.FFI.CharPtrTy).cstr 31 | 32 | @unittest.skipIf(not has_purectypes, "purectypes not installed") 33 | class GenCTypesTest(unittest.TestCase): 34 | def setUp(self): 35 | self.purectypes = purectypes 36 | 37 | def generated_types(self, T, name): 38 | G = purectypes.generators.pydffi() 39 | A = G(T) 40 | yield A 41 | 42 | # Dump, eval and retry 43 | D = purectypes.dump(A) 44 | globals_ = {} 45 | O = six.exec_(D, globals_) 46 | yield globals_[name] 47 | -------------------------------------------------------------------------------- /tests/compile.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/compile%exeext" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main(int argc, char** argv) 23 | { 24 | DFFI::initialize(); 25 | 26 | CCOpts Opts; 27 | Opts.OptLevel = 2; 28 | 29 | DFFI Jit(Opts); 30 | 31 | std::string Err; 32 | auto CU = Jit.compile("int add(int a, int b) { return a+b; }", Err); 33 | if (!CU) { 34 | std::cerr << "Compile error: " << Err << std::endl; 35 | return 1; 36 | } 37 | int a = 2; 38 | int b = 4; 39 | int ret; 40 | void* Args_[] = {&a, &b}; 41 | CU.getFunction("add").call(&ret, &Args_[0]); 42 | if (ret != a+b) { 43 | std::cerr << "Invalid sum!" << std::endl; 44 | return 1; 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /tests/multiple_defs.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/multiple_defs%exeext" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main() 23 | { 24 | DFFI::initialize(); 25 | 26 | CCOpts Opts; 27 | Opts.OptLevel = 2; 28 | Opts.LazyJITWrappers = false; 29 | 30 | DFFI Jit(Opts); 31 | 32 | std::string Err; 33 | auto CU = Jit.cdef(R"( 34 | int foo(int a); 35 | int foo(int a); 36 | )", nullptr, Err); 37 | if (!CU) { 38 | std::cerr << Err << std::endl; 39 | return 1; 40 | } 41 | 42 | Jit.compile("int foo(int a) { return a+1; }", Err); 43 | 44 | NativeFunc F = CU.getFunction("foo"); 45 | if (!F) { 46 | std::cerr << "foo isn't available!" << std::endl; 47 | return 1; 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /bindings/python/tests/test_basicobjs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import random 17 | 18 | import pydffi 19 | from common import DFFITest 20 | 21 | class BasicObjsTest(DFFITest): 22 | def test_basicobjs(self): 23 | D = self.FFI 24 | for Ty in (D.Int8Ty, D.UInt8Ty, D.Int16Ty, D.UInt16Ty, D.Int32Ty, D.UInt32Ty, D.Int64Ty, D.UInt64Ty): 25 | for i in range(10): 26 | v = random.getrandbits(Ty.size*8-1) 27 | self.assertEqual(Ty(v), Ty(v)) 28 | self.assertEqual(Ty(v), v) 29 | self.assertTrue(Ty(0) <= Ty(v)) 30 | self.assertTrue(Ty(0) <= v) 31 | self.assertTrue(Ty(v) >= Ty(0)) 32 | self.assertTrue(Ty(v) >= 0) 33 | 34 | if __name__ == '__main__': 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /tests/inline.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/inline%exeext" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main() 23 | { 24 | DFFI::initialize(); 25 | 26 | CCOpts Opts; 27 | Opts.OptLevel = 2; 28 | Opts.LazyJITWrappers = false; 29 | 30 | DFFI Jit(Opts); 31 | 32 | std::string Err; 33 | auto CU = Jit.cdef(R"( 34 | inline int foo() { return 1; } 35 | int bar() { return foo(); } 36 | )", nullptr, Err); 37 | if (!CU) { 38 | std::cerr << Err << std::endl; 39 | return 1; 40 | } 41 | 42 | Jit.compile("int foo() { return 1; }", Err); 43 | 44 | NativeFunc F = CU.getFunction("foo"); 45 | if (!F) { 46 | std::cerr << "foo isn't available!" << std::endl; 47 | return 1; 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /bindings/python/tests/test_bool.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import random 17 | 18 | import pydffi 19 | from common import DFFITest 20 | 21 | class BoolTest(DFFITest): 22 | def test_bool(self): 23 | CU = self.FFI.compile(''' 24 | #include 25 | bool foo(int a) { return a==1; } 26 | bool invert(const bool v) { return !v; } 27 | ''') 28 | 29 | self.assertTrue(CU.funcs.foo(1)) 30 | self.assertFalse(CU.funcs.foo(0)) 31 | self.assertFalse(CU.funcs.invert(True)) 32 | self.assertTrue(CU.funcs.invert(False)) 33 | 34 | b = CU.funcs.foo(1) 35 | self.assertTrue(b) 36 | self.assertFalse(not b) 37 | self.assertFalse(b & False) 38 | self.assertTrue(b & True) 39 | 40 | if __name__ == '__main__': 41 | unittest.main() 42 | -------------------------------------------------------------------------------- /bindings/python/tests/test_ignored.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pydffi 16 | import unittest 17 | 18 | from common import DFFITest 19 | 20 | class IgnoredTest(DFFITest): 21 | def verify_ignored(self, CU, name): 22 | with self.assertRaises(pydffi.UnknownFunctionError): 23 | getattr(CU.funcs,name) 24 | 25 | def test_ignored(self): 26 | D = self.FFI 27 | CU = D.compile(''' 28 | #include 29 | #include 30 | __attribute__((noreturn)) void fatal(const char* err) { 31 | puts(err); 32 | exit(1); 33 | } 34 | ''') 35 | self.verify_ignored(CU, "fatal") 36 | 37 | CU = D.cdef(''' 38 | __attribute__((noreturn)) void fatal(const char* err); 39 | ''') 40 | self.verify_ignored(CU, "fatal") 41 | 42 | if __name__ == '__main__': 43 | unittest.main() 44 | -------------------------------------------------------------------------------- /lib/dffi_api_osx.inc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Inspired by https://github.com/JuliaLang/julia/blob/0027ed143e90d0f965694de7ea8c692d75ffa1a5/src/sys.c#L572-L583 5 | void* dffi::DynamicLibrary::baseAddress() const 6 | { 7 | assert(valid()); 8 | // We start by the end, because chances are we just loaded that library! 9 | const int32_t Count = _dyld_image_count(); 10 | if (Count <= 0) { 11 | return nullptr; 12 | } 13 | for (int32_t i = Count-1; i >= 0; --i) { 14 | // dlopen() each image, check handle 15 | const char *image_name = _dyld_get_image_name(i); 16 | void *probe_handle = dlopen(image_name, RTLD_LAZY); 17 | dlclose(probe_handle); 18 | 19 | // If the handle is the same as what was passed in (modulo mode bits), return this image name 20 | if (((uintptr_t)Data_ & (~uintptr_t(3U))) == ((uintptr_t)probe_handle & (~uintptr_t(3U)))) { 21 | return (void*)_dyld_get_image_vmaddr_slide(i); 22 | } 23 | } 24 | return nullptr; 25 | } 26 | 27 | // LastError handling 28 | #include 29 | #include 30 | 31 | // TODO: verify initial value 32 | static thread_local int dffi_errno = 0; 33 | 34 | void dffi::NativeFunc::swapLastError() 35 | { 36 | std::swap(dffi_errno, errno); 37 | } 38 | 39 | dffi::NativeFunc::LastErrorTy dffi::NativeFunc::getLastError() { 40 | return dffi_errno; 41 | } 42 | 43 | void dffi::NativeFunc::setLastError(LastErrorTy Err) { 44 | dffi_errno = Err; 45 | } 46 | -------------------------------------------------------------------------------- /bindings/python/tests/test_str.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Copyright 2018 Adrien Guinet 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import unittest 17 | import pydffi 18 | 19 | from common import DFFITest 20 | 21 | class StrTest(DFFITest): 22 | def test_str(self): 23 | J=self.FFI 24 | 25 | CU = J.compile(''' 26 | #include 27 | #include 28 | 29 | bool check_str0(const char* msg) { 30 | return strcmp(msg, "coucou") == 0; 31 | } 32 | bool check_str1(const char* msg) { 33 | return strcmp(msg, "héllo") == 0; 34 | } 35 | const char* get_str() { return "hello"; } 36 | ''') 37 | get_str = CU.funcs.get_str 38 | 39 | self.assertTrue(CU.funcs.check_str0("coucou")) 40 | self.assertTrue(CU.funcs.check_str1("héllo")) 41 | 42 | self.assertEqual((get_str().cstr).tobytes(), b"hello") 43 | 44 | if __name__ == '__main__': 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /include/dffi/exports.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef DFFI_EXPORTS_H 16 | #define DFFI_EXPORTS_H 17 | 18 | #if defined _WIN32 || defined __CYGWIN__ 19 | #define DFFI_HELPER_IMPORT __declspec(dllimport) 20 | #define DFFI_HELPER_EXPORT __declspec(dllexport) 21 | #define DFFI_HELPER_LOCAL 22 | #else 23 | #define DFFI_HELPER_IMPORT __attribute__ ((visibility ("default"))) 24 | #define DFFI_HELPER_EXPORT __attribute__ ((visibility ("default"))) 25 | #define DFFI_HELPER_LOCAL __attribute__ ((visibility ("hidden"))) 26 | #endif 27 | 28 | #ifdef dffi_STATIC 29 | #define DFFI_API 30 | #define DFFI_LOCAL 31 | #elif defined(dffi_EXPORTS) 32 | #define DFFI_API DFFI_HELPER_EXPORT 33 | #define DFFI_LOCAL DFFI_HELPER_LOCAL 34 | #else 35 | #define DFFI_API DFFI_HELPER_IMPORT 36 | #define DFFI_LOCAL DFFI_HELPER_LOCAL 37 | #endif 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /bindings/python/tests/test_funcs_prop.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # RUN: "%python" "%s" | "%FileCheck" "%s" 16 | # 17 | 18 | import unittest 19 | import pydffi 20 | 21 | from common import DFFITest 22 | 23 | class FuncsPropTest(DFFITest): 24 | def test_funcs_prop(self): 25 | D = self.FFI 26 | CU = D.compile(''' 27 | #include 28 | #include 29 | 30 | int foo() { return 42; } 31 | 32 | struct A { 33 | int a; 34 | }; 35 | __attribute__((ms_abi)) bool verify(struct A a, int ref) { 36 | return a.a == ref; 37 | } 38 | 39 | bool verify_int(int a, int ref) { return a == ref; } 40 | ''') 41 | self.assertEqual(CU.funcs.foo().value, 42) 42 | 43 | a = CU.types.A(a=15) 44 | self.assertTrue(getattr(CU.funcs, "verify")(a, 15)) 45 | self.assertTrue(CU.funcs.verify_int(D.Int32(42), 42)) 46 | 47 | if __name__ == '__main__': 48 | unittest.main() 49 | -------------------------------------------------------------------------------- /bindings/python/tests/test_array_ptr.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | 18 | from common import DFFITest 19 | 20 | class ArrayPtrTest(DFFITest): 21 | def test_array_ptr(self): 22 | CU = self.FFI.compile(''' 23 | #include 24 | typedef int int2[2]; 25 | void foo(char* buf, size_t n, int2* ar) 26 | { 27 | snprintf(buf, n, "%d %d", ar[0][0], ar[0][1]); 28 | } 29 | ''') 30 | 31 | v = CU.types.int2() 32 | v.set(0, 1) 33 | v.set(1, 2) 34 | buf = self.FFI.arrayType(self.FFI.CharTy, 128)() 35 | CU.funcs.foo(pydffi.ptr(buf), 128, pydffi.ptr(v)) 36 | self.assertEqual(self.cstr_from_array(buf), b"1 2") 37 | 38 | v[0] = 10 39 | v[1] = 20 40 | CU.funcs.foo(pydffi.ptr(buf), 128, pydffi.ptr(v)) 41 | self.assertEqual(self.cstr_from_array(buf), b"10 20") 42 | 43 | if __name__ == '__main__': 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /include/dffi/compat.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef DFFI_COMPAT_H 16 | #define DFFI_COMPAT_H 17 | 18 | // Adapted from LLVM's Compiler.h 19 | 20 | #ifndef __has_cpp_attribute 21 | #define __has_cpp_attribute(x) 0 22 | #endif 23 | 24 | #if __cplusplus > 201402L && __has_cpp_attribute(nodiscard) 25 | #define DFFI_NODISCARD [[nodiscard]] 26 | #elif !__cplusplus 27 | // Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious 28 | // error when __has_cpp_attribute is given a scoped attribute in C mode. 29 | #define DFFI_NODISCARD 30 | #elif __has_cpp_attribute(clang::warn_unused_result) 31 | #define DFFI_NODISCARD [[clang::warn_unused_result]] 32 | #else 33 | #define DFFI_NODISCARD 34 | #endif 35 | 36 | #ifdef _MSC_VER 37 | #define ALIGN(N) __declspec(align(N)) 38 | #else 39 | #define ALIGN(N) __attribute__((aligned(N))) 40 | #endif 41 | 42 | #if defined(_MSC_VER) 43 | #include 44 | typedef SSIZE_T ssize_t; 45 | #endif 46 | 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /.github/workflows/osx.yml.dis: -------------------------------------------------------------------------------- 1 | name: Tests OSX 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macOS-latest 13 | strategy: 14 | matrix: 15 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | - name: Setup Miniconda 24 | uses: conda-incubator/setup-miniconda@v3.1.1 25 | - name: Install system dependencies 26 | run: | 27 | brew install cmake ninja 28 | sudo conda install -c conda-forge llvmdev==13.0.0 clangdev==13.0.0 29 | python -m pip install --upgrade pip setuptools 30 | python -m pip install lit purectypes==0.2 six psutil 31 | mkdir build 32 | - name: CMake 33 | working-directory: build 34 | run: | 35 | cmake \ 36 | -DBUILD_SHARED_LIBS=ON \ 37 | -DPYTHON_BINDINGS=ON \ 38 | -DBUILD_TESTS=ON \ 39 | -DLLVM_CONFIG=$CONDA/bin/llvm-config -DCMAKE_BUILD_TYPE=debug \ 40 | -DCMAKE_BUILD_TYPE=Debug \ 41 | -G Ninja .. 42 | - name: Build 43 | working-directory: build 44 | run: | 45 | ninja 46 | - name: Tests Python 47 | working-directory: build 48 | run: | 49 | ninja check_python 50 | - name: Tests native 51 | working-directory: build 52 | run: | 53 | ninja check_lib 54 | -------------------------------------------------------------------------------- /tests/enum.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/enum%exeext" | "%FileCheck" "%s" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace dffi; 22 | 23 | int main(int argc, char** argv) 24 | { 25 | DFFI::initialize(); 26 | 27 | CCOpts Opts; 28 | Opts.OptLevel = 2; 29 | 30 | DFFI Jit(Opts); 31 | 32 | std::string Err; 33 | auto CU = Jit.compile(R"( 34 | enum A 35 | { 36 | V0 = 0, 37 | V1 = 1, 38 | V4 = 4, 39 | V10 = 10 40 | }; 41 | 42 | int get(enum A a) { 43 | return a; 44 | } 45 | )", Err); 46 | if (!CU) { 47 | std::cerr << Err << std::endl; 48 | return 1; 49 | } 50 | 51 | auto* Enum = CU.getEnumType("A"); 52 | // CHECK-DAG: V0 = 0 53 | // CHECK-DAG: V1 = 1 54 | // CHECK-DAG: V4 = 4 55 | // CHECK-DAG: V10 = 10 56 | for (auto const& F: Enum->getFields()) { 57 | std::cout << F.first << " = " << F.second << std::endl; 58 | } 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /bindings/python/tests/test_new_structs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | 18 | from common import DFFITest 19 | 20 | class NewStructsTest(DFFITest): 21 | def test_new_structs(self): 22 | D = self.FFI 23 | CU = D.compile(''' 24 | #include 25 | struct A { 26 | int a; 27 | short b; 28 | }; 29 | void print(char* ret, size_t n, struct A a) { 30 | snprintf(ret, n, "%d %d", a.a, a.b); 31 | } 32 | ''') 33 | 34 | buf = self.FFI.arrayType(self.FFI.CharTy, 128)() 35 | a = CU.types.A(a=4,b=10) 36 | getattr(CU.funcs, "print")(pydffi.ptr(buf), 128, a) 37 | self.assertEqual(self.cstr_from_array(buf), b"4 10") 38 | 39 | a = CU.types.A(a=0,b=0) 40 | getattr(CU.funcs, "print")(pydffi.ptr(buf), 128, a) 41 | self.assertEqual(self.cstr_from_array(buf), b"0 0") 42 | 43 | if __name__ == '__main__': 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /tests/compile_cxx.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/compile_cxx%exeext" 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | using namespace dffi; 23 | 24 | int main(int argc, char** argv) 25 | { 26 | DFFI::initialize(); 27 | 28 | CCOpts Opts; 29 | Opts.OptLevel = 2; 30 | Opts.CXX = CXXMode::Std11; 31 | 32 | DFFI Jit(Opts); 33 | 34 | std::string Err; 35 | auto CU = Jit.compile(R"( 36 | template 37 | static T foo(T a, T b) { return a+b; } 38 | extern "C" int foo_int(int a, int b) { return foo(a,b); } 39 | )", Err); 40 | if (!CU) { 41 | std::cerr << "Compile error: " << Err << std::endl; 42 | return 1; 43 | } 44 | int a = 2; 45 | int b = 4; 46 | int ret; 47 | void* Args_[] = {&a, &b}; 48 | CU.getFunction("foo_int").call(&ret, &Args_[0]); 49 | if (ret != a+b) { 50 | std::cerr << "Invalid sum!" << std::endl; 51 | return 1; 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /bindings/python/tests/test_enum_.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pydffi 16 | import unittest 17 | 18 | from common import DFFITest 19 | 20 | try: 21 | long(0) 22 | except: 23 | long = lambda v: int(v) 24 | 25 | class EnumTest(DFFITest): 26 | def test_enum(self): 27 | D = self.FFI 28 | CU = D.compile(''' 29 | enum A { 30 | V0 = 0, 31 | V10 = 10 32 | }; 33 | 34 | struct S { 35 | enum A a; 36 | }; 37 | 38 | int get(enum A a) { return a; } 39 | enum A get_a(struct S s) { return s.a; } 40 | ''') 41 | 42 | A = CU.types.A 43 | self.assertEqual(int(A.V0), 0) 44 | self.assertEqual(int(A.V10), 10) 45 | 46 | self.assertEqual(set(iter(A)), {(u"V0",long(0)),(u"V10",long(10))}) 47 | self.assertEqual(set(dict(iter(A)).items()), {(u"V0",long(0)),(u"V10",long(10))}) 48 | 49 | S = CU.types.S() 50 | S.a = 10 51 | A = S.a 52 | self.assertEqual(A, 10) 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | -------------------------------------------------------------------------------- /third-party/cc-clang.patch: -------------------------------------------------------------------------------- 1 | commit 316b47b8314e51d1f3055f1c32d32b4f1f0375f3 2 | Author: Adrien Guinet 3 | Date: Sun Jan 21 12:40:46 2018 +0100 4 | 5 | Emit DWARF private CC for every supported Clang CC 6 | 7 | This needs the associated LLVM commit! 8 | 9 | diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp 10 | index 18b1d10..ffc71ed 100644 11 | --- a/lib/CodeGen/CGDebugInfo.cpp 12 | +++ b/lib/CodeGen/CGDebugInfo.cpp 13 | @@ -954,20 +954,28 @@ static unsigned getDwarfCC(CallingConv CC) { 14 | return llvm::dwarf::DW_CC_LLVM_vectorcall; 15 | case CC_X86Pascal: 16 | return llvm::dwarf::DW_CC_BORLAND_pascal; 17 | - 18 | - // FIXME: Create new DW_CC_ codes for these calling conventions. 19 | case CC_Win64: 20 | + return llvm::dwarf::DW_CC_LLVM_Win64; 21 | case CC_X86_64SysV: 22 | + return llvm::dwarf::DW_CC_LLVM_X86_64SysV; 23 | case CC_AAPCS: 24 | + return llvm::dwarf::DW_CC_LLVM_AAPCS; 25 | case CC_AAPCS_VFP: 26 | + return llvm::dwarf::DW_CC_LLVM_AAPCS_VFP; 27 | case CC_IntelOclBicc: 28 | + return llvm::dwarf::DW_CC_LLVM_IntelOclBicc; 29 | case CC_SpirFunction: 30 | + return llvm::dwarf::DW_CC_LLVM_SpirFunction; 31 | case CC_OpenCLKernel: 32 | + return llvm::dwarf::DW_CC_LLVM_OpenCLKernel; 33 | case CC_Swift: 34 | + return llvm::dwarf::DW_CC_LLVM_Swift; 35 | case CC_PreserveMost: 36 | + return llvm::dwarf::DW_CC_LLVM_PreserveMost; 37 | case CC_PreserveAll: 38 | + return llvm::dwarf::DW_CC_LLVM_PreserveAll; 39 | case CC_X86RegCall: 40 | - return 0; 41 | + return llvm::dwarf::DW_CC_LLVM_X86RegCall; 42 | } 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /bindings/python/tests/test_views.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | import sys 18 | import struct 19 | 20 | from common import DFFITest 21 | 22 | class ViewsTest(DFFITest): 23 | def test_views(self): 24 | F = self.FFI 25 | CU = F.cdef(''' 26 | #include 27 | typedef struct { 28 | uint32_t a; 29 | uint64_t b; 30 | } A; 31 | ''') 32 | 33 | A = CU.types.A 34 | Len = A.size 35 | S = pydffi.view_as(pydffi.const(A), b"A"*Len) 36 | self.assertEqual(int(S.a), 0x41414141) 37 | self.assertEqual(int(S.b), 0x4141414141414141) 38 | B = bytearray(pydffi.view_as_bytes(S)) 39 | self.assertEqual(B, b"A"*Len) 40 | 41 | B = pydffi.view_as_bytes(S) 42 | One = 1 if sys.version_info >= (3, 0) else struct.pack("B", 1) 43 | B[0] = One 44 | B[1] = One 45 | B[2] = One 46 | B[3] = One 47 | self.assertEqual(int(S.a), 0x01010101) 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | -------------------------------------------------------------------------------- /tests/compile_error.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/compile_error%exeext" 1>"%t.out" 2>"%t.err" 16 | // RUN: "%FileCheck" "%s" <"%t.out" 17 | // RUN: "%count" 0 <"%t.err" 18 | 19 | #include 20 | #include 21 | 22 | using namespace dffi; 23 | 24 | int main(int argc, char** argv) 25 | { 26 | DFFI::initialize(); 27 | 28 | CCOpts Opts; 29 | Opts.OptLevel = 2; 30 | 31 | DFFI Jit(Opts); 32 | 33 | std::string Err; 34 | // CHECK: error: unknown type name 'this' 35 | if (Jit.compile("this is not valid C code", Err)) { 36 | // We want an error, so returning 1 in the case no error is found! 37 | return 1; 38 | } 39 | 40 | std::cout << Err << std::endl; 41 | 42 | // CHECK-NOT: error: unknown type name 'this' 43 | // CHECK-DAG: error: unknown type name 'still' 44 | if (Jit.compile("still invalide code", Err)) { 45 | return 1; 46 | } 47 | 48 | std::cout << Err << std::endl; 49 | 50 | if (!Jit.compile("int foo(int a, int b) { return a+b; }", Err)) { 51 | std::cout << "error!! " << Err << std::endl; 52 | return 1; 53 | } 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /tests/anon_union.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/anon_union%exeext" | "%FileCheck" "%s" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | typedef struct { 23 | char a; 24 | union { 25 | int a; 26 | short b; 27 | } u; 28 | } A; 29 | 30 | int main() 31 | { 32 | DFFI::initialize(); 33 | 34 | CCOpts Opts; 35 | Opts.OptLevel = 2; 36 | 37 | DFFI Jit(Opts); 38 | 39 | std::string Err; 40 | auto CU = Jit.compile(R"( 41 | typedef struct { 42 | char a; 43 | union { 44 | int a; 45 | short b; 46 | } u; 47 | } A; 48 | 49 | void dump(A a) { 50 | printf("u.a=%d, u.b=%d\n", a.u.a, a.u.b); 51 | } 52 | 53 | /*void dump_anon(struct { int a; int b; } s) { 54 | printf("a=%d, b=%d\n", s.a, s.b); 55 | }*/ 56 | )", Err); 57 | if (!CU) { 58 | std::cerr << Err << std::endl; 59 | return 1; 60 | } 61 | 62 | A a; 63 | a.u.a = 15; 64 | void* Args[] = { &a }; 65 | // CHECK: u.a=15, u.b=15 66 | CU.getFunction("dump").call(&Args[0]); 67 | //Args[0] = &a.s; 68 | //CU.getFunction("dump_anon").call(&Args[0]); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /.github/workflows/win.yml.dis: -------------------------------------------------------------------------------- 1 | # Disabled because the conda llvm 13 package is broken. Tests are done with the 2 | # compiled wheel... 3 | 4 | name: Tests Windows 5 | 6 | 7 | on: 8 | push: 9 | branches: 10 | - master 11 | pull_request: 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: windows-latest 17 | strategy: 18 | matrix: 19 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Setup Miniconda 28 | uses: conda-incubator/setup-miniconda@v3.1.1 29 | - name: Install system dependencies 30 | run: | 31 | choco install --accept-license ninja 32 | conda activate base 33 | conda update -c conda-forge python 34 | conda install -c conda-forge llvmdev==13.0.0 clangdev==13.0.0 35 | python -m pip install --upgrade pip 36 | python -m pip install lit purectypes==0.2 six setuptools 37 | mkdir build 38 | - uses: ilammy/msvc-dev-cmd@v1 39 | - name: CMake + build 40 | working-directory: build 41 | run: | 42 | cmake -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe -DPYTHON_BINDINGS=ON -DBUILD_TESTS=ON -DLLVM_CONFIG=C:/Miniconda/condabin/llvm-config.exe -DCMAKE_BUILD_TYPE=release -DCMAKE_PREFIX_PATH="C:/Miniconda/Library/lib/" -G Ninja .. 43 | ninja 44 | - name: Native & Python tests 45 | working-directory: build 46 | run: | 47 | $env:Path = "C:/Miniconda/Library/bin/;$env:Path" 48 | copy C:\Miniconda\Library\bin\zlib.dll bindings\python\zlib.dll 49 | ninja check 50 | -------------------------------------------------------------------------------- /tests/decl_cxx.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/decl_cxx%exeext" 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | using namespace dffi; 23 | 24 | int main(int argc, char** argv) 25 | { 26 | DFFI::initialize(); 27 | 28 | CCOpts Opts; 29 | Opts.OptLevel = 2; 30 | Opts.CXX = CXXMode::Std11; 31 | Opts.LazyJITWrappers = false; 32 | 33 | DFFI Jit(Opts); 34 | 35 | std::string Err; 36 | auto CU = Jit.cdef(R"( 37 | extern "C" void print(const char* s); 38 | extern "C" { 39 | int foo(); 40 | int bar(); 41 | } 42 | )", 43 | nullptr, Err); 44 | 45 | #define CHECK_COMPILE()\ 46 | if (!CU) {\ 47 | std::cerr << Err << std::endl;\ 48 | return 1;\ 49 | } 50 | CHECK_COMPILE() 51 | 52 | const auto funcs = CU.getFunctions(); 53 | const std::set funcsSet(funcs.begin(), funcs.end()); 54 | 55 | #define CHECK_FUNC(name) \ 56 | if (funcsSet.find(name) == funcsSet.end()) { \ 57 | std::cerr << "missing " << name << std::endl; \ 58 | return 1; \ 59 | } 60 | 61 | CHECK_FUNC("print"); 62 | CHECK_FUNC("foo"); 63 | CHECK_FUNC("bar"); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /bindings/python/tests/test_constness.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | import struct 18 | import sys 19 | 20 | from common import DFFITest 21 | 22 | class ConstnessTest(DFFITest): 23 | def test_constness(self): 24 | FFI = self.FFI 25 | CU = FFI.compile(''' 26 | static int ar[] = {0,1,2,3,4}; 27 | const int* foo() { 28 | return ar; 29 | } 30 | 31 | struct A { 32 | int v; 33 | }; 34 | 35 | struct A ga = {1}; 36 | 37 | const struct A* bar() { return &ga; } 38 | ''') 39 | 40 | ar = CU.funcs.foo() 41 | ar = ar.viewObjects(5) 42 | def upck_int(v): 43 | if isinstance(v,int): 44 | return v 45 | return struct.unpack("i",v)[0] 46 | self.assertTrue(all(upck_int(v) == i for i,v in enumerate(ar))) 47 | 48 | with self.assertRaises(Exception): 49 | ar[0] = 1 if sys.version_info >= (3, 0) else struct.pack("B", 1) 50 | 51 | ga = CU.funcs.bar() 52 | with self.assertRaises(Exception): 53 | ga.obj.v = 2 54 | 55 | self.assertTrue(pydffi.cast(ga.obj, CU.types.A) is None) 56 | 57 | if __name__ == '__main__': 58 | unittest.main() 59 | -------------------------------------------------------------------------------- /tests/attrs.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/attrs%exeext" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main(int argc, char** argv) 23 | { 24 | DFFI::initialize(); 25 | 26 | CCOpts Opts; 27 | Opts.OptLevel = 2; 28 | Opts.LazyJITWrappers = false; 29 | 30 | DFFI Jit(Opts); 31 | 32 | std::string Err; 33 | 34 | auto CU = Jit.compile(R"( 35 | int __attribute__((ms_abi)) foo(int a, int b) { return a+b; } 36 | )", Err); 37 | if (!CU) { 38 | std::cerr << "Error: " << Err << std::endl; 39 | return 1; 40 | } 41 | 42 | CU = Jit.cdef(R"( 43 | int __attribute__((ms_abi)) foo(int a, int b); 44 | )", nullptr, Err); 45 | if (!CU) { 46 | std::cerr << "Error: " << Err << std::endl; 47 | return 1; 48 | } 49 | 50 | auto F = CU.getFunction("foo"); 51 | if (!F) { 52 | std::cerr << "unable to get function foo!" << std::endl; 53 | return 1; 54 | } 55 | int Ret; 56 | int a = 1; 57 | int b = 10; 58 | void* Args[] = {&a, &b}; 59 | F.call(&Ret, Args); 60 | if (Ret != 11) { 61 | std::cerr << "invalid result!" << std::endl; 62 | return 1; 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /tests/includes.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/compile%exeext" "%S/includes" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main(int argc, char** argv) 23 | { 24 | DFFI::initialize(); 25 | 26 | { 27 | CCOpts Opts; 28 | Opts.OptLevel = 2; 29 | Opts.IncludeDirs.emplace_back(argv[1]); 30 | 31 | DFFI Jit(Opts); 32 | 33 | std::string Err; 34 | auto CU = Jit.compile("#include \"add.h\"", Err); 35 | if (!CU) { 36 | std::cerr << "Compile error: " << Err << std::endl; 37 | return 1; 38 | } 39 | int a = 2; 40 | int b = 4; 41 | int ret; 42 | void* Args_[] = {&a, &b}; 43 | CU.getFunction("add").call(&ret, &Args_[0]); 44 | if (ret != a+b) { 45 | std::cerr << "Invalid sum!" << std::endl; 46 | return 1; 47 | } 48 | } 49 | 50 | { 51 | CCOpts Opts; 52 | Opts.OptLevel = 2; 53 | DFFI Jit(Opts); 54 | 55 | std::string Err; 56 | auto CU = Jit.compile("#include \"add.h\"", Err); 57 | if (CU) { 58 | std::cerr << "This CU shouldn't compile!" << Err << std::endl; 59 | return 1; 60 | } 61 | } 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /tests/asm_redirect.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/asm_redirect%exeext" | "%FileCheck" "%s" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main(int argc, char** argv) 23 | { 24 | DFFI::initialize(); 25 | 26 | CCOpts Opts; 27 | Opts.OptLevel = 2; 28 | Opts.LazyJITWrappers = false; 29 | 30 | DFFI Jit(Opts); 31 | 32 | std::string Err; 33 | // This trick comes directly from stdio.h under Debian! Enjoy' :) 34 | auto CU = Jit.compile(R"( 35 | void myfunc() __asm__ ("" "redirected_myfunc"); 36 | void myfunc() { puts("toto"); } 37 | )", Err); 38 | if (!CU) { 39 | std::cerr << Err << std::endl; 40 | return 1; 41 | } 42 | 43 | // CHECK: toto 44 | CU.getFunction("myfunc").call(); 45 | // CHECK: toto 46 | CU.getFunction("redirected_myfunc").call(); 47 | 48 | CU = Jit.cdef(R"( 49 | void myfunc() __asm__ ("" "redirected_myfunc"); 50 | void myfunc(); 51 | )", nullptr, Err); 52 | if (!CU) { 53 | std::cerr << Err << std::endl; 54 | return 1; 55 | } 56 | 57 | // CHECK: toto 58 | CU.getFunction("myfunc").call(); 59 | // CHECK: toto 60 | CU.getFunction("redirected_myfunc").call(); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /tests/bool.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/bool%exeext" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main(int argc, char** argv) 23 | { 24 | DFFI::initialize(); 25 | 26 | CCOpts Opts; 27 | Opts.OptLevel = 2; 28 | 29 | DFFI Jit(Opts); 30 | 31 | std::string Err; 32 | auto CU = Jit.compile(R"( 33 | #include 34 | bool foo(int a) { return a == 1; } 35 | bool invert(bool v) { return !v; } 36 | )", Err); 37 | if (!CU) { 38 | std::cerr << "Compile error: " << Err << std::endl; 39 | return 1; 40 | } 41 | int a = 1; 42 | bool Ret = false; 43 | void* Args_[] = {&a}; 44 | CU.getFunction("foo").call(&Ret, &Args_[0]); 45 | if (Ret != true) { 46 | std::cerr << "Invalid bool!" << std::endl; 47 | return 1; 48 | } 49 | 50 | a = 0; 51 | CU.getFunction("foo").call(&Ret, &Args_[0]); 52 | if (Ret != false) { 53 | std::cerr << "Invalid bool!" << std::endl; 54 | return 1; 55 | } 56 | 57 | bool bA = true; 58 | Args_[0] = &bA; 59 | CU.getFunction("invert").call(&Ret, &Args_[0]); 60 | if (Ret != false) { 61 | std::cerr << "Error on invert!" << std::endl; 62 | return 1; 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /bindings/python/tests/test_structs_format.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Copyright 2018 Adrien Guinet 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import unittest 17 | import pydffi 18 | import struct 19 | import codecs 20 | 21 | from common import DFFITest 22 | 23 | class StructsFormatTest(DFFITest): 24 | def test_structsformat(self): 25 | FFI = pydffi.FFI() 26 | CU = FFI.cdef(''' 27 | #include 28 | typedef struct { 29 | bool a; 30 | unsigned int b; 31 | unsigned short c; 32 | } A; 33 | 34 | typedef struct { 35 | unsigned char buf[9]; 36 | unsigned short v0; 37 | A a; 38 | unsigned short v1; 39 | } B; 40 | ''') 41 | 42 | vA = CU.types.A(a=1,b=0xAAAAAAAA,c=0x4444) 43 | buf = pydffi.view_as_bytes(vA) 44 | vAup = struct.unpack(CU.types.A.format, buf) 45 | self.assertEqual(vAup, (1,0xAAAAAAAA,0x4444)) 46 | 47 | buf_ref = bytearray(b"012345678") 48 | vB = CU.types.B(v0=1,v1=2,a=vA,buf=pydffi.view_as(CU.types.B.buf.type, buf_ref)) 49 | buf = pydffi.view_as_bytes(vB) 50 | vBup = struct.unpack(CU.types.B.format, buf) 51 | self.assertEqual(bytearray(vBup[:9]), buf_ref) 52 | self.assertEqual(vBup[9:], (1,1,0xAAAAAAAA,0x4444,2)) 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | -------------------------------------------------------------------------------- /tests/anon_members.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/anon_members%exeext" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | typedef struct { 23 | char buf[7]; 24 | struct { 25 | int a; 26 | int b; 27 | struct { 28 | union { 29 | int c; 30 | float d; 31 | }; 32 | int e; 33 | }; 34 | }; 35 | int f; 36 | } A; 37 | 38 | int main() 39 | { 40 | DFFI::initialize(); 41 | 42 | CCOpts Opts; 43 | Opts.OptLevel = 2; 44 | 45 | DFFI Jit(Opts); 46 | 47 | std::string Err; 48 | auto CU = Jit.compile(R"( 49 | typedef struct { 50 | char buf[7]; 51 | struct { 52 | int a; 53 | int b; 54 | struct { 55 | union { 56 | int c; 57 | float d; 58 | }; 59 | int e; 60 | }; 61 | }; 62 | int f; 63 | } A; 64 | 65 | A foo() { 66 | A ret; 67 | ret.a = 1; 68 | ret.b = 2; 69 | ret.c = 4; 70 | ret.e = 5; 71 | ret.f = 6; 72 | return ret; 73 | } 74 | )", Err); 75 | if (!CU) { 76 | std::cerr << Err << std::endl; 77 | return 1; 78 | } 79 | 80 | A a; 81 | CU.getFunction("foo").call(&a, NULL); 82 | const bool Valid = (a.a == 1 && a.b == 2 && a.c == 4 && a.e == 5 && a.f == 6); 83 | return Valid?0:1; 84 | } 85 | -------------------------------------------------------------------------------- /third-party/pybind11/detail/typeid.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/detail/typeid.h: Compiler-independent access to type identifiers 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | #if defined(__GNUG__) 16 | # include 17 | #endif 18 | 19 | #include "common.h" 20 | 21 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 22 | PYBIND11_NAMESPACE_BEGIN(detail) 23 | 24 | /// Erase all occurrences of a substring 25 | inline void erase_all(std::string &string, const std::string &search) { 26 | for (size_t pos = 0;;) { 27 | pos = string.find(search, pos); 28 | if (pos == std::string::npos) { 29 | break; 30 | } 31 | string.erase(pos, search.length()); 32 | } 33 | } 34 | 35 | PYBIND11_NOINLINE void clean_type_id(std::string &name) { 36 | #if defined(__GNUG__) 37 | int status = 0; 38 | std::unique_ptr res{ 39 | abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free}; 40 | if (status == 0) { 41 | name = res.get(); 42 | } 43 | #else 44 | detail::erase_all(name, "class "); 45 | detail::erase_all(name, "struct "); 46 | detail::erase_all(name, "enum "); 47 | #endif 48 | detail::erase_all(name, "pybind11::"); 49 | } 50 | 51 | inline std::string clean_type_id(const char *typeid_name) { 52 | std::string name(typeid_name); 53 | detail::clean_type_id(name); 54 | return name; 55 | } 56 | 57 | PYBIND11_NAMESPACE_END(detail) 58 | 59 | /// Return a string representation of a C++ type 60 | template 61 | static std::string type_id() { 62 | return detail::clean_type_id(typeid(T).name()); 63 | } 64 | 65 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 66 | -------------------------------------------------------------------------------- /tests/lasterror.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/lasterror%exeext" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | static bool test(DFFI& FFI, bool UseLastError) 23 | { 24 | // Call open on a non existant file and check we have ENOENT as last error. 25 | std::string Err; 26 | auto CU = FFI.compile( 27 | #ifdef _WIN32 28 | R"( 29 | #include 30 | void seterrno(int val) { 31 | SetLastError(val); 32 | } 33 | )" 34 | #else 35 | R"( 36 | #include 37 | void seterrno(int val) { 38 | errno = val; 39 | } 40 | )" 41 | #endif 42 | , Err, UseLastError); 43 | if (!CU) { 44 | std::cerr << Err << std::endl; 45 | return 1; 46 | } 47 | 48 | NativeFunc::setLastError(0); 49 | int val = 10; 50 | void* Args[] = {&val}; 51 | CU.getFunction("seterrno").call(Args); 52 | // Simulate errno being modified 53 | errno = 0; 54 | const int lastError = NativeFunc::getLastError(); 55 | return lastError == 10; 56 | } 57 | 58 | int main(int argc, char** argv) 59 | { 60 | DFFI::initialize(); 61 | 62 | CCOpts Opts; 63 | Opts.OptLevel = 2; 64 | 65 | DFFI FFI(Opts); 66 | 67 | bool Ret = true; 68 | Ret &= test(FFI, true); 69 | Ret &= !test(FFI, false); 70 | return Ret?0:1; 71 | } 72 | -------------------------------------------------------------------------------- /third-party/cc-llvm.patch: -------------------------------------------------------------------------------- 1 | commit 218130c59167766f943c6b76f7ad3bb05a4dcf93 2 | Author: Adrien Guinet 3 | Date: Sun Jan 21 12:41:37 2018 +0100 4 | 5 | Add constructor DWARF CC for every supported LLVM CC 6 | 7 | They are in the "constructor" range defined by the DWARF standard. 8 | Moreover, add GCC's private calling conventions 9 | 10 | diff --git a/include/llvm/BinaryFormat/Dwarf.def b/include/llvm/BinaryFormat/Dwarf.def 11 | index 3df3300de46..1b233ec7f9e 100644 12 | --- a/include/llvm/BinaryFormat/Dwarf.def 13 | +++ b/include/llvm/BinaryFormat/Dwarf.def 14 | @@ -696,6 +696,7 @@ HANDLE_DW_CC(0x03, nocall) 15 | HANDLE_DW_CC(0x04, pass_by_reference) 16 | HANDLE_DW_CC(0x05, pass_by_value) 17 | // Vendor extensions: 18 | +HANDLE_DW_CC(0x40, GNU_renesas_sh) 19 | HANDLE_DW_CC(0x41, GNU_borland_fastcall_i386) 20 | HANDLE_DW_CC(0xb0, BORLAND_safecall) 21 | HANDLE_DW_CC(0xb1, BORLAND_stdcall) 22 | @@ -705,6 +706,22 @@ HANDLE_DW_CC(0xb4, BORLAND_msreturn) 23 | HANDLE_DW_CC(0xb5, BORLAND_thiscall) 24 | HANDLE_DW_CC(0xb6, BORLAND_fastcall) 25 | HANDLE_DW_CC(0xc0, LLVM_vectorcall) 26 | +HANDLE_DW_CC(0xc1, LLVM_Win64) 27 | +HANDLE_DW_CC(0xc2, LLVM_X86_64SysV) 28 | +HANDLE_DW_CC(0xc3, LLVM_AAPCS) 29 | +HANDLE_DW_CC(0xc4, LLVM_AAPCS_VFP) 30 | +HANDLE_DW_CC(0xc5, LLVM_IntelOclBicc) 31 | +HANDLE_DW_CC(0xc6, LLVM_SpirFunction) 32 | +HANDLE_DW_CC(0xc7, LLVM_OpenCLKernel) 33 | +HANDLE_DW_CC(0xc8, LLVM_Swift) 34 | +HANDLE_DW_CC(0xc9, LLVM_PreserveMost) 35 | +HANDLE_DW_CC(0xca, LLVM_PreserveAll) 36 | +HANDLE_DW_CC(0xcb, LLVM_X86RegCall) 37 | +// From GCC source code (include/dwarf2.h): This DW_CC_ value is not currently 38 | +// generated by any toolchain. It is used internally to GDB to indicate OpenCL C 39 | +// functions that have been compiled with the IBM XL C for OpenCL compiler and use 40 | +// a non-platform calling convention for passing OpenCL C vector types. 41 | +HANDLE_DW_CC(0xff, GDB_IBM_OpenCL) 42 | 43 | // Line Number Extended Opcode Encodings 44 | HANDLE_DW_LNE(0x01, end_sequence) 45 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Configure lit 16 | configure_file("lit.site.cfg.in" "${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg" ESCAPE_QUOTES @ONLY) 17 | 18 | if (BUILD_TESTS) 19 | set(TESTS 20 | anon_members 21 | anon_struct 22 | anon_union 23 | array 24 | asm_redirect 25 | attrs 26 | bool 27 | cconv 28 | compile 29 | compile_cxx 30 | compile_error 31 | decl 32 | decl_cxx 33 | dlopen 34 | enum 35 | func_ptr 36 | includes 37 | inline 38 | lasterror 39 | multiple_defs 40 | stdint 41 | struct 42 | system_headers 43 | typedef 44 | union 45 | varargs 46 | ) 47 | 48 | # Compile tests 49 | foreach(TEST ${TESTS}) 50 | add_executable(${TEST} ${TEST}.cpp) 51 | target_link_libraries(${TEST} dffi) 52 | endforeach() 53 | 54 | find_package(PythonInterp REQUIRED) 55 | set(_LIT_RUNNER "${CMAKE_CURRENT_SOURCE_DIR}/run_lit.py") 56 | set(LIT_RUNNER "${_LIT_RUNNER}" PARENT_SCOPE) 57 | 58 | # NOTE: added --max-time to timeout lit after specified seconds. 59 | # Default timeout is one week 60 | add_custom_target(check_lib 61 | COMMAND "${PYTHON_EXECUTABLE}" "${_LIT_RUNNER}" "${CMAKE_CURRENT_BINARY_DIR}" -v --max-time=900) 62 | 63 | add_dependencies(check_lib ${TESTS}) 64 | add_dependencies(check check_lib) 65 | set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE_SAVE}) 66 | endif() 67 | -------------------------------------------------------------------------------- /lib/dffi_impl_clang_res.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "dffi_impl.h" 16 | #include 17 | #include 18 | 19 | using namespace clang; 20 | using namespace llvm; 21 | 22 | // Implemented in clang_res.h included below 23 | static void initVFS(vfs::InMemoryFileSystem&); 24 | 25 | // Used by initVFS! 26 | static void addPath(vfs::InMemoryFileSystem& VFS, const char* Path, const uint8_t* Data, size_t Len) 27 | { 28 | assert(sys::path::is_relative(Path) && "path must be relative!"); 29 | SmallString<1024> FullPath; 30 | (StringRef{dffi::details::getClangResRootDirectory()} + "/" + Path).toStringRef(FullPath); 31 | VFS.addFile(FullPath, time(NULL), 32 | MemoryBuffer::getMemBuffer(StringRef{(const char*)Data, Len}, "", false)); 33 | } 34 | 35 | static IntrusiveRefCntPtr createClangResFileSystem() 36 | { 37 | auto* VFS = new vfs::InMemoryFileSystem{}; 38 | initVFS(*VFS); 39 | return IntrusiveRefCntPtr{VFS}; 40 | } 41 | 42 | // Data generated by cmake! 43 | #include 44 | 45 | // "Public" API 46 | IntrusiveRefCntPtr dffi::details::getClangResFileSystem() 47 | { 48 | static IntrusiveRefCntPtr FS = createClangResFileSystem(); 49 | return FS; 50 | } 51 | 52 | const char* dffi::details::getClangResRootDirectory() 53 | { 54 | return "/__clang_resources"; 55 | } 56 | -------------------------------------------------------------------------------- /bindings/python/tests/test_multiple_cu.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | import platform 18 | 19 | from common import DFFITest 20 | 21 | class MultipleCUTest(DFFITest): 22 | def test_mutiplecu(self): 23 | if platform.system() == "Windows": 24 | return 25 | 26 | J = self.FFI 27 | CU = J.cdef(''' 28 | struct A { 29 | short a; 30 | int b; 31 | }; 32 | 33 | short get_a(struct A a); 34 | int get_b(struct A b); 35 | ''', '/lib.h') 36 | 37 | CUA = J.compile(''' 38 | #include "/lib.h" 39 | short get_a(struct A a) { return a.a; } 40 | ''') 41 | 42 | CUB = J.compile(''' 43 | #include "/lib.h" 44 | int get_b(struct A a) { return a.b; } 45 | ''') 46 | 47 | # Explicitly delete J. Everything should still works! 48 | del J 49 | 50 | SA = CU.types.A 51 | A = CU.types.A(a=1,b=2) 52 | self.assertEqual(int(CU.funcs.get_a(A)), 1) 53 | self.assertEqual(int(CU.funcs.get_b(A)), 2) 54 | 55 | # TODO: assert struct A types are all equal between various CU. This is not the 56 | # case for now, and so these tests fail! 57 | #assert(CUA.getFunction("get_a").call(A).value == 1) 58 | #assert(CUB.getFunction("get_b").call(A).value == 2) 59 | 60 | if __name__ == '__main__': 61 | unittest.main() 62 | -------------------------------------------------------------------------------- /bindings/python/tests/test_mdarray.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | import struct 18 | 19 | from common import DFFITest 20 | 21 | class MDArrayTest(DFFITest): 22 | def test_mdarray(self): 23 | N = 10 24 | FFI = self.FFI 25 | ArTy = FFI.arrayType(FFI.DoubleTy, N) 26 | Ar = ArTy() 27 | for i in range(len(Ar)): 28 | Ar[i] = i 29 | Ref = [float(i) for i in range(len(Ar))] 30 | self.assertEqual(list(Ar), Ref) 31 | ArV = memoryview(Ar) 32 | self.assertEqual(ArV.format, "d") 33 | def upck_double(v): 34 | if isinstance(v,float): 35 | return v 36 | return struct.unpack("d",v)[0] 37 | self.assertTrue(all(upck_double(a) == b for a,b in zip(ArV,Ref))) 38 | 39 | Ar2Ty = FFI.arrayType(FFI.arrayType(FFI.DoubleTy, 10), 2) 40 | Ar = Ar2Ty() 41 | ArV = memoryview(Ar) 42 | self.assertEqual(ArV.format, "d") 43 | self.assertEqual(ArV.ndim, 2) 44 | self.assertEqual(ArV.shape, (2,10)) 45 | self.assertEqual(ArV.strides, (80,8)) 46 | 47 | try: 48 | import numpy as np 49 | except ImportError: 50 | return 51 | 52 | ar = np.ndarray(N) 53 | car = pydffi.view_as(ArTy, ar) 54 | self.assertTrue(all(a == b for a,b in zip(ar,car))) 55 | 56 | if __name__ == '__main__': 57 | unittest.main() 58 | -------------------------------------------------------------------------------- /include/dffi/cc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef DFFI_CC_H 16 | #define DFFI_CC_H 17 | 18 | namespace dffi { 19 | 20 | // This enum is mapped on clang's calling convention values (see 21 | // clang/Basic/Specifiers.h). Static asserts are made to ensure the 22 | // consistency! 23 | enum CallingConv { 24 | CC_C, // __attribute__((cdecl)) 25 | CC_X86StdCall, // __attribute__((stdcall)) 26 | CC_X86FastCall, // __attribute__((fastcall)) 27 | CC_X86ThisCall, // __attribute__((thiscall)) 28 | CC_X86VectorCall, // __attribute__((vectorcall)) 29 | CC_X86Pascal, // __attribute__((pascal)) 30 | CC_Win64, // __attribute__((ms_abi)) 31 | CC_X86_64SysV, // __attribute__((sysv_abi)) 32 | CC_X86RegCall, // __attribute__((regcall)) 33 | CC_AAPCS, // __attribute__((pcs("aapcs"))) 34 | CC_AAPCS_VFP, // __attribute__((pcs("aapcs-vfp"))) 35 | CC_IntelOclBicc, // __attribute__((intel_ocl_bicc)) 36 | CC_SpirFunction, // default for OpenCL functions on SPIR target 37 | CC_OpenCLKernel, // inferred for OpenCL kernels 38 | CC_Swift, // __attribute__((swiftcall)) 39 | CC_SwiftAsync, // __attribute__((swiftasynccall)) 40 | CC_PreserveMost, // __attribute__((preserve_most)) 41 | CC_PreserveAll, // __attribute__((preserve_all)) 42 | CC_AArch64VectorCall, // __attribute__((aarch64_vector_pcs)) 43 | }; 44 | 45 | const char* CCToClangAttribute(CallingConv CC); 46 | 47 | } // dffi 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /bindings/python/tests/test_func_ptr.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import pydffi 17 | 18 | from common import DFFITest 19 | 20 | class FuncPtrTest(DFFITest): 21 | def test_func_ptr(self): 22 | FFI = self.FFI 23 | CU = FFI.compile(''' 24 | typedef struct 25 | { 26 | int a; 27 | int b; 28 | int res; 29 | } Res; 30 | 31 | typedef Res(*op)(int,int); 32 | 33 | static Res get_res(int res, int a, int b) { 34 | Res Ret = {a,b,res}; 35 | return Ret; 36 | } 37 | 38 | static Res add(int a, int b) { 39 | return get_res(a+b,a,b); 40 | } 41 | static Res sub(int a, int b) { 42 | return get_res(a-b,a,b); 43 | } 44 | 45 | op get_op(unsigned Id) 46 | { 47 | if (Id == 0) return add; 48 | if (Id == 1) return sub; 49 | return 0; 50 | } 51 | 52 | Res call(op f, int a, int b) { 53 | return f(a,b); 54 | } 55 | ''') 56 | 57 | Add=CU.funcs.get_op(0) 58 | Add=Add.obj 59 | Res=Add(1,4) 60 | self.assertEqual(Res.res, 5) 61 | 62 | sub_addr = pydffi.ptr(CU.funcs.sub) 63 | Res=CU.funcs.call(sub_addr, 1, 5) 64 | self.assertEqual(Res.res, -4) 65 | 66 | subFuncTy = pydffi.typeof(CU.funcs.sub).type 67 | funcByAddr = subFuncTy(sub_addr) 68 | self.assertEqual(funcByAddr(1,5).res,-4) 69 | 70 | funcByAddr = subFuncTy(sub_addr.value) 71 | self.assertEqual(funcByAddr(1,5).res,-4) 72 | 73 | if __name__ == '__main__': 74 | unittest.main() 75 | -------------------------------------------------------------------------------- /tests/typedef.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/typedef%exeext" | "%FileCheck" "%s" 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | using namespace dffi; 23 | 24 | struct A { 25 | int a; 26 | int b; 27 | short c; 28 | double d; 29 | }; 30 | 31 | int main() 32 | { 33 | DFFI::initialize(); 34 | 35 | CCOpts Opts; 36 | Opts.OptLevel = 2; 37 | 38 | DFFI Jit(Opts); 39 | 40 | std::string Err; 41 | auto CU = Jit.compile(R"( 42 | #include 43 | typedef int MyInt; 44 | typedef short MyShort; 45 | 46 | struct A { 47 | MyInt a; 48 | MyInt b; 49 | MyShort c; 50 | double d; 51 | }; 52 | 53 | typedef struct A SA; 54 | 55 | void dump(SA a) 56 | { 57 | printf("a=%d, b=%d, c=%d, d=%f\n", a.a, a.b, a.c, a.d); 58 | } 59 | )", Err); 60 | if (!CU) { 61 | std::cerr << Err << std::endl; 62 | return 1; 63 | } 64 | 65 | A obj = {1,2,4,5.}; 66 | void* Args[] = {&obj}; 67 | // CHECK: a=1, b=2, c=4, d=5.000000 68 | CU.getFunction("dump").call(&Args[0]); 69 | 70 | auto const* STy = CU.getType("SA"); 71 | if (!STy) { 72 | std::cerr << "unable to find type 'SA'!" << std::endl; 73 | return 1; 74 | } 75 | // CHECK: a 76 | // CHECK: b 77 | // CHECK: c 78 | // CHECK: d 79 | for (auto const& F: static_cast(STy)->getOrgFields()) { 80 | std::cout << F.getName() << std::endl; 81 | } 82 | 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /tests/system_headers.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/system_headers%exeext" | "%FileCheck" "%s" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main(int argc, char** argv) 23 | { 24 | DFFI::initialize(); 25 | 26 | CCOpts Opts; 27 | Opts.OptLevel = 2; 28 | Opts.LazyJITWrappers = false; 29 | 30 | DFFI Jit(Opts); 31 | 32 | std::string Err; 33 | auto CU = Jit.compile(R"( 34 | #include 35 | void foo() { 36 | puts("hello world!"); 37 | } 38 | )", 39 | Err); 40 | 41 | if (!CU) { 42 | std::cerr << "Compile error: " << Err << std::endl; 43 | return 1; 44 | } 45 | 46 | // CHECK: hello world! 47 | CU.getFunction("foo").call(); 48 | 49 | CU = Jit.cdef("#include \n#include \n", nullptr, Err); 50 | if (!CU) { 51 | std::cerr << "Compile error: " << Err << std::endl; 52 | return 1; 53 | } 54 | 55 | const char* Msg = "hello puts!"; 56 | void* Args[] = {&Msg}; 57 | int Ret; 58 | // CHECK: hello puts! 59 | CU.getFunction("puts").call(&Ret, Args); 60 | if (Ret < 0) { 61 | std::cerr << "error while calling puts!" << std::endl; 62 | return 1; 63 | } 64 | 65 | const char* Int = "10"; 66 | Args[0] = ∬ 67 | Ret = -1; 68 | CU.getFunction("atoi").call(&Ret, Args); 69 | if (Ret != 10) { 70 | std::cerr << "error while calling atoi!" << std::endl; 71 | return 1; 72 | } 73 | 74 | 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /CMakeClangRes.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | file(GLOB_RECURSE CLANG_RES_GLOB LIST_DIRECTORIES false RELATIVE "${CLANG_RES_DIR}" "${CLANG_RES_DIR}/*") 16 | set(VFS_INIT "") 17 | set(FILE_IDX 0) 18 | file(WRITE "${CLANG_RES_HEADER}" "") 19 | set(RES_BLACKLIST 20 | "riscv_vector.h" 21 | "opencl-c.h" 22 | "opencl-c-base.h" 23 | "altivec.h" 24 | "hexagon_protos.h" 25 | "hvx_hexagon_protos.h" 26 | "hexagon_types.h" 27 | "wasm_simd128.h" 28 | ) 29 | set(DIR_BLACKLIST 30 | "cuda_wrappers" 31 | "ppc_wrappers" 32 | "profile" 33 | "sanitizer" 34 | "xray" 35 | ) 36 | foreach(RES_NAME ${CLANG_RES_GLOB}) 37 | # Is blacklisted? 38 | list(FIND RES_BLACKLIST "${RES_NAME}" IS_BLACKLISTED) 39 | if (NOT IS_BLACKLISTED EQUAL -1) 40 | continue() 41 | endif() 42 | get_filename_component(RES_DIR "${RES_NAME}" DIRECTORY) 43 | list(FIND DIR_BLACKLIST "${RES_DIR}" IS_BLACKLISTED) 44 | if (NOT IS_BLACKLISTED EQUAL -1) 45 | continue() 46 | endif() 47 | 48 | set(RES "${CLANG_RES_DIR}/${RES_NAME}") 49 | file(READ "${RES}" RES_DATA HEX) 50 | string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," RES_DATA_ARRAY "${RES_DATA}") 51 | set(VAR_NAME "__file_${FILE_IDX}") 52 | file(APPEND "${CLANG_RES_HEADER}" "static const uint8_t ${VAR_NAME}[] = { ${RES_DATA_ARRAY} 0x00 };\n") 53 | set(VFS_INIT "${VFS_INIT}addPath(VFS, \"include/${RES_NAME}\", ${VAR_NAME}, sizeof(${VAR_NAME})-1);\n") 54 | MATH(EXPR FILE_IDX "${FILE_IDX}+1") 55 | endforeach() 56 | file(APPEND "${CLANG_RES_HEADER}" "void initVFS(llvm::vfs::InMemoryFileSystem& VFS) {\n ${VFS_INIT} \n}") 57 | -------------------------------------------------------------------------------- /lib/dffi_llvm_wrapper.cpp: -------------------------------------------------------------------------------- 1 | // This file need to be in an external CU, because it uses Clang/LLVM 2 | // structures, which are potentially compiled w/o RTTI. In such a case, this 3 | // file is compiled w/o rtti, in order not to link with inexistant typeinfo 4 | // structures. 5 | // This is kind of a hack waiting for making pybind11 RTTI-free. 6 | 7 | #include 8 | #include 9 | #include 10 | #include "dffi_impl.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace dffi; 17 | 18 | namespace { 19 | 20 | std::unique_ptr isolateFunc(llvm::Function* F, const char* FuncName) 21 | { 22 | assert(F && "LLVM wrapper should exist!"); 23 | std::unique_ptr Ret(new llvm::Module{"", F->getContext()}); 24 | Ret->setTargetTriple(F->getParent()->getTargetTriple()); 25 | Ret->setDataLayout(F->getParent()->getDataLayout()); 26 | auto* NewF = llvm::Function::Create(F->getFunctionType(), F->getLinkage(), FuncName, Ret.get()); 27 | llvm::ValueToValueMapTy VMap; 28 | auto NewFArgsIt = NewF->arg_begin(); 29 | auto FArgsIt = F->arg_begin(); 30 | for (auto FArgsEnd = F->arg_end(); FArgsIt != FArgsEnd; ++NewFArgsIt, ++FArgsIt) { 31 | VMap[&*FArgsIt] = &*NewFArgsIt; 32 | } 33 | llvm::SmallVector Returns; 34 | llvm::CloneFunctionInto(NewF, F, VMap, llvm::CloneFunctionChangeType::GlobalChanges, Returns); 35 | return Ret; 36 | } 37 | 38 | } // anonymous 39 | 40 | std::string FunctionType::getWrapperLLVM(const char* FuncName) const 41 | { 42 | llvm::Function* F = getDFFI().getWrapperLLVMFunc(this, llvm::None); 43 | auto M = isolateFunc(F, FuncName); 44 | std::string Ret; 45 | llvm::raw_string_ostream ss(Ret); 46 | llvm::WriteBitcodeToFile(*M, ss); 47 | return Ret; 48 | } 49 | 50 | std::string FunctionType::getWrapperLLVMStr(const char* FuncName) const 51 | { 52 | llvm::Function* F = getDFFI().getWrapperLLVMFunc(this, llvm::None); 53 | auto M = isolateFunc(F, FuncName); 54 | std::string Ret; 55 | llvm::raw_string_ostream ss(Ret); 56 | ss << *M; 57 | return Ret; 58 | } 59 | -------------------------------------------------------------------------------- /include/dffi/native_func.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef DFFI_NATIVE_FUNC_H 16 | #define DFFI_NATIVE_FUNC_H 17 | 18 | #include 19 | #ifdef _WIN32 20 | #include // For DWORD 21 | #endif 22 | 23 | namespace dffi { 24 | 25 | class FunctionType; 26 | class Type; 27 | 28 | namespace details { 29 | struct DFFIImpl; 30 | } // details 31 | 32 | struct DFFI_API NativeFunc 33 | { 34 | typedef void(*TrampPtrTy)(void*, void*, void**); 35 | 36 | #ifdef _WIN32 37 | using LastErrorTy = DWORD; 38 | #else 39 | using LastErrorTy = int; 40 | #endif 41 | 42 | NativeFunc(); 43 | 44 | NativeFunc(NativeFunc&&) = default; 45 | NativeFunc(NativeFunc const&) = default; 46 | 47 | void call(void* Ret, void** Args) const; 48 | void call(void** Args) const; 49 | void call() const; 50 | 51 | TrampPtrTy getTrampPtr() const { return TrampFuncPtr_; } 52 | void* getFuncCodePtr() const { return FuncCodePtr_; } 53 | // TODO! 54 | //size_t getFuncCodeSize() const; 55 | 56 | operator bool() const; 57 | 58 | dffi::FunctionType const* getType() const { return FTy_; } 59 | dffi::Type const* getReturnType() const; 60 | 61 | static LastErrorTy getLastError(); 62 | static void setLastError(LastErrorTy Err); 63 | 64 | protected: 65 | friend struct details::DFFIImpl; 66 | 67 | NativeFunc(TrampPtrTy Ptr, void* CodePtr, dffi::FunctionType const* FTy); 68 | 69 | private: 70 | static void swapLastError(); 71 | 72 | TrampPtrTy TrampFuncPtr_; 73 | void* FuncCodePtr_; 74 | dffi::FunctionType const* FTy_; 75 | }; 76 | 77 | } // dffi 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /tests/struct.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/struct%exeext" | "%FileCheck" "%s" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | typedef struct { 23 | int a; 24 | int b; 25 | short c; 26 | double d; 27 | } A; 28 | 29 | int main() 30 | { 31 | DFFI::initialize(); 32 | 33 | CCOpts Opts; 34 | Opts.OptLevel = 2; 35 | 36 | DFFI Jit(Opts); 37 | 38 | std::string Err; 39 | auto CU = Jit.compile(R"( 40 | struct A { 41 | int a; 42 | int b; 43 | short c; 44 | double d; 45 | }; 46 | 47 | void foo2(struct A* a); 48 | 49 | void foo(struct A a) { 50 | foo2(&a); 51 | } 52 | 53 | void foo2(struct A* a) { 54 | printf("a=%d, b=%d, c=%d, d=%f\n", a->a, a->b, a->c, a->d); 55 | a->a = 2; 56 | } 57 | 58 | void set(struct A* a) { 59 | a->d = 4.; 60 | } 61 | )", Err); 62 | if (!CU) { 63 | std::cerr << Err << std::endl; 64 | return 1; 65 | } 66 | 67 | A obj = {1,2,4,5.}; 68 | void* Args[] = {&obj}; 69 | // CHECK: a=1, b=2, c=4, d=5.000000 70 | CU.getFunction("foo").call(&Args[0]); 71 | if (obj.a != 1) { 72 | std::cerr << "invalid value for a!" << std::endl; 73 | return 1; 74 | } 75 | A* pObj = &obj; 76 | Args[0] = &pObj; 77 | // CHECK: a=1, b=2, c=4, d=5.000000 78 | CU.getFunction("foo2").call(&Args[0]); 79 | if (obj.a != 2) { 80 | std::cerr << "invalid value for a!" << std::endl; 81 | return 1; 82 | } 83 | CU.getFunction("set").call(&Args[0]); 84 | if (obj.d != 4.) { 85 | std::cerr << "invalid value for d!" << std::endl; 86 | return 1; 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /tests/anon_struct.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/anon_struct%exeext" | "%FileCheck" "%s" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | typedef struct { 23 | char a; 24 | struct { 25 | int a; 26 | int b; 27 | } s; 28 | } A; 29 | 30 | int main() 31 | { 32 | DFFI::initialize(); 33 | 34 | CCOpts Opts; 35 | Opts.OptLevel = 2; 36 | Opts.LazyJITWrappers = false; 37 | 38 | DFFI Jit(Opts); 39 | 40 | std::string Err; 41 | auto CU = Jit.compile(R"( 42 | typedef struct { 43 | char a; 44 | struct { 45 | int a; 46 | int b; 47 | } s; 48 | } A; 49 | 50 | void dump(A a) { 51 | printf("s.a=%d, s.b=%d\n", a.s.a, a.s.b); 52 | } 53 | )", Err); 54 | if (!CU) { 55 | std::cerr << Err << std::endl; 56 | return 1; 57 | } 58 | 59 | A a; 60 | a.s.a = 1; a.s.b = 2; 61 | void* Args[] = { &a }; 62 | // CHECK: s.a=1, s.b=2 63 | CU.getFunction("dump").call(&Args[0]); 64 | 65 | // TODO: emit error in this case! 66 | #if 0 67 | CU = Jit.compile(R"( 68 | void dump_anon(struct { int a; int b; } s) { 69 | printf("a=%d, b=%d\n", s.a, s.b); 70 | } 71 | )", Err); 72 | if (!CU) { 73 | std::cerr << Err << std::endl; 74 | return 1; 75 | } 76 | Args[0] = &a.s; 77 | // IGNCHECK: a=1, b=2 78 | CU.getFunction("dump_anon").call(&Args[0]); 79 | 80 | CU = Jit.cdef(R"( 81 | void dump_anon(struct { int a; int b; } s); 82 | )", "api.h", Err); 83 | if (!CU) { 84 | std::cerr << Err << std::endl; 85 | return 1; 86 | } 87 | // IGNCHECK: a=1, b=2 88 | CU.getFunction("dump_anon").call(&Args[0]); 89 | #endif 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /bindings/python/tests/test_buffers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | # Copyright 2018 Adrien Guinet 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import random 17 | import unittest 18 | 19 | import pydffi 20 | from common import DFFITest 21 | 22 | class BuffersTest(DFFITest): 23 | def test_buffers(self): 24 | CU = self.FFI.compile(''' 25 | #include 26 | #include 27 | 28 | void print(const char* msg) { 29 | puts(msg); 30 | } 31 | void print_u8(uint8_t const* buf, size_t len) { 32 | for (size_t i = 0; i < len; ++i) { 33 | printf("%02X ", buf[i]); 34 | } 35 | printf("\\n"); 36 | } 37 | 38 | void bytesupper(uint8_t* S) { 39 | const size_t Len = strlen(S); 40 | printf("%lu\\n", Len); 41 | for (size_t i = 0; i < Len; ++i) { 42 | S[i] = toupper(S[i]); 43 | } 44 | } 45 | 46 | void strupper(char* S) { 47 | bytesupper(S); 48 | } 49 | ''') 50 | print_ = getattr(CU.funcs, "print") 51 | print_u8 = CU.funcs.print_u8 52 | bytesupper = CU.funcs.bytesupper 53 | strupper = CU.funcs.strupper 54 | 55 | # CHECK: coucou 56 | print_("coucou") 57 | # CHECK: héllo 58 | print_("héllo") 59 | 60 | buf = u"héllo".encode("utf8") 61 | # CHECK: 68 C3 A9 6C 6C 6F 62 | print_u8(buf, len(buf)) 63 | 64 | buf = bytearray(b"hello") 65 | bytesupper(buf) 66 | self.assertEqual(buf, b"HELLO") 67 | 68 | buf = bytearray(b"hello") 69 | buf_char = pydffi.view_as(self.FFI.arrayType(self.FFI.UInt8Ty, len(buf)), buf) 70 | strupper(pydffi.cast(pydffi.ptr(buf_char),self.FFI.CharPtrTy)) 71 | self.assertEqual(buf, b"HELLO") 72 | 73 | if __name__ == '__main__': 74 | unittest.main() 75 | -------------------------------------------------------------------------------- /lib/types_printer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef DFFI_TYPE_PRINTER_H 16 | #define DFFI_TYPE_PRINTER_H 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | using dffi::cast; 31 | using dffi::dyn_cast; 32 | using dffi::dyn_cast_or_null; 33 | using dffi::isa; 34 | 35 | namespace dffi { 36 | 37 | struct TypePrinter 38 | { 39 | enum DeclMode 40 | { 41 | Full, 42 | Forward, 43 | None 44 | }; 45 | 46 | TypePrinter(bool ViewEnumAsBasicType = true): 47 | Decls_(DeclsStr_), 48 | ViewEnumAsBasicType_(ViewEnumAsBasicType) 49 | { } 50 | 51 | // Print the type definition. 52 | llvm::raw_ostream& print_def(llvm::raw_ostream& OS, dffi::Type const* Ty, DeclMode DMode, const char* Name = nullptr); 53 | 54 | 55 | std::string& getDecls() { return Decls_.str(); } 56 | 57 | private: 58 | void add_decl(dffi::Type const* Ty); 59 | void add_forward_decl(dffi::Type const* Ty); 60 | 61 | 62 | private: 63 | void print_decl_impl(llvm::raw_string_ostream& ss, dffi::CompositeType const* Ty); 64 | void print_decl_impl(llvm::raw_string_ostream& ss, dffi::EnumType const* Ty); 65 | 66 | 67 | private: 68 | llvm::DenseMap NamedTys_; 69 | llvm::DenseSet Declared_; 70 | llvm::DenseSet ForwardDeclared_; 71 | std::string DeclsStr_; 72 | llvm::raw_string_ostream Decls_; 73 | bool ViewEnumAsBasicType_; 74 | }; 75 | 76 | } // dffi 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /tests/union.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/union%exeext" >"%t" 16 | // RUN: "%FileCheck" "%s" <"%t" 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace dffi; 23 | 24 | typedef union { 25 | int a; 26 | int b; 27 | short c; 28 | double d; 29 | } A; 30 | 31 | int main() 32 | { 33 | DFFI::initialize(); 34 | 35 | CCOpts Opts; 36 | Opts.OptLevel = 2; 37 | 38 | DFFI Jit(Opts); 39 | 40 | std::string Err; 41 | auto CU = Jit.compile(R"( 42 | #include 43 | union A { 44 | int a; 45 | int b; 46 | short c; 47 | double d; 48 | }; 49 | 50 | void dump(union A* a) { 51 | printf("a=%d, b=%d, c=%d\n", a->a, a->b, a->c); 52 | } 53 | 54 | void dump_value(union A a) { 55 | printf("a=%d, b=%d, c=%d\n", a.a, a.b, a.c); 56 | } 57 | 58 | void set(union A* a) { 59 | a->d = 4.; 60 | } 61 | )", Err); 62 | if (!CU) { 63 | fprintf(stderr, "%s\n", Err.c_str()); 64 | return 1; 65 | } 66 | 67 | A obj; 68 | obj.a = 1; 69 | void* Args[] = {&obj}; 70 | // CHECK: a=1, b=1, c=1 71 | CU.getFunction("dump_value").call(&Args[0]); 72 | A* pObj = &obj; 73 | Args[0] = &pObj; 74 | // CHECK: a=1, b=1, c=1 75 | CU.getFunction("dump").call(&Args[0]); 76 | CU.getFunction("set").call(&Args[0]); 77 | if (obj.d != 4.) { 78 | fprintf(stderr, "invalid value for d!\n", Err.c_str()); 79 | return 1; 80 | } 81 | 82 | auto* UTy = CU.getUnionType("A"); 83 | auto const& Fields = UTy->getOrgFields(); 84 | // CHECK: a 85 | // CHECK: b 86 | // CHECK: c 87 | // CHECK: d 88 | for (auto const& F: Fields) { 89 | printf("%s\n", F.getName()); 90 | } 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /bindings/python/tests/test_array.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pydffi 16 | import sys 17 | import struct 18 | import unittest 19 | 20 | from common import DFFITest 21 | 22 | class ArrayTest(DFFITest): 23 | def test_array(self): 24 | CU=self.FFI.compile(''' 25 | #include 26 | #include 27 | #include 28 | 29 | struct A 30 | { 31 | char buf[128]; 32 | }; 33 | 34 | struct A init() { 35 | struct A a; 36 | strcpy(a.buf, "hello"); 37 | return a; 38 | } 39 | 40 | bool verify(struct A const* v, const char* ref) { 41 | return strcmp(v->buf, ref) == 0; 42 | } 43 | ''') 44 | A = CU.funcs.init() 45 | Buf = A.buf 46 | self.assertEqual(Buf.get(0), "h") 47 | Buf.set(0, 'H') 48 | self.assertEqual(Buf.get(0), "H") 49 | self.assertTrue(CU.funcs.verify(pydffi.ptr(A), b"Hello")) 50 | 51 | m = pydffi.view_as_bytes(Buf) 52 | v = ord("Z") if sys.version_info >= (3, 0) else struct.pack("B", ord("Z")) 53 | m[0] = v 54 | self.assertTrue(CU.funcs.verify(pydffi.ptr(A), b"Zello")) 55 | 56 | UIntTy = self.FFI.basicType(pydffi.BasicKind.UInt) 57 | N = 10 58 | ArrTy = self.FFI.arrayType(UIntTy, N) 59 | Arr=pydffi.CArrayObj(ArrTy) 60 | for i in range(N): 61 | Arr.set(i, i) 62 | for i in range(N): 63 | self.assertEqual(Arr.get(i), i) 64 | 65 | # TOFIX! 66 | #m = pydffi.view_as_bytes(Arr) 67 | #for i in range(N): 68 | # v = m[i] 69 | # if sys.version_info[0] < 3: 70 | # v = struct.unpack("I", v)[0] 71 | # assert(v == i) 72 | 73 | if __name__ == '__main__': 74 | unittest.main() 75 | -------------------------------------------------------------------------------- /bindings/python/tests/test_cast.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import random 16 | import unittest 17 | 18 | import pydffi 19 | from common import DFFITest 20 | 21 | class CastTest(DFFITest): 22 | def test_cast(self): 23 | FFI = self.FFI 24 | # Integer casts 25 | for Ty in (FFI.UInt8, FFI.UInt16, FFI.UInt32, FFI.UInt64): 26 | v = random.getrandbits(pydffi.sizeof(Ty(0))*8) 27 | V = Ty(v) 28 | for CTy in (FFI.UInt8Ty, FFI.UInt16Ty, FFI.UInt32Ty, FFI.UInt64Ty): 29 | VC = pydffi.cast(V,CTy) 30 | self.assertEqual(VC.value, v & (2**(CTy.size*8)-1)) 31 | 32 | # Array/pointer casts 33 | CU = FFI.compile(''' 34 | #include 35 | #include 36 | 37 | typedef struct { 38 | char buf[256]; 39 | } A; 40 | 41 | bool verify(const char* msg, const char* ref) { 42 | return strcmp(msg, ref) == 0; 43 | } 44 | 45 | bool verify_struct(A const* a, const char* ref) { 46 | return strcmp(a->buf, ref) == 0; 47 | } 48 | ''') 49 | verify_struct = CU.funcs.verify_struct 50 | 51 | SA = CU.types.A 52 | A = pydffi.CStructObj(SA) 53 | mem = pydffi.view_as_bytes(A) 54 | b = b"hello!\x00" 55 | mem[:len(b)] = b 56 | 57 | self.assertTrue(verify_struct(pydffi.ptr(A), b"hello!")) 58 | 59 | verify = getattr(CU.funcs, "verify") 60 | buf = A.buf 61 | buf = pydffi.cast(pydffi.ptr(buf),FFI.Int8PtrTy) 62 | self.assertTrue(verify(buf, b"hello!")) 63 | 64 | # Cast back 65 | buf = pydffi.cast(buf, FFI.pointerType(SA)) 66 | self.assertTrue(verify_struct(buf, b"hello!")) 67 | 68 | if __name__ == '__main__': 69 | unittest.main() 70 | -------------------------------------------------------------------------------- /third-party/pybind11/complex.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/complex.h: Complex number support 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | 14 | #include 15 | 16 | /// glibc defines I as a macro which breaks things, e.g., boost template names 17 | #ifdef I 18 | # undef I 19 | #endif 20 | 21 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 22 | 23 | template 24 | struct format_descriptor, detail::enable_if_t::value>> { 25 | static constexpr const char c = format_descriptor::c; 26 | static constexpr const char value[3] = {'Z', c, '\0'}; 27 | static std::string format() { return std::string(value); } 28 | }; 29 | 30 | #ifndef PYBIND11_CPP17 31 | 32 | template 33 | constexpr const char 34 | format_descriptor, 35 | detail::enable_if_t::value>>::value[3]; 36 | 37 | #endif 38 | 39 | PYBIND11_NAMESPACE_BEGIN(detail) 40 | 41 | template 42 | struct is_fmt_numeric, detail::enable_if_t::value>> { 43 | static constexpr bool value = true; 44 | static constexpr int index = is_fmt_numeric::index + 3; 45 | }; 46 | 47 | template 48 | class type_caster> { 49 | public: 50 | bool load(handle src, bool convert) { 51 | if (!src) { 52 | return false; 53 | } 54 | if (!convert && !PyComplex_Check(src.ptr())) { 55 | return false; 56 | } 57 | Py_complex result = PyComplex_AsCComplex(src.ptr()); 58 | if (result.real == -1.0 && PyErr_Occurred()) { 59 | PyErr_Clear(); 60 | return false; 61 | } 62 | value = std::complex((T) result.real, (T) result.imag); 63 | return true; 64 | } 65 | 66 | static handle 67 | cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { 68 | return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); 69 | } 70 | 71 | PYBIND11_TYPE_CASTER(std::complex, const_name("complex")); 72 | }; 73 | PYBIND11_NAMESPACE_END(detail) 74 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 75 | -------------------------------------------------------------------------------- /examples/lit.cfg: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import lit 16 | import os 17 | import platform 18 | import subprocess 19 | import ctypes.util 20 | 21 | config.name = "dragonffi-examples" 22 | config.test_source_root = os.path.dirname(__file__) 23 | config.suffixes = ['.py'] 24 | config.test_format = lit.formats.ShTest(True) 25 | 26 | def get_filecheck(): 27 | ret = os.path.join(config.llvm_bindir,"FileCheck" + config.exe_suffix) 28 | if os.path.exists(ret): return ret 29 | ret = os.path.join(config.llvm_bindir,"../libexec/llvm/FileCheck" + config.exe_suffix) 30 | if os.path.exists(ret): return ret 31 | raise RuntimeError("unable to find FileCheck!") 32 | 33 | # substitutions 34 | config.substitutions.append(("%FileCheck",get_filecheck())) 35 | config.substitutions.append(("%python",config.python_bin)) 36 | config.environment["PYTHONPATH"] = os.getenv("PYDFFI_DIR") 37 | 38 | def check_lib(name, feature): 39 | if ctypes.util.find_library(name) is not None: 40 | config.available_features.add(feature) 41 | 42 | check_lib("archive", "libarchive") 43 | check_lib("fftw3", "libfftw3") 44 | 45 | if platform.system() == "Darwin": 46 | config.available_features.add("darwin") 47 | # Get the sysroot thanks to xcrun and set the DFFI_SYSROOT environment variable 48 | sysroot = subprocess.check_output(["xcrun", "--show-sdk-path"]).strip() 49 | config.environment["DFFI_SYSROOT"] = sysroot 50 | if platform.system() == "Linux": 51 | config.available_features.add("linux") 52 | if platform.system() in ("Darwin","Linux"): 53 | config.available_features.add("posix") 54 | 55 | if platform.system() == "Windows": 56 | # Add the current build directory to the path, so that executables can load 57 | # dffi.dll (no rpath equivalent on windows) 58 | config.environment['PATH'] += ";" + config.dffi_lib_dir.replace("/","\\") 59 | -------------------------------------------------------------------------------- /bindings/python/tests/test_structs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pydffi 16 | import struct 17 | import sys 18 | import unittest 19 | 20 | from common import DFFITest 21 | 22 | class StructsTest(DFFITest): 23 | def test_structs(self): 24 | FFI=self.FFI 25 | CU = FFI.compile(''' 26 | struct A 27 | { 28 | unsigned char a; 29 | short b; 30 | }; 31 | 32 | int check(struct A a, unsigned char aref, short bref) { 33 | return (a.a == aref) && (a.b == bref); 34 | } 35 | int checkptr(struct A* a, unsigned char aref, short bref) { 36 | return (a->a == aref) && (a->b == bref); 37 | } 38 | void set(struct A* a) { 39 | a->a = 59; 40 | a->b = 1111; 41 | } 42 | struct A init() { 43 | struct A ret; 44 | ret.a = 44; 45 | ret.b = 5555; 46 | return ret; 47 | } 48 | ''') 49 | A = CU.types.A 50 | fields_name = sorted((f.name for f in A)) 51 | self.assertEqual(fields_name[0], 'a') 52 | self.assertEqual(fields_name[1], 'b') 53 | 54 | Av = CU.types.A(a=1,b=2) 55 | 56 | # Direct data access throught a memoryview 57 | mv = pydffi.view_as_bytes(Av) 58 | 59 | # Set a throught mv 60 | 61 | v = 5 if sys.version_info >= (3, 0) else struct.pack("B", 5) 62 | mv[0] = v 63 | self.assertEqual(Av.a, 5) 64 | self.assertEqual(Av.b, 2) 65 | self.assertTrue(getattr(CU.funcs, "check")(Av, 5, 2)) 66 | 67 | pAv = pydffi.ptr(Av) 68 | self.assertTrue(CU.funcs.checkptr(pAv, 5, 2)) 69 | CU.funcs.set(pAv) 70 | self.assertTrue(getattr(CU.funcs, "check")(Av, 59, 1111)) 71 | self.assertEqual(Av.a, 59) 72 | self.assertEqual(Av.b, 1111) 73 | 74 | Av = CU.funcs.init() 75 | self.assertEqual(Av.a, 44) 76 | self.assertEqual(Av.b, 5555) 77 | 78 | if __name__ == '__main__': 79 | unittest.main() 80 | -------------------------------------------------------------------------------- /lib/dffictx.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "dffictx.h" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | details::DFFICtx::DFFICtx() 23 | { } 24 | 25 | details::DFFICtx::~DFFICtx() 26 | { 27 | for (FunctionType* FTy: FunctionTys_) { 28 | delete FTy; 29 | } 30 | } 31 | 32 | BasicType* details::DFFICtx::getBasicType(DFFIImpl& Dffi, BasicType::BasicKind Kind) 33 | { 34 | auto It = BasicTys_.emplace(std::piecewise_construct, 35 | std::forward_as_tuple(Kind), 36 | std::forward_as_tuple(BasicType{Dffi, Kind})); 37 | return &It.first->second; 38 | } 39 | 40 | PointerType* details::DFFICtx::getPtrType(DFFIImpl& Dffi, QualType Pointee) 41 | { 42 | auto It = PointerTys_.find(Pointee); 43 | if (It != PointerTys_.end()) { 44 | return It->second.get(); 45 | } 46 | std::unique_ptr Obj(new PointerType{Dffi, Pointee}); 47 | auto* Ret = Obj.get(); 48 | PointerTys_[Pointee] = std::move(Obj); 49 | return Ret; 50 | } 51 | 52 | ArrayType* details::DFFICtx::getArrayType(DFFIImpl& Dffi, QualType EltTy, uint64_t NElements) 53 | { 54 | ArrayTypeKeyInfo::KeyTy K(EltTy, NElements); 55 | auto It = ArrayTys_.find_as(K); 56 | if (It != ArrayTys_.end()) { 57 | return *It; 58 | } 59 | auto* Ret = new ArrayType{Dffi, EltTy, NElements}; 60 | ArrayTys_.insert(Ret); 61 | return Ret; 62 | } 63 | 64 | FunctionType* details::DFFICtx::getFunctionType(DFFIImpl& Dffi, QualType RetTy, llvm::ArrayRef ParamsTy, CallingConv CC, bool VarArgs, bool UseLastError) 65 | { 66 | FunctionTypeKeyInfo::KeyTy K(RetTy, ParamsTy, CC, VarArgs, UseLastError); 67 | auto It = FunctionTys_.find_as(K); 68 | if (It != FunctionTys_.end()) { 69 | return *It; 70 | } 71 | auto* Ret = new FunctionType{Dffi, RetTy, ParamsTy, CC, VarArgs, UseLastError}; 72 | FunctionTys_.insert(Ret); 73 | return Ret; 74 | } 75 | -------------------------------------------------------------------------------- /bindings/python/pydffi/purectypes_generator/generator.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | import pydffi 3 | from purectypes.types import BasicTy,ArrayTy,StructTy,PointerTy,Field,EnumTy,UnionTy,FunctionTy,VoidTy 4 | 5 | _Generating = object() 6 | 7 | _ty_mapping = {pydffi.StructType: StructTy, pydffi.UnionType: UnionTy, pydffi.ArrayType: ArrayTy} 8 | 9 | class GenPureCType: 10 | def __init__(self): 11 | self._types = {} 12 | self._triple = pydffi.native_triple() 13 | 14 | def __call__(self, Ty): 15 | if isinstance(Ty, pydffi.QualType): 16 | return self(Ty.type) 17 | ret = self._types.get(Ty, None) 18 | if not ret is None: 19 | return ret 20 | 21 | # This would allow an early termination of the recursion for recursive 22 | # types. 23 | if isinstance(Ty, (pydffi.StructType, pydffi.UnionType, pydffi.ArrayType)): 24 | ret = _ty_mapping[type(Ty)]() 25 | self._types[Ty] = ret 26 | 27 | try: 28 | name = next(Ty.names) 29 | except StopIteration: name = None 30 | 31 | if Ty is None: 32 | # void 33 | name = "VoidTy" 34 | ret = VoidTy() 35 | self._types[None] = ret 36 | return ret 37 | if isinstance(Ty, pydffi.BasicType): 38 | ret = BasicTy(Ty.portable_format) 39 | name = "__" + str(Ty.kind).split(".")[1] + "Ty" 40 | elif isinstance(Ty, pydffi.StructType): 41 | fields = {} 42 | for f in Ty: 43 | fields[f.name] = Field(offset=f.offset, type_=self(f.type)) 44 | ret._fields = fields 45 | elif isinstance(Ty, pydffi.ArrayType): 46 | ret._elt_type = self(Ty.elementType()) 47 | ret._elt_count = Ty.count() 48 | elif isinstance(Ty, pydffi.PointerType): 49 | ret = PointerTy(self(Ty.pointee()), Ty.portable_format) 50 | elif isinstance(Ty, pydffi.EnumType): 51 | ret = EnumTy(pydffi.portable_format(Ty), Enum(name, dict(iter(Ty)))) 52 | elif isinstance(Ty, pydffi.UnionType): 53 | ret._types = {t.name: self(t.type) for t in Ty} 54 | elif isinstance(Ty, pydffi.FunctionType): 55 | ret = FunctionTy(self(Ty.returnType), tuple(self(a) for a in Ty.params), Ty.varArgs) 56 | else: 57 | raise ValueError("unsupported type %s" % repr(Ty)) 58 | ret._triple = self._triple 59 | ret._size = Ty.size 60 | ret._align = Ty.align 61 | ret._name = name 62 | self._types[Ty] = ret 63 | return ret 64 | -------------------------------------------------------------------------------- /tests/lit.cfg: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import lit 16 | import os 17 | import platform 18 | import subprocess 19 | 20 | config.name = "dragonffi" 21 | config.test_source_root = os.path.dirname(__file__) 22 | config.suffixes = ['.cpp'] 23 | config.test_format = lit.formats.ShTest(True) 24 | 25 | def get_llvm_tool(name): 26 | ret = os.path.join(config.llvm_bindir,name + config.exe_suffix) 27 | if os.path.exists(ret): return ret 28 | ret = os.path.join(config.llvm_bindir,"../libexec/llvm/" + name + config.exe_suffix) 29 | if os.path.exists(ret): return ret 30 | raise RuntimeError("unable to find %s!" % name) 31 | 32 | # substitutions 33 | config.substitutions.append(("%build_dir",config.build_dir)) 34 | config.substitutions.append(("%llvm_bindir",config.llvm_bindir)) 35 | config.substitutions.append(("%FileCheck",get_llvm_tool("FileCheck"))) 36 | config.substitutions.append(("%count",get_llvm_tool("count"))) 37 | config.substitutions.append(("%exeext",config.exe_suffix)) 38 | config.substitutions.append(("%clang", config.llvm_bindir + "/clang" + config.exe_suffix)) 39 | 40 | TEST_dlopen_LDFLAGS = "" 41 | if platform.system() == "Darwin": 42 | config.available_features.add("darwin") 43 | # Get the sysroot thanks to xcrun and set the DFFI_SYSROOT environment variable 44 | sysroot = subprocess.check_output(["xcrun", "--show-sdk-path"]).strip() 45 | config.environment["DFFI_SYSROOT"] = sysroot 46 | TEST_dlopen_LDFLAGS = f"-L{sysroot.decode()}/usr/lib/" 47 | if platform.system() == "Linux": 48 | config.available_features.add("linux") 49 | if platform.system() in ("Darwin","Linux"): 50 | config.available_features.add("posix") 51 | 52 | if platform.system() == "Windows": 53 | # Add the current build directory to the path, so that executables can load 54 | # dffi.dll (no rpath equivalent on windows) 55 | config.environment['PATH'] += ";" + config.dffi_lib_dir.replace("/","\\") 56 | 57 | config.substitutions.append(("%TEST_dlopen_LDFLAGS", TEST_dlopen_LDFLAGS)) 58 | -------------------------------------------------------------------------------- /bindings/python/errors.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PYDFFI_ERRORS_H 16 | #define PYDFFI_ERRORS_H 17 | 18 | #include 19 | 20 | namespace dffi { 21 | class Type; 22 | } 23 | 24 | struct DFFIError 25 | { 26 | virtual ~DFFIError() { } 27 | virtual const char* what() const = 0; 28 | }; 29 | 30 | struct DFFIErrorStr: public DFFIError 31 | { 32 | DFFIErrorStr(); 33 | DFFIErrorStr(std::string Err); 34 | const char* what() const override; 35 | 36 | protected: 37 | std::string Err_; 38 | }; 39 | 40 | struct CompileError: public DFFIErrorStr 41 | { 42 | using DFFIErrorStr::DFFIErrorStr; 43 | }; 44 | 45 | struct TypeError: public DFFIErrorStr 46 | { 47 | TypeError(dffi::Type const* Got, dffi::Type const* Expected); 48 | TypeError(std::string Err): 49 | DFFIErrorStr(std::move(Err)) 50 | { } 51 | }; 52 | 53 | struct DLOpenError: public DFFIErrorStr 54 | { 55 | using DFFIErrorStr::DFFIErrorStr; 56 | }; 57 | 58 | struct UnknownFunctionError: public DFFIErrorStr 59 | { 60 | UnknownFunctionError(const char* Name); 61 | }; 62 | 63 | struct BadFunctionCall: public DFFIErrorStr 64 | { 65 | using DFFIErrorStr::DFFIErrorStr; 66 | }; 67 | 68 | struct AllocError: public DFFIErrorStr 69 | { 70 | using DFFIErrorStr::DFFIErrorStr; 71 | }; 72 | 73 | struct UnknownField: public DFFIErrorStr 74 | { 75 | using DFFIErrorStr::DFFIErrorStr; 76 | }; 77 | 78 | struct ConstError: public DFFIErrorStr 79 | { 80 | ConstError(); 81 | }; 82 | 83 | struct ConstCastError: public DFFIErrorStr 84 | { 85 | ConstCastError(); 86 | }; 87 | 88 | template 89 | struct ThrowError 90 | { 91 | ~ThrowError() noexcept(false) 92 | { 93 | throw T{ss.str()}; 94 | } 95 | 96 | template 97 | ThrowError& operator<<(U&& o) { 98 | ss << std::forward(o); 99 | return *this; 100 | } 101 | 102 | private: 103 | std::stringstream ss; 104 | }; 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /tests/varargs.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/varargs%exeext" | "%FileCheck" "%s" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | typedef struct { 23 | char a; 24 | struct { 25 | int a; 26 | int b; 27 | } s; 28 | } A; 29 | 30 | int main() 31 | { 32 | DFFI::initialize(); 33 | 34 | CCOpts Opts; 35 | Opts.OptLevel = 2; 36 | Opts.LazyJITWrappers = false; 37 | 38 | DFFI Jit(Opts); 39 | 40 | std::string Err; 41 | auto CU = Jit.compile(R"( 42 | #include 43 | #include 44 | 45 | void print(const char* prefix, ...) 46 | { 47 | va_list args; 48 | va_start(args, prefix); 49 | while (1) { 50 | const char* str = va_arg(args, const char*); 51 | if (!str) break; 52 | printf("%s: %s\n", prefix, str); 53 | } 54 | va_end(args); 55 | } 56 | )", Err); 57 | if (!CU) { 58 | fprintf(stderr, "%s\n", Err.c_str()); 59 | return 1; 60 | } 61 | 62 | const void* Args[4]; 63 | void* NullPtr = nullptr; 64 | Type const* VarArgsTy[3]; 65 | const char* Prefix = "prefix"; 66 | const char* Str0 = "coucou"; 67 | Args[0] = &Prefix; 68 | Args[1] = &Str0; 69 | Args[2] = &NullPtr; 70 | VarArgsTy[0] = Jit.getCharPtrTy(); 71 | VarArgsTy[1] = Jit.getCharPtrTy(); 72 | VarArgsTy[2] = Jit.getCharPtrTy(); 73 | // CHECK: prefix: coucou 74 | CU.getFunction("print", VarArgsTy, 2).call((void**)Args); 75 | // CHECK: prefix: coucou 76 | CU.getFunction("print", VarArgsTy, 2).call((void**)Args); 77 | 78 | const char* Str1 = "coucou2"; 79 | Args[2] = &Str1; 80 | Args[3] = &NullPtr; 81 | // CHECK: prefix: coucou 82 | // CHECK: prefix: coucou2 83 | CU.getFunction("print", VarArgsTy, 3).call((void**)Args); 84 | 85 | CU = Jit.cdef(R"( 86 | void print(const char* prefix, ...); 87 | )", nullptr, Err); 88 | if (!CU) { 89 | fprintf(stderr, "%s\n", Err.c_str()); 90 | return 1; 91 | } 92 | // CHECK: prefix: coucou 93 | // CHECK: prefix: coucou2 94 | CU.getFunction("print", VarArgsTy, 3).call((void**)Args); 95 | 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /include/dffi/iterator_range.h: -------------------------------------------------------------------------------- 1 | // From LLVM's iterator_range.h 2 | // 3 | //===- iterator_range.h - A range adaptor for iterators ---------*- C++ -*-===// 4 | // 5 | // The LLVM Compiler Infrastructure 6 | // 7 | // This file is distributed under the University of Illinois Open Source 8 | // License. See LICENSE.TXT for details. 9 | // 10 | //===----------------------------------------------------------------------===// 11 | /// \file 12 | /// This provides a very simple, boring adaptor for a begin and end iterator 13 | /// into a range type. This should be used to build range views that work well 14 | /// with range based for loops and range based constructors. 15 | /// 16 | /// Note that code here follows more standards-based coding conventions as it 17 | /// is mirroring proposed interfaces for standardization. 18 | /// 19 | //===----------------------------------------------------------------------===// 20 | 21 | #ifndef DFFI_ADT_ITERATOR_RANGE_H 22 | #define DFFI_ADT_ITERATOR_RANGE_H 23 | 24 | #include 25 | #include 26 | 27 | namespace dffi { 28 | 29 | /// A range adaptor for a pair of iterators. 30 | /// 31 | /// This just wraps two iterators into a range-compatible interface. Nothing 32 | /// fancy at all. 33 | template 34 | class iterator_range { 35 | IteratorT begin_iterator, end_iterator; 36 | 37 | public: 38 | //TODO: Add SFINAE to test that the Container's iterators match the range's 39 | // iterators. 40 | template 41 | iterator_range(Container &&c) 42 | //TODO: Consider ADL/non-member begin/end calls. 43 | : begin_iterator(c.begin()), end_iterator(c.end()) {} 44 | iterator_range(IteratorT begin_iterator, IteratorT end_iterator) 45 | : begin_iterator(std::move(begin_iterator)), 46 | end_iterator(std::move(end_iterator)) {} 47 | 48 | IteratorT begin() const { return begin_iterator; } 49 | IteratorT end() const { return end_iterator; } 50 | }; 51 | 52 | /// Convenience function for iterating over sub-ranges. 53 | /// 54 | /// This provides a bit of syntactic sugar to make using sub-ranges 55 | /// in for loops a bit easier. Analogous to std::make_pair(). 56 | template iterator_range make_range(T x, T y) { 57 | return iterator_range(std::move(x), std::move(y)); 58 | } 59 | 60 | template iterator_range make_range(std::pair p) { 61 | return iterator_range(std::move(p.first), std::move(p.second)); 62 | } 63 | 64 | template 65 | iterator_range()))> drop_begin(T &&t, 66 | int n) { 67 | return make_range(std::next(adl_begin(t), n), adl_end(t)); 68 | } 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /tests/func_ptr.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/func_ptr%exeext" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace dffi; 22 | 23 | struct Res 24 | { 25 | int a; 26 | int b; 27 | int res; 28 | }; 29 | 30 | int main(int argc, char** argv) 31 | { 32 | DFFI::initialize(); 33 | 34 | CCOpts Opts; 35 | Opts.OptLevel = 2; 36 | 37 | DFFI Jit(Opts); 38 | 39 | std::string Err; 40 | auto CU = Jit.compile(R"( 41 | typedef struct 42 | { 43 | int a; 44 | int b; 45 | int res; 46 | } Res; 47 | 48 | typedef Res(*op)(int,int); 49 | 50 | static Res get_res(int res, int a, int b) { 51 | Res Ret = {a,b,res}; 52 | return Ret; 53 | } 54 | 55 | static Res add(int a, int b) { 56 | return get_res(a+b,a,b); 57 | } 58 | static Res sub(int a, int b) { 59 | return get_res(a-b,a,b); 60 | } 61 | 62 | op get_op(const char* name) 63 | { 64 | if (name[0] == '+') return add; 65 | if (name[0] == '-') return sub; 66 | return 0; 67 | } 68 | 69 | Res call(op f, int a, int b) { 70 | return f(a,b); 71 | } 72 | )", 73 | Err); 74 | 75 | if (!CU) { 76 | std::cerr << "Compile error: " << Err << std::endl; 77 | return 1; 78 | } 79 | 80 | NativeFunc GetOp = CU.getFunction("get_op"); 81 | PointerType const* PtrOpFTy = cast(GetOp.getReturnType()); 82 | FunctionType const* OpFTy = cast(PtrOpFTy->getPointee()); 83 | 84 | void* AddOp; 85 | const char* OpName = "+"; 86 | void* Args[] = {&OpName}; 87 | GetOp.call(&AddOp, Args); 88 | 89 | void* SubOp; 90 | OpName = "-"; 91 | Args[0] = &OpName; 92 | GetOp.call(&SubOp, Args); 93 | 94 | int a = 1; 95 | int b = 5; 96 | struct Res Ret; 97 | void* OpArgs[] = {&a, &b}; 98 | 99 | Jit.getFunction(OpFTy, AddOp).call(&Ret, OpArgs); 100 | if (Ret.res != 6) { 101 | std::cerr << "add op failed!" << std::endl; 102 | return 1; 103 | } 104 | 105 | Jit.getFunction(OpFTy, SubOp).call(&Ret, OpArgs); 106 | if (Ret.res != -4) { 107 | std::cerr << "sub op failed!" << std::endl; 108 | return 1; 109 | } 110 | 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /lib/cconv.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | // Static asserts that DFFI's calling convention enum has the same values as 19 | // the clang one! 20 | 21 | #pragma GCC diagnostic push 22 | #pragma GCC diagnostic ignored "-Wenum-compare" 23 | static_assert(dffi::CC_C == clang::CC_C, "inconsistency between dffi and clang calling conventions enum!"); 24 | static_assert(dffi::CC_X86StdCall == clang::CC_X86StdCall, "inconsistency between dffi and clang calling conventions enum!"); 25 | static_assert(dffi::CC_X86FastCall == clang::CC_X86FastCall, "inconsistency between dffi and clang calling conventions enum!"); 26 | static_assert(dffi::CC_X86ThisCall == clang::CC_X86ThisCall, "inconsistency between dffi and clang calling conventions enum!"); 27 | static_assert(dffi::CC_X86VectorCall == clang::CC_X86VectorCall, "inconsistency between dffi and clang calling conventions enum!"); 28 | static_assert(dffi::CC_X86Pascal == clang::CC_X86Pascal, "inconsistency between dffi and clang calling conventions enum!"); 29 | static_assert(dffi::CC_Win64 == clang::CC_Win64, "inconsistency between dffi and clang calling conventions enum!"); 30 | static_assert(dffi::CC_X86_64SysV == clang::CC_X86_64SysV, "inconsistency between dffi and clang calling conventions enum!"); 31 | static_assert(dffi::CC_X86RegCall == clang::CC_X86RegCall, "inconsistency between dffi and clang calling conventions enum!"); 32 | static_assert(dffi::CC_AAPCS == clang::CC_AAPCS, "inconsistency between dffi and clang calling conventions enum!"); 33 | static_assert(dffi::CC_AAPCS_VFP == clang::CC_AAPCS_VFP, "inconsistency between dffi and clang calling conventions enum!"); 34 | static_assert(dffi::CC_IntelOclBicc == clang::CC_IntelOclBicc, "inconsistency between dffi and clang calling conventions enum!"); 35 | static_assert(dffi::CC_SpirFunction == clang::CC_SpirFunction, "inconsistency between dffi and clang calling conventions enum!"); 36 | static_assert(dffi::CC_OpenCLKernel == clang::CC_OpenCLKernel, "inconsistency between dffi and clang calling conventions enum!"); 37 | static_assert(dffi::CC_Swift == clang::CC_Swift, "inconsistency between dffi and clang calling conventions enum!"); 38 | static_assert(dffi::CC_PreserveMost == clang::CC_PreserveMost, "inconsistency between dffi and clang calling conventions enum!"); 39 | static_assert(dffi::CC_PreserveAll == clang::CC_PreserveAll, "inconsistency between dffi and clang calling conventions enum!"); 40 | #pragma GCC diagnostic pop 41 | -------------------------------------------------------------------------------- /third-party/pybind11/options.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/options.h: global settings that are configurable at runtime. 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "detail/common.h" 13 | 14 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 15 | 16 | class options { 17 | public: 18 | // Default RAII constructor, which leaves settings as they currently are. 19 | options() : previous_state(global_state()) {} 20 | 21 | // Class is non-copyable. 22 | options(const options &) = delete; 23 | options &operator=(const options &) = delete; 24 | 25 | // Destructor, which restores settings that were in effect before. 26 | ~options() { global_state() = previous_state; } 27 | 28 | // Setter methods (affect the global state): 29 | 30 | options &disable_user_defined_docstrings() & { 31 | global_state().show_user_defined_docstrings = false; 32 | return *this; 33 | } 34 | 35 | options &enable_user_defined_docstrings() & { 36 | global_state().show_user_defined_docstrings = true; 37 | return *this; 38 | } 39 | 40 | options &disable_function_signatures() & { 41 | global_state().show_function_signatures = false; 42 | return *this; 43 | } 44 | 45 | options &enable_function_signatures() & { 46 | global_state().show_function_signatures = true; 47 | return *this; 48 | } 49 | 50 | options &disable_enum_members_docstring() & { 51 | global_state().show_enum_members_docstring = false; 52 | return *this; 53 | } 54 | 55 | options &enable_enum_members_docstring() & { 56 | global_state().show_enum_members_docstring = true; 57 | return *this; 58 | } 59 | 60 | // Getter methods (return the global state): 61 | 62 | static bool show_user_defined_docstrings() { 63 | return global_state().show_user_defined_docstrings; 64 | } 65 | 66 | static bool show_function_signatures() { return global_state().show_function_signatures; } 67 | 68 | static bool show_enum_members_docstring() { 69 | return global_state().show_enum_members_docstring; 70 | } 71 | 72 | // This type is not meant to be allocated on the heap. 73 | void *operator new(size_t) = delete; 74 | 75 | private: 76 | struct state { 77 | bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. 78 | bool show_function_signatures = true; //< Include auto-generated function signatures 79 | // in docstrings. 80 | bool show_enum_members_docstring = true; //< Include auto-generated member list in enum 81 | // docstrings. 82 | }; 83 | 84 | static state &global_state() { 85 | static state instance; 86 | return instance; 87 | } 88 | 89 | state previous_state; 90 | }; 91 | 92 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 93 | -------------------------------------------------------------------------------- /tests/cconv.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/cconv%exeext" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | struct Res 23 | { 24 | int a; 25 | int b; 26 | int res; 27 | }; 28 | 29 | int main(int argc, char** argv) 30 | { 31 | DFFI::initialize(); 32 | 33 | CCOpts Opts; 34 | Opts.OptLevel = 2; 35 | 36 | DFFI Jit(Opts); 37 | 38 | std::string Err; 39 | auto CU = Jit.compile(R"( 40 | __attribute__((ms_abi)) int add(int a) { return a+10; } 41 | )", Err); 42 | 43 | #define CHECK_COMPILE()\ 44 | if (!CU) {\ 45 | std::cerr << Err << std::endl;\ 46 | return 1;\ 47 | } 48 | CHECK_COMPILE() 49 | 50 | { 51 | int Arg = 1; 52 | int Ret; 53 | void* Args[] = {&Arg}; 54 | CU.getFunction("add").call(&Ret, Args); 55 | if (Ret != 11) { 56 | std::cerr << "Invalid return value!" << std::endl; 57 | return 1; 58 | } 59 | } 60 | 61 | CU = Jit.compile(R"( 62 | typedef struct 63 | { 64 | int a; 65 | int b; 66 | int res; 67 | } Res; 68 | 69 | typedef Res(__attribute__((ms_abi)) *op)(int,int); 70 | 71 | __attribute__((ms_abi)) static Res get_res(int res, int a, int b) { 72 | Res Ret = {a,b,res}; 73 | return Ret; 74 | } 75 | 76 | __attribute__((ms_abi)) static Res add(int a, int b) { 77 | return get_res(a+b,a,b); 78 | } 79 | __attribute__((ms_abi)) static Res sub(int a, int b) { 80 | return get_res(a-b,a,b); 81 | } 82 | 83 | __attribute__((ms_abi)) op get_op(const char* name) 84 | { 85 | if (name[0] == '+') return add; 86 | if (name[0] == '-') return sub; 87 | return 0; 88 | } 89 | 90 | __attribute__((ms_abi)) Res call(op f, int a, int b) { 91 | return f(a,b); 92 | } 93 | )", Err); 94 | CHECK_COMPILE() 95 | 96 | NativeFunc GetOp = CU.getFunction("get_op"); 97 | PointerType const* PtrOpFTy = cast(GetOp.getReturnType()); 98 | FunctionType const* OpFTy = cast(PtrOpFTy->getPointee()); 99 | 100 | void* AddOp; 101 | const char* OpName = "+"; 102 | void* Args[] = {&OpName}; 103 | GetOp.call(&AddOp, Args); 104 | 105 | void* SubOp; 106 | OpName = "-"; 107 | Args[0] = &OpName; 108 | GetOp.call(&SubOp, Args); 109 | 110 | int a = 1; 111 | int b = 5; 112 | struct Res Ret; 113 | void* OpArgs[] = {&a, &b}; 114 | 115 | Jit.getFunction(OpFTy, AddOp).call(&Ret, OpArgs); 116 | if (Ret.res != 6) { 117 | std::cerr << "add op failed!" << std::endl; 118 | return 1; 119 | } 120 | 121 | Jit.getFunction(OpFTy, SubOp).call(&Ret, OpArgs); 122 | if (Ret.res != -4) { 123 | std::cerr << "sub op failed!" << std::endl; 124 | return 1; 125 | } 126 | 127 | return 0; 128 | } 129 | -------------------------------------------------------------------------------- /tests/decl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // RUN: "%build_dir/decl%exeext" | "%FileCheck" "%s" 16 | 17 | #include 18 | #include 19 | 20 | using namespace dffi; 21 | 22 | int main(int argc, char** argv) 23 | { 24 | DFFI::initialize(); 25 | 26 | CCOpts Opts; 27 | Opts.OptLevel = 2; 28 | Opts.LazyJITWrappers = false; 29 | 30 | DFFI Jit(Opts); 31 | 32 | const char* myinc; 33 | #ifdef _WIN32 34 | myinc = nullptr; 35 | #else 36 | myinc = "/myinc.h"; 37 | #endif 38 | std::string Err; 39 | auto CU = Jit.cdef(R"( 40 | void print(const char* s); 41 | static const char* msg="def msg"; 42 | )", 43 | myinc, Err); 44 | 45 | #define CHECK_COMPILE()\ 46 | if (!CU) {\ 47 | std::cerr << Err << std::endl;\ 48 | return 1;\ 49 | } 50 | CHECK_COMPILE() 51 | 52 | CU = Jit.cdef(R"( 53 | #include 54 | void print(const char* s) 55 | { 56 | puts(s); 57 | } 58 | )", nullptr, Err); 59 | CHECK_COMPILE() 60 | 61 | const char* arg0 = "coucou"; 62 | void* Args[] = {&arg0}; 63 | int Ret; 64 | // CHECK: coucou 65 | CU.getFunction("puts").call(&Ret, Args); 66 | // CHECK: coucou 67 | CU.getFunction("puts").call(&Ret, Args); 68 | 69 | CU = Jit.compile(R"( 70 | #ifdef _WIN32 71 | // Includes of previously defined CU is broken under Windows... 72 | void print(const char* s); 73 | static const char* msg="def msg"; 74 | #else 75 | #include "/myinc.h" 76 | #endif 77 | void foo() { 78 | print(msg); 79 | print("coucou from foo"); 80 | } 81 | 82 | void toto() 83 | { 84 | print("coucou from toto"); 85 | })", Err); 86 | CHECK_COMPILE() 87 | 88 | // CHECK: coucou from toto 89 | CU.getFunction("toto").call(nullptr); 90 | 91 | // CHECK: def msg 92 | // CHECK: coucou from foo 93 | CU.getFunction("foo").call(nullptr); 94 | 95 | CU = Jit.compile(R"( 96 | void foo(); 97 | void foo2() { 98 | print("foo2"); 99 | foo(); 100 | })", Err); 101 | CHECK_COMPILE() 102 | 103 | // CHECK: foo2 104 | // CHECK: def msg 105 | // CHECK: coucou from foo 106 | CU.getFunction("foo2").call(nullptr); 107 | 108 | CU = Jit.cdef(R"( 109 | #include 110 | typedef uint32_t MyInt; 111 | )", nullptr, Err); 112 | CHECK_COMPILE() 113 | Type const* Ty = CU.getType("MyInt"); 114 | if (!Ty) { 115 | std::cerr << "type MyInt isn't defined!" << std::endl; 116 | return 1; 117 | } 118 | BasicType const* BTy = dyn_cast(Ty); 119 | if (!BTy) { 120 | std::cerr << "type MyInt isn't a basic type!" << std::endl; 121 | return 1; 122 | } 123 | if (BTy->getBasicKind() != BasicType::getKind()) { 124 | std::cerr << "type MyInt isn't an uint32_t!" << std::endl; 125 | return 1; 126 | } 127 | 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /bindings/python/tests/test_mem_refs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # RUN: "%python" "%s" | "%FileCheck" "%s" 16 | # Create everything and only returns a specific object. This object should 17 | # still be usable! 18 | 19 | import unittest 20 | import pydffi 21 | 22 | from common import getFFI 23 | 24 | class MemRefsTest(unittest.TestCase): 25 | def get_CU(self): 26 | FFI = getFFI() 27 | return FFI.compile(''' 28 | struct A { 29 | short a; 30 | int b; 31 | }; 32 | 33 | short get_a(struct A a) { return a.a; }; 34 | int get_b(struct A a) { return a.b; }; 35 | ''') 36 | 37 | def run_CU(self): 38 | CU = self.get_CU() 39 | SA = CU.types.A 40 | A = pydffi.CStructObj(SA) 41 | A.b = 2 42 | self.assertEqual(CU.funcs.get_b(A).value, 2) 43 | 44 | def get_fun(self): 45 | return getFFI().compile(''' 46 | unsigned fact(unsigned n) { 47 | unsigned ret = 1; 48 | for (unsigned i = 2; i <= n; ++i) { 49 | ret *= i; 50 | } 51 | return ret; 52 | } 53 | ''').getFunction("fact") 54 | 55 | def run_fun(self): 56 | fact = self.get_fun() 57 | def pyfact(n): 58 | n = 1 59 | for i in range(2,n+1): n *= i 60 | return n 61 | self.assertEqual(fact.call(6).value, pyfact(6)) 62 | 63 | def get_A(self): 64 | FFI = getFFI() 65 | CU = FFI.cdef(''' 66 | struct A { 67 | short a; 68 | int b; 69 | }; 70 | // TODO: force declaration of struct A... 71 | short __foo(struct A a) { return a.a; } 72 | ''') 73 | 74 | SA = CU.types.A 75 | return pydffi.CStructObj(SA) 76 | 77 | def run_A(self): 78 | A = self.get_A() 79 | A.a = 1 80 | A.b = 5 81 | self.assertEqual(A.a, 1) 82 | self.assertEqual(A.b, 5) 83 | 84 | def get_buf_view(self): 85 | FFI = getFFI() 86 | buf = bytearray(b"hello\x00") 87 | return FFI,pydffi.view_as(FFI.arrayType(FFI.UInt8Ty, len(buf)), buf) 88 | 89 | def run_buf_view(self): 90 | FFI,buf = self.get_buf_view() 91 | CU = FFI.compile(''' 92 | #include 93 | #include 94 | void print_(uint8_t* msg) { 95 | puts(msg); 96 | } 97 | ''') 98 | # CHECK: hello 99 | CU.funcs.print_(buf) 100 | 101 | def test_memrefs(self): 102 | self.run_CU() 103 | self.run_A() 104 | self.run_buf_view() 105 | 106 | self.assertEqual(getFFI().compile("int foo(int a, int b) { return a+b; }").funcs.foo(1,4), 5) 107 | 108 | if __name__ == '__main__': 109 | unittest.main() 110 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 0.9.4 2 | ----- 3 | 4 | * Build wheels for new Python3 versions 5 | * Don't build for macos, as there is a toolchain configuration issue 6 | 7 | 0.9.3 8 | ----- 9 | 10 | * Based on Clang/LLVM 13.0.0 11 | 12 | 0.9.2 13 | ----- 14 | 15 | * Based on Clang/LLVM 12.0.1 16 | * Removed support for Python < 3.6 17 | * Added support for Python 3.10 18 | 19 | 0.9.1 20 | ----- 21 | 22 | * Add the LazyJITWrappers option in Python bindings 23 | 24 | 0.9.0 25 | ----- 26 | 27 | * C++ compilation mode where extern "C" functions can be called 28 | * FFI wrappers are now only compiled when needed, dividing cdef loading time by ~2 29 | 30 | 0.8.0 31 | ----- 32 | 33 | * Support saving errno/LastError for system functions (closes #62) 34 | * Based on Clang/LLVM 11.1 35 | 36 | 0.7.0 37 | ----- 38 | 39 | * Based on Clang/LLVM 11 40 | 41 | 0.6.3 42 | ----- 43 | 44 | * Python: add support to create a pointer with an arbitrary value 45 | 46 | 0.6.2 47 | ----- 48 | 49 | * Fix Python 2.7mu wheels 50 | 51 | 0.6.1 52 | ----- 53 | 54 | * Fix issue with alias functions 55 | 56 | 0.6.0 57 | ----- 58 | 59 | * Add API to add custom symbols 60 | * Add API to get base address of dlopen'ed libraries 61 | * Python: fix creation of functions by their type and absolute address 62 | * Python: allow creation of pointers with an arbitrary value 63 | 64 | 0.5.1 65 | ----- 66 | 67 | * Support for inline anonymous members in structs/unions. Bug found by @Neitsa. 68 | 69 | 0.5.0 70 | ----- 71 | 72 | * Based on LLVM8 73 | * Many bug fixes 74 | 75 | 0.4.0 76 | ----- 77 | 78 | * Based on LLVM7 79 | * Use pybind11 2.2.4 80 | 81 | 0.3.0 82 | ----- 83 | 84 | Python API changes: 85 | 86 | * remove CFunction::call 87 | * remove old and redundant getFunction/get*Type APIs 88 | * Move typeof/sizeof/alignof/ptr APIs to pydffi, as they do not need the 89 | FFI object to be called, and are generic C "builtins". 90 | 91 | Improvements: 92 | 93 | * Better python API for arrays 94 | * CArrayObj iterator API 95 | * All CObj objects now have a QualType, to honor various constness 96 | * Generate a memoryview of a CObj as a sequence of bytes 97 | * Generate a multi-dimensional memoryview of CArrayObj (if relevant) 98 | * Rebase pybind11 on 2.2.3 99 | * APIs to get the LLVM IR of function wrappers 100 | 101 | Bug fixes: 102 | 103 | * fix type promotion for arrays when calling C functions (invalid cast 104 | array->pointer) 105 | * bug in pybind11 (?): format buffer of a memoryview can become invalid 106 | 107 | 0.2.3 108 | ----- 109 | 110 | Improvements: 111 | 112 | * Better python API for arrays 113 | 114 | Bug fixes: 115 | 116 | * Support pointer to arrays as function arguments 117 | * Fix in DFFICtx for arrays: arrays of the same type with a different number of 118 | elements could be considered equal! 119 | 120 | 0.2.2 121 | ----- 122 | 123 | Features: 124 | 125 | * Support for variadic functions 126 | * Official support of the library for Linux/AArch64 127 | 128 | Improvements: 129 | 130 | * Use the real C basic types as DFFI's basic types. This has a side effect that 131 | stdint.h isn't needed anymore for the wrappers code! 132 | * Python bindings: create a union object from its python type 133 | 134 | Bug fixes: 135 | 136 | * Some functions (like malloc, printf, ...) that are pre-declared by clang 137 | weren't considered by cdef 138 | * Support inline functions in cdef 139 | * Fix support for (u)int128_t and complex types! 140 | 141 | 0.2.1 142 | ----- 143 | 144 | Features: 145 | 146 | * Support for Windows 10 x64, with bindings for Python 3 147 | 148 | 0.2.0 149 | ----- 150 | 151 | Features: 152 | 153 | * Support for the C99 boolean type 154 | * Support for OSX x86 32/64 bits 155 | * Support for Linux x86 32 bits 156 | * Support for typedefs in cdef 157 | 158 | Bug fixes: 159 | 160 | * the UnknownField and AllocError exceptions were not registered in pybind11 161 | * an invalid triple was potentially used by the LLVM JITer 162 | * {Complex,}Float{32,64,128} are renamed into 163 | {Complex,}{Float,Double,LongDouble} (closer to the C reality) 164 | 165 | 0.1.0 166 | ----- 167 | 168 | Initial version 169 | -------------------------------------------------------------------------------- /bindings/python/dispatcher.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PYDFFI_DISPATCHER_H 16 | #define PYDFFI_DISPATCHER_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "errors.h" 23 | 24 | template 25 | struct TypeDispatcher 26 | { 27 | template 28 | static auto switch_(dffi::Type const* Ty, Args&& ... args) 29 | -> decltype(T::template case_basic((dffi::BasicType const*)Ty, std::forward(args)...)) 30 | { 31 | assert(Ty != nullptr && "conversion of void type requested!"); 32 | 33 | switch (Ty->getKind()) { 34 | case dffi::Type::TY_Basic: 35 | { 36 | auto* BTy = dffi::cast(Ty); 37 | #define HANDLE_BASICTY(DTy, CTy)\ 38 | case dffi::BasicType::DTy:\ 39 | return T::template case_basic(BTy, std::forward(args)...); 40 | 41 | switch (BTy->getBasicKind()) { 42 | HANDLE_BASICTY(Bool, c_bool); 43 | HANDLE_BASICTY(Char, c_char); 44 | HANDLE_BASICTY(UChar, c_unsigned_char); 45 | HANDLE_BASICTY(UShort, c_unsigned_short); 46 | HANDLE_BASICTY(UInt, c_unsigned_int); 47 | HANDLE_BASICTY(ULong, c_unsigned_long); 48 | HANDLE_BASICTY(ULongLong, c_unsigned_long_long); 49 | #ifdef DFFI_SUPPORT_I128 50 | HANDLE_BASICTY(UInt128, __uint128_t); 51 | #endif 52 | HANDLE_BASICTY(SChar, c_signed_char); 53 | HANDLE_BASICTY(Short, c_short); 54 | HANDLE_BASICTY(Int, c_int); 55 | HANDLE_BASICTY(Long, c_long); 56 | HANDLE_BASICTY(LongLong, c_long_long); 57 | #ifdef DFFI_SUPPORT_I128 58 | HANDLE_BASICTY(Int128, __int128_t); 59 | #endif 60 | HANDLE_BASICTY(Float, c_float); 61 | HANDLE_BASICTY(Double, c_double); 62 | HANDLE_BASICTY(LongDouble, c_long_double); 63 | #ifdef DFFI_SUPPORT_COMPLEX 64 | HANDLE_BASICTY(ComplexFloat, c_complex_float); 65 | HANDLE_BASICTY(ComplexDouble, c_complex_double); 66 | HANDLE_BASICTY(ComplexLongDouble, c_complex_long_double); 67 | #endif 68 | #undef HANDLE_BASICTY 69 | }; 70 | break; 71 | } 72 | case dffi::Type::TY_Struct: 73 | { 74 | auto* STy = dffi::cast(Ty); 75 | return T::case_composite(STy, std::forward(args)...); 76 | } 77 | case dffi::Type::TY_Union: 78 | { 79 | auto* UTy = dffi::cast(Ty); 80 | return T::case_composite(UTy, std::forward(args)...); 81 | } 82 | case dffi::Type::TY_Enum: 83 | { 84 | auto* UTy = dffi::cast(Ty); 85 | return T::case_enum(UTy, std::forward(args)...); 86 | } 87 | case dffi::Type::TY_Pointer: 88 | { 89 | auto* PTy = dffi::cast(Ty); 90 | return T::case_pointer(PTy, std::forward(args)...); 91 | } 92 | case dffi::Type::TY_Array: 93 | { 94 | auto* ATy = dffi::cast(Ty); 95 | return T::case_array(ATy, std::forward(args)...); 96 | } 97 | case dffi::Type::TY_Function: 98 | { 99 | auto* FTy = dffi::cast(Ty); 100 | return T::case_func(FTy, std::forward(args)...); 101 | } 102 | default: 103 | break; 104 | } 105 | dffi::unreachable("unsupported type!"); 106 | } 107 | }; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /lib/anon_member_inliner.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Adrien Guinet 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "dffi_impl.h" 22 | 23 | using namespace llvm; 24 | 25 | namespace dffi { 26 | 27 | void CompositeType::inlineAnonymousMembers() 28 | { 29 | SmallVector ToInline; 30 | const auto ItEnd = FieldsMap_.end(); 31 | for (auto It = FieldsMap_.begin(); It != ItEnd; ++It) { 32 | auto const& CF = *It->second; 33 | if (CF.anonymous()) { 34 | if (isa(CF.getType())) { 35 | ToInline.emplace_back(It); 36 | } 37 | } 38 | } 39 | 40 | InlinedFields_.reserve(ToInline.size()); 41 | for (auto const& It: ToInline) { 42 | CompositeField const& CF = *It->second; 43 | FieldsMap_.erase(It); 44 | // We need to inline the type of this CF. We adjust the offset of the new 45 | // fields thanks to the one of CF. 46 | const unsigned CFOff = CF.getOffset(); 47 | for (auto const& ToInlineF: cast(CF.getType())->getFields()) { 48 | const char* Name = ToInlineF.getName(); 49 | InlinedFields_.emplace_back(CompositeField{Name, ToInlineF.getType(), CFOff+ToInlineF.getOffset()}); 50 | } 51 | } 52 | 53 | for (auto const& CF: InlinedFields_) { 54 | FieldsMap_[CF.getName()] = &CF; 55 | } 56 | } 57 | 58 | namespace details { 59 | 60 | void CUImpl::inlineCompositesAnonymousMembersImpl(std::unordered_set& Visited, CompositeType* CTy) 61 | { 62 | if (!Visited.insert(CTy).second) { 63 | return; 64 | } 65 | 66 | SmallVector CTys; 67 | for (auto& CF: CTy->getFields()) { 68 | if (CF.anonymous()) { 69 | // TODO: what to do when we have an enum? 70 | if (auto* CFTy = dyn_cast(CF.getType())) { 71 | CTys.push_back(const_cast(CFTy)); 72 | } 73 | } 74 | } 75 | 76 | for (auto* CFTy: CTys) { 77 | inlineCompositesAnonymousMembersImpl(Visited, CFTy); 78 | } 79 | 80 | if (CTys.size() > 0) { 81 | CTy->inlineAnonymousMembers(); 82 | } 83 | } 84 | 85 | void CUImpl::inlineCompositesAnonymousMembers() 86 | { 87 | std::unordered_set Visited; 88 | for (auto const& It: CompositeTys_) { 89 | if (auto* CTy = dyn_cast(It.getValue().get())) { 90 | inlineCompositesAnonymousMembersImpl(Visited, CTy); 91 | } 92 | } 93 | #if 0 94 | SetVector OrderedCTys; 95 | 96 | { 97 | SmallVector WorkList; 98 | for (auto const& It: CompositeTys_) { 99 | WorkList.push_back(It->second.get()); 100 | } 101 | std::unordered_set Visited; 102 | 103 | while (!WorkList.empty()) { 104 | CompositeType* Cur = WorkList.back(); 105 | WorkList.pop_back(); 106 | if (!Cur.insert(Visisted).second) { 107 | continue; 108 | } 109 | bool Found = false; 110 | for (auto& CF: Cur->getFields()) { 111 | if (CF.getName().empty()) { 112 | Found = true; 113 | WorkList.push_back(CF.getType()); 114 | } 115 | } 116 | if (Found) { 117 | OrderedCTys.insert(Cur); 118 | } 119 | } 120 | } 121 | 122 | // Let's walk through OrderedCTys backwards, and inline anonymous members 123 | const auto ItEnd = OrderedCTys.end(); 124 | for (auto It = OrderedCTys.rbegin(); It != ItEnd; ++It) { 125 | (*It)->inlineAnonymousMembers(); 126 | } 127 | #endif 128 | } 129 | 130 | } // details 131 | } // dffi 132 | -------------------------------------------------------------------------------- /bindings/python/tests/test_purectypes_gen.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Adrien Guinet 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pydffi 16 | import unittest 17 | import struct 18 | 19 | from common import GenCTypesTest 20 | 21 | class GenTest(GenCTypesTest): 22 | def test_basic(self): 23 | FFI = pydffi.FFI() 24 | Obj = FFI.UIntTy(10) 25 | for T in self.generated_types(FFI.UIntTy, "__UIntTy"): 26 | V = self.purectypes.unpack(T, bytes(pydffi.view_as_bytes(Obj))) 27 | self.assertEqual(V, 10) 28 | 29 | def test_struct(self): 30 | FFI = pydffi.FFI() 31 | CU = FFI.cdef(''' 32 | typedef struct { 33 | unsigned char a; 34 | int b; 35 | int c; 36 | short d; 37 | } A; 38 | ''') 39 | Obj = CU.types.A(a=1,b=2,c=10,d=20) 40 | for A in self.generated_types(CU.types.A, "A"): 41 | V = self.purectypes.unpack(A, bytes(pydffi.view_as_bytes(Obj))) 42 | self.assertEqual(V.a, 1) 43 | self.assertEqual(V.b, 2) 44 | self.assertEqual(V.c, 10) 45 | self.assertEqual(V.d, 20) 46 | V = self.purectypes.pack(A, V) 47 | V = pydffi.view_as(CU.types.A, V) 48 | self.assertEqual(Obj.a, V.a) 49 | self.assertEqual(Obj.b, V.b) 50 | self.assertEqual(Obj.c, V.c) 51 | self.assertEqual(Obj.d, V.d) 52 | 53 | def test_union(self): 54 | FFI = pydffi.FFI() 55 | CU = FFI.cdef(''' 56 | #include 57 | typedef union { 58 | struct { 59 | uint8_t v8[4]; 60 | }; 61 | uint32_t v32; 62 | } IP; 63 | ''') 64 | Obj = CU.types.IP() 65 | Obj.v32 = 0xAABBCCDD 66 | for IP in self.generated_types(CU.types.IP, "IP"): 67 | V = self.purectypes.unpack(IP, bytes(pydffi.view_as_bytes(Obj))) 68 | self.assertEqual(V.v32, 0xAABBCCDD) 69 | for i in range(4): 70 | self.assertEqual(V.v8[i], Obj.v8[i]) 71 | V = self.purectypes.pack(IP, V) 72 | V = pydffi.view_as(pydffi.const(CU.types.IP), V) 73 | self.assertEqual(V.v32, 0xAABBCCDD) 74 | 75 | def test_recursive_struct(self): 76 | FFI = pydffi.FFI() 77 | CU = FFI.cdef(''' 78 | typedef struct { 79 | unsigned char a; 80 | int b; 81 | int c; 82 | short d; 83 | } A; 84 | 85 | typedef struct _Node Node; 86 | 87 | struct _Node { 88 | A v; 89 | struct _Node* next; 90 | }; 91 | ''') 92 | A = CU.types.A 93 | Node = CU.types.Node 94 | A0 = A(a=0,b=1,c=10,d=20) 95 | A1 = A(a=1,b=2,c=-10,d=-20) 96 | N1 = Node(v=A1,next=pydffi.ptr(Node)()) 97 | N0 = Node(v=A0,next=pydffi.ptr(N1)) 98 | 99 | for T in self.generated_types(Node, "_Node"): 100 | V = self.purectypes.unpack(T, bytes(pydffi.view_as_bytes(N0))) 101 | for attr in ("a","b","c","d"): 102 | self.assertEqual(getattr(V.v, attr), getattr(N0.v, attr)) 103 | self.assertEqual(V.next, int(N0.next)) 104 | 105 | def test_recursive_union(self): 106 | FFI = pydffi.FFI() 107 | CU = FFI.cdef(''' 108 | typedef struct { 109 | unsigned char a; 110 | int b; 111 | int c; 112 | short d; 113 | } A; 114 | 115 | typedef union _Node Node; 116 | 117 | union _Node { 118 | A v; 119 | Node* next; 120 | }; 121 | ''') 122 | A = CU.types.A 123 | Node = CU.types.Node 124 | A0 = A(a=0,b=1,c=10,d=20) 125 | A1 = A(a=1,b=2,c=-10,d=-20) 126 | N1 = Node() 127 | N1.next = pydffi.ptr(Node)() 128 | N0 = Node() 129 | N0.next = pydffi.ptr(N1) 130 | 131 | for T in self.generated_types(Node, "_Node"): 132 | V = self.purectypes.unpack(T, bytes(pydffi.view_as_bytes(N0))) 133 | self.assertEqual(V.next, int(N0.next)) 134 | 135 | if __name__ == "__main__": 136 | unittest.main() 137 | -------------------------------------------------------------------------------- /third-party/pybind11/stl/filesystem.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 The Pybind Development Team. 2 | // All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "../pybind11.h" 8 | #include "../detail/common.h" 9 | #include "../detail/descr.h" 10 | #include "../cast.h" 11 | #include "../pytypes.h" 12 | 13 | #include 14 | 15 | #ifdef __has_include 16 | # if defined(PYBIND11_CPP17) 17 | # if __has_include() && \ 18 | PY_VERSION_HEX >= 0x03060000 19 | # include 20 | # define PYBIND11_HAS_FILESYSTEM 1 21 | # elif __has_include() 22 | # include 23 | # define PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM 1 24 | # endif 25 | # endif 26 | #endif 27 | 28 | #if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) \ 29 | && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL) 30 | # error \ 31 | "Neither #include nor #include 39 | struct path_caster { 40 | 41 | private: 42 | static PyObject *unicode_from_fs_native(const std::string &w) { 43 | # if !defined(PYPY_VERSION) 44 | return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size())); 45 | # else 46 | // PyPy mistakenly declares the first parameter as non-const. 47 | return PyUnicode_DecodeFSDefaultAndSize(const_cast(w.c_str()), ssize_t(w.size())); 48 | # endif 49 | } 50 | 51 | static PyObject *unicode_from_fs_native(const std::wstring &w) { 52 | return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size())); 53 | } 54 | 55 | public: 56 | static handle cast(const T &path, return_value_policy, handle) { 57 | if (auto py_str = unicode_from_fs_native(path.native())) { 58 | return module_::import("pathlib") 59 | .attr("Path")(reinterpret_steal(py_str)) 60 | .release(); 61 | } 62 | return nullptr; 63 | } 64 | 65 | bool load(handle handle, bool) { 66 | // PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of 67 | // calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy 68 | // issue #3168) so we do it ourselves instead. 69 | PyObject *buf = PyOS_FSPath(handle.ptr()); 70 | if (!buf) { 71 | PyErr_Clear(); 72 | return false; 73 | } 74 | PyObject *native = nullptr; 75 | if constexpr (std::is_same_v) { 76 | if (PyUnicode_FSConverter(buf, &native) != 0) { 77 | if (auto *c_str = PyBytes_AsString(native)) { 78 | // AsString returns a pointer to the internal buffer, which 79 | // must not be free'd. 80 | value = c_str; 81 | } 82 | } 83 | } else if constexpr (std::is_same_v) { 84 | if (PyUnicode_FSDecoder(buf, &native) != 0) { 85 | if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) { 86 | // AsWideCharString returns a new string that must be free'd. 87 | value = c_str; // Copies the string. 88 | PyMem_Free(c_str); 89 | } 90 | } 91 | } 92 | Py_XDECREF(native); 93 | Py_DECREF(buf); 94 | if (PyErr_Occurred()) { 95 | PyErr_Clear(); 96 | return false; 97 | } 98 | return true; 99 | } 100 | 101 | PYBIND11_TYPE_CASTER(T, const_name("os.PathLike")); 102 | }; 103 | 104 | #endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) 105 | 106 | #if defined(PYBIND11_HAS_FILESYSTEM) 107 | template <> 108 | struct type_caster : public path_caster {}; 109 | #elif defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) 110 | template <> 111 | struct type_caster 112 | : public path_caster {}; 113 | #endif 114 | 115 | PYBIND11_NAMESPACE_END(detail) 116 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 117 | -------------------------------------------------------------------------------- /third-party/pybind11/eval.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/eval.h: Support for evaluating Python expressions and statements 3 | from strings and files 4 | 5 | Copyright (c) 2016 Klemens Morgenstern and 6 | Wenzel Jakob 7 | 8 | All rights reserved. Use of this source code is governed by a 9 | BSD-style license that can be found in the LICENSE file. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include "pybind11.h" 15 | 16 | #include 17 | 18 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 19 | PYBIND11_NAMESPACE_BEGIN(detail) 20 | 21 | inline void ensure_builtins_in_globals(object &global) { 22 | #if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000 23 | // Running exec and eval adds `builtins` module under `__builtins__` key to 24 | // globals if not yet present. Python 3.8 made PyRun_String behave 25 | // similarly. Let's also do that for older versions, for consistency. This 26 | // was missing from PyPy3.8 7.3.7. 27 | if (!global.contains("__builtins__")) 28 | global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE); 29 | #else 30 | (void) global; 31 | #endif 32 | } 33 | 34 | PYBIND11_NAMESPACE_END(detail) 35 | 36 | enum eval_mode { 37 | /// Evaluate a string containing an isolated expression 38 | eval_expr, 39 | 40 | /// Evaluate a string containing a single statement. Returns \c none 41 | eval_single_statement, 42 | 43 | /// Evaluate a string containing a sequence of statement. Returns \c none 44 | eval_statements 45 | }; 46 | 47 | template 48 | object eval(const str &expr, object global = globals(), object local = object()) { 49 | if (!local) { 50 | local = global; 51 | } 52 | 53 | detail::ensure_builtins_in_globals(global); 54 | 55 | /* PyRun_String does not accept a PyObject / encoding specifier, 56 | this seems to be the only alternative */ 57 | std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; 58 | 59 | int start = 0; 60 | switch (mode) { 61 | case eval_expr: 62 | start = Py_eval_input; 63 | break; 64 | case eval_single_statement: 65 | start = Py_single_input; 66 | break; 67 | case eval_statements: 68 | start = Py_file_input; 69 | break; 70 | default: 71 | pybind11_fail("invalid evaluation mode"); 72 | } 73 | 74 | PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); 75 | if (!result) { 76 | throw error_already_set(); 77 | } 78 | return reinterpret_steal(result); 79 | } 80 | 81 | template 82 | object eval(const char (&s)[N], object global = globals(), object local = object()) { 83 | /* Support raw string literals by removing common leading whitespace */ 84 | auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) : str(s); 85 | return eval(expr, std::move(global), std::move(local)); 86 | } 87 | 88 | inline void exec(const str &expr, object global = globals(), object local = object()) { 89 | eval(expr, std::move(global), std::move(local)); 90 | } 91 | 92 | template 93 | void exec(const char (&s)[N], object global = globals(), object local = object()) { 94 | eval(s, std::move(global), std::move(local)); 95 | } 96 | 97 | #if defined(PYPY_VERSION) 98 | template 99 | object eval_file(str, object, object) { 100 | pybind11_fail("eval_file not supported in PyPy3. Use eval"); 101 | } 102 | template 103 | object eval_file(str, object) { 104 | pybind11_fail("eval_file not supported in PyPy3. Use eval"); 105 | } 106 | template 107 | object eval_file(str) { 108 | pybind11_fail("eval_file not supported in PyPy3. Use eval"); 109 | } 110 | #else 111 | template 112 | object eval_file(str fname, object global = globals(), object local = object()) { 113 | if (!local) { 114 | local = global; 115 | } 116 | 117 | detail::ensure_builtins_in_globals(global); 118 | 119 | int start = 0; 120 | switch (mode) { 121 | case eval_expr: 122 | start = Py_eval_input; 123 | break; 124 | case eval_single_statement: 125 | start = Py_single_input; 126 | break; 127 | case eval_statements: 128 | start = Py_file_input; 129 | break; 130 | default: 131 | pybind11_fail("invalid evaluation mode"); 132 | } 133 | 134 | int closeFile = 1; 135 | std::string fname_str = (std::string) fname; 136 | FILE *f = _Py_fopen_obj(fname.ptr(), "r"); 137 | if (!f) { 138 | PyErr_Clear(); 139 | pybind11_fail("File \"" + fname_str + "\" could not be opened!"); 140 | } 141 | 142 | if (!global.contains("__file__")) { 143 | global["__file__"] = std::move(fname); 144 | } 145 | 146 | PyObject *result 147 | = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile); 148 | 149 | if (!result) { 150 | throw error_already_set(); 151 | } 152 | return reinterpret_steal(result); 153 | } 154 | #endif 155 | 156 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 157 | -------------------------------------------------------------------------------- /third-party/pybind11/functional.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/functional.h: std::function<> support 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | 14 | #include 15 | 16 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 17 | PYBIND11_NAMESPACE_BEGIN(detail) 18 | 19 | template 20 | struct type_caster> { 21 | using type = std::function; 22 | using retval_type = conditional_t::value, void_type, Return>; 23 | using function_type = Return (*)(Args...); 24 | 25 | public: 26 | bool load(handle src, bool convert) { 27 | if (src.is_none()) { 28 | // Defer accepting None to other overloads (if we aren't in convert mode): 29 | if (!convert) { 30 | return false; 31 | } 32 | return true; 33 | } 34 | 35 | if (!isinstance(src)) { 36 | return false; 37 | } 38 | 39 | auto func = reinterpret_borrow(src); 40 | 41 | /* 42 | When passing a C++ function as an argument to another C++ 43 | function via Python, every function call would normally involve 44 | a full C++ -> Python -> C++ roundtrip, which can be prohibitive. 45 | Here, we try to at least detect the case where the function is 46 | stateless (i.e. function pointer or lambda function without 47 | captured variables), in which case the roundtrip can be avoided. 48 | */ 49 | if (auto cfunc = func.cpp_function()) { 50 | auto *cfunc_self = PyCFunction_GET_SELF(cfunc.ptr()); 51 | if (cfunc_self == nullptr) { 52 | PyErr_Clear(); 53 | } else if (isinstance(cfunc_self)) { 54 | auto c = reinterpret_borrow(cfunc_self); 55 | 56 | function_record *rec = nullptr; 57 | // Check that we can safely reinterpret the capsule into a function_record 58 | if (detail::is_function_record_capsule(c)) { 59 | rec = c.get_pointer(); 60 | } 61 | 62 | while (rec != nullptr) { 63 | if (rec->is_stateless 64 | && same_type(typeid(function_type), 65 | *reinterpret_cast(rec->data[1]))) { 66 | struct capture { 67 | function_type f; 68 | }; 69 | value = ((capture *) &rec->data)->f; 70 | return true; 71 | } 72 | rec = rec->next; 73 | } 74 | } 75 | // PYPY segfaults here when passing builtin function like sum. 76 | // Raising an fail exception here works to prevent the segfault, but only on gcc. 77 | // See PR #1413 for full details 78 | } 79 | 80 | // ensure GIL is held during functor destruction 81 | struct func_handle { 82 | function f; 83 | #if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17)) 84 | // This triggers a syntax error under very special conditions (very weird indeed). 85 | explicit 86 | #endif 87 | func_handle(function &&f_) noexcept 88 | : f(std::move(f_)) { 89 | } 90 | func_handle(const func_handle &f_) { operator=(f_); } 91 | func_handle &operator=(const func_handle &f_) { 92 | gil_scoped_acquire acq; 93 | f = f_.f; 94 | return *this; 95 | } 96 | ~func_handle() { 97 | gil_scoped_acquire acq; 98 | function kill_f(std::move(f)); 99 | } 100 | }; 101 | 102 | // to emulate 'move initialization capture' in C++11 103 | struct func_wrapper { 104 | func_handle hfunc; 105 | explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {} 106 | Return operator()(Args... args) const { 107 | gil_scoped_acquire acq; 108 | // casts the returned object as a rvalue to the return type 109 | return hfunc.f(std::forward(args)...).template cast(); 110 | } 111 | }; 112 | 113 | value = func_wrapper(func_handle(std::move(func))); 114 | return true; 115 | } 116 | 117 | template 118 | static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { 119 | if (!f_) { 120 | return none().release(); 121 | } 122 | 123 | auto result = f_.template target(); 124 | if (result) { 125 | return cpp_function(*result, policy).release(); 126 | } 127 | return cpp_function(std::forward(f_), policy).release(); 128 | } 129 | 130 | PYBIND11_TYPE_CASTER(type, 131 | const_name("Callable[[") + concat(make_caster::name...) 132 | + const_name("], ") + make_caster::name 133 | + const_name("]")); 134 | }; 135 | 136 | PYBIND11_NAMESPACE_END(detail) 137 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 138 | -------------------------------------------------------------------------------- /third-party/pybind11/detail/descr.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "common.h" 13 | 14 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 15 | PYBIND11_NAMESPACE_BEGIN(detail) 16 | 17 | #if !defined(_MSC_VER) 18 | # define PYBIND11_DESCR_CONSTEXPR static constexpr 19 | #else 20 | # define PYBIND11_DESCR_CONSTEXPR const 21 | #endif 22 | 23 | /* Concatenate type signatures at compile time */ 24 | template 25 | struct descr { 26 | char text[N + 1]{'\0'}; 27 | 28 | constexpr descr() = default; 29 | // NOLINTNEXTLINE(google-explicit-constructor) 30 | constexpr descr(char const (&s)[N + 1]) : descr(s, make_index_sequence()) {} 31 | 32 | template 33 | constexpr descr(char const (&s)[N + 1], index_sequence) : text{s[Is]..., '\0'} {} 34 | 35 | template 36 | // NOLINTNEXTLINE(google-explicit-constructor) 37 | constexpr descr(char c, Chars... cs) : text{c, static_cast(cs)..., '\0'} {} 38 | 39 | static constexpr std::array types() { 40 | return {{&typeid(Ts)..., nullptr}}; 41 | } 42 | }; 43 | 44 | template 45 | constexpr descr plus_impl(const descr &a, 46 | const descr &b, 47 | index_sequence, 48 | index_sequence) { 49 | PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b); 50 | return {a.text[Is1]..., b.text[Is2]...}; 51 | } 52 | 53 | template 54 | constexpr descr operator+(const descr &a, 55 | const descr &b) { 56 | return plus_impl(a, b, make_index_sequence(), make_index_sequence()); 57 | } 58 | 59 | template 60 | constexpr descr const_name(char const (&text)[N]) { 61 | return descr(text); 62 | } 63 | constexpr descr<0> const_name(char const (&)[1]) { return {}; } 64 | 65 | template 66 | struct int_to_str : int_to_str {}; 67 | template 68 | struct int_to_str<0, Digits...> { 69 | // WARNING: This only works with C++17 or higher. 70 | static constexpr auto digits = descr(('0' + Digits)...); 71 | }; 72 | 73 | // Ternary description (like std::conditional) 74 | template 75 | constexpr enable_if_t> const_name(char const (&text1)[N1], char const (&)[N2]) { 76 | return const_name(text1); 77 | } 78 | template 79 | constexpr enable_if_t> const_name(char const (&)[N1], char const (&text2)[N2]) { 80 | return const_name(text2); 81 | } 82 | 83 | template 84 | constexpr enable_if_t const_name(const T1 &d, const T2 &) { 85 | return d; 86 | } 87 | template 88 | constexpr enable_if_t const_name(const T1 &, const T2 &d) { 89 | return d; 90 | } 91 | 92 | template 93 | auto constexpr const_name() -> remove_cv_t::digits)> { 94 | return int_to_str::digits; 95 | } 96 | 97 | template 98 | constexpr descr<1, Type> const_name() { 99 | return {'%'}; 100 | } 101 | 102 | // If "_" is defined as a macro, py::detail::_ cannot be provided. 103 | // It is therefore best to use py::detail::const_name universally. 104 | // This block is for backward compatibility only. 105 | // (The const_name code is repeated to avoid introducing a "_" #define ourselves.) 106 | #ifndef _ 107 | # define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY 108 | template 109 | constexpr descr _(char const (&text)[N]) { 110 | return const_name(text); 111 | } 112 | template 113 | constexpr enable_if_t> _(char const (&text1)[N1], char const (&text2)[N2]) { 114 | return const_name(text1, text2); 115 | } 116 | template 117 | constexpr enable_if_t> _(char const (&text1)[N1], char const (&text2)[N2]) { 118 | return const_name(text1, text2); 119 | } 120 | template 121 | constexpr enable_if_t _(const T1 &d1, const T2 &d2) { 122 | return const_name(d1, d2); 123 | } 124 | template 125 | constexpr enable_if_t _(const T1 &d1, const T2 &d2) { 126 | return const_name(d1, d2); 127 | } 128 | 129 | template 130 | auto constexpr _() -> remove_cv_t::digits)> { 131 | return const_name(); 132 | } 133 | template 134 | constexpr descr<1, Type> _() { 135 | return const_name(); 136 | } 137 | #endif // #ifndef _ 138 | 139 | constexpr descr<0> concat() { return {}; } 140 | 141 | template 142 | constexpr descr concat(const descr &descr) { 143 | return descr; 144 | } 145 | 146 | template 147 | constexpr auto concat(const descr &d, const Args &...args) 148 | -> decltype(std::declval>() + concat(args...)) { 149 | return d + const_name(", ") + concat(args...); 150 | } 151 | 152 | template 153 | constexpr descr type_descr(const descr &descr) { 154 | return const_name("{") + descr + const_name("}"); 155 | } 156 | 157 | PYBIND11_NAMESPACE_END(detail) 158 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 159 | --------------------------------------------------------------------------------