diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst index bfe8f907..3b2f48fc 100644 --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -5,7 +5,12 @@ What's New v2.2.0.dev0 =========== -* TBD +* Fixed variadic function calls on Apple platforms when ``_cffi_backend`` is + built against a libffi other than the one provided by the macOS SDK — that + is, the iOS/tvOS/watchOS wheels, or a macOS build using a Homebrew libffi. + (`#265`_). + +.. _`#265`: https://github.com/python-cffi/cffi/pull/265 v2.1.0 ====== diff --git a/src/c/_cffi_backend.c b/src/c/_cffi_backend.c index b8b3d8ff..6ef9b30e 100644 --- a/src/c/_cffi_backend.c +++ b/src/c/_cffi_backend.c @@ -117,6 +117,25 @@ # define CFFI_CHECK_FFI_PREP_CIF_VAR __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *) # define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 1 +#elif defined(__APPLE__) + +/* On Apple platforms, but built against a libffi whose headers do not + * define FFI_AVAILABLE_APPLE: not the OS-provided libffi, but e.g. the + * static libffi that iOS/tvOS/watchOS wheels are built against, or a + * Homebrew libffi on macOS. There is no runtime-availability question + * there: whatever the linker resolved is present, so all three functions + * can be used unconditionally. ffi_prep_cif_var() in particular must be + * used: on arm64 Apple platforms variadic arguments are passed on the + * stack, unlike regular arguments, so calling a variadic function through + * a cif prepared with ffi_prep_cif() silently passes garbage. + */ +# define CFFI_CHECK_FFI_CLOSURE_ALLOC 1 +# define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 1 +# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC 1 +# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 1 +# define CFFI_CHECK_FFI_PREP_CIF_VAR 1 +# define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 1 + #elif defined(__EMSCRIPTEN__) # define CFFI_CHECK_FFI_CLOSURE_ALLOC 1 diff --git a/src/c/test_c.py b/src/c/test_c.py index c8a88c9f..f9d76665 100644 --- a/src/c/test_c.py +++ b/src/c/test_c.py @@ -1233,11 +1233,6 @@ def test_cannot_pass_struct_with_array_of_length_0(): BFunc2 = new_function_type((BInt,), BStruct, False) pytest.raises(NotImplementedError, cast(BFunc2, 123), 123) -@pytest.mark.xfail( - is_ios, - reason="For an unknown reason f(1, cast(BInt, 42)) returns 36792864", - raises=AssertionError, -) def test_call_function_9(): BInt = new_primitive_type("int") BFunc9 = new_function_type((BInt,), BInt, True) # vararg @@ -3022,11 +3017,6 @@ def test_string_assignment_to_byte_array(): except ImportError: pass # win32 -@pytest.mark.skipif( - is_ios, - reason="For an unknown reason fscanf() doesn't read anything on 3.14" - " and crashes on 3.13 (that's why it's not an xfail)", -) def test_FILE(): if sys.platform == "win32": pytest.skip("testing FILE not implemented")