Affix

 view release on metacpan or  search on metacpan

Changes.md  view on Meta::CPAN


### Changed

  - [[infix]] The JIT memory allocator on Linux now uses `memfd_create` (on kernels 3.17+) to create anonymous file descriptors for dual-mapped W^X memory. This avoids creating visible temporary files in `/dev/shm` and improves hygiene and security. ...
  - \[infix] On dual-mapped platforms (Linux/BSD), the Read-Write view of the JIT memory is now **unmapped immediately** after code generation. This closes a security window where an attacker with a heap read/write primitive could potentially modify ...
  - \[infix] `infix_library_open` now uses `RTLD_LOCAL` instead of `RTLD_GLOBAL` on POSIX systems. This prevents symbols from loaded libraries from polluting the global namespace and causing conflicts with other plugins or the host application.

### Fixed

  - Fixed `CLONE` to correctly copy user-defined types (typedefs, structs) to new threads. Previously, child threads started with an empty registry, causing lookup failures for types defined in the parent.
  - Thread safety: Fixed a crash when callbacks are invoked from foreign threads. Affix now correctly injects the Perl interpreter context into the TLS before executing the callback.
  - Added stack overflow protection to the FFI trigger. Argument marshalling buffers larger than 2KB are now allocated on the heap (arena) instead of the stack, preventing crashes on Windows and other platforms with limited stack sizes.
  - Type resolution: Fixed a logic bug where `Pointer[SV]` types were incorrectly treated as generic pointers if `typedef`'d. They are now correctly unwrapped into Perl CODE refs or blessed objects.
  - Process exit: Disabled explicit library unloading (`dlclose`/`FreeLibrary`) during global destruction. This prevents segmentation faults when background threads from loaded libraries try to execute code that has been unmapped from memory during s...
    I tried to just limit it to Go lang libs but it's just more trouble than it's worth until I resolve a few more things.
  - \[infix] Fixed stack corruption on macOS ARM64 (Apple Silicon). `long double` on this platform is 8 bytes (an alias for `double`), unlike standard AAPCS64 where it is 16 bytes. The JIT previously emitted 16-byte stores (`STR Qn`) for these types,...
  - \[infix] Fixed `long double` handling on macOS Intel (Darwin). Verified that Apple adheres to the System V ABI for this type: it requires 16-byte stack alignment and returns values on the x87 FPU stack (`ST(0)`).
  - \[infix] Fixed a generic System V ABI bug where 128-bit types (vectors, `__int128`) were not correctly aligned to 16 bytes on the stack relative to the return address, causing data corruption when mixed with odd numbers of 8-byte arguments.
  - \[infix] Enforced natural alignment for stack arguments in the AAPCS64 implementation. Previously, arguments were packed to 8-byte boundaries, which violated alignment requirements for 128-bit types.
  - \[infix] Fixed a critical deployment issue where the public `infix.h` header included an internal file (`common/compat_c23.h`). The header is now fully self-contained and defines `INFIX_NODISCARD` for attribute compatibility.
  - \[infix] Fixed 128-bit vector truncation on System V x64 (Linux/macOS). Reverse trampolines previously used 64-bit moves (`MOVSD`) for all SSE arguments, corrupting the upper half of vector arguments. They now correctly use `MOVUPS`.

Changes.md  view on Meta::CPAN

### Changed

  - `Array[Char]` function arguments now accept Perl strings directly, copying the string data into the temporary C array.
  - `Affix::errno()` now returns a dualvar containing both the numeric error code (`errno`/`GetLastError`) and the system error string (`strerror`/`FormatMessage`).

### Fixed

  - Correctly implemented array decay for function arguments on ARM and Win64. `Array[...]` types are now marshalled into temporary C arrays and passed as pointers, matching standard C behavior. Previously, they were incorrectly passed by value, caus...
  - Fixed binary safety for `Array[Char/UChar]`. Reading these arrays now respects the explicit length rather than stopping at the first null byte.
  - The write-back mechanism no longer attempts to overwrite the read-only ArrayRef scalar with the pointer address.
  - `Pointer[SV]` is now handled properly as args, return values, and in callbacks. Reference counting is automatic to prevent premature garbage collection of passed scalars.
  - Shared libs written in Go spin up background threads (for GC and scheduling) that do not shut down cleanly when a shared library is unloaded. This often causes access violations on Windows during program exit. We attempt to work around this by de...

## [v1.0.2] - 2025-12-14

### Changed

  - In an attempt to debug mystery failures in SDL3.pm, Affix.pm will warn and return `undef` instead of `croak`ing.
  - Improved error reporting: if the internal error message is empty, the numeric error code is now included in the warning.

### Fixed

Changes.md  view on Meta::CPAN

  - Cleanup VM on Affix::END()
  - Simplify API around named subs
  - Support for WStr (wchar_t *, PWSTR, etc.)

## [0.09] - 2023-01-26

### Added

  - Structs may now contain a CodeRef
  - CodeRef, Any, etc. are now properly handled as aggregate members
  - Nesting CodeRefs used as callbacks work now
  - Bind to exported values with pin()
  - Expose aggregate by value and syscall support in Affix::Feature
  - Survive callbacks with unexpectedly empty return values
  - Delayed type resolution with InstanceOf

## [0.08] - 2022-12-19

### Fixed

  - Correct struct alignment for perls with quadmath and/or longdouble enabled

## [0.07] - 2022-12-17

MANIFEST  view on Meta::CPAN

t/003_pin.t
t/004_typedef.t
t/005_varargs.t
t/006_out_params.t
t/007_pointers.t
t/008_long_double.t
t/009_128bit.t
t/010_simd.t
t/011_type_introspection.t
t/012_enum.t
t/013_callbacks.t
t/014_array.t
t/015_library.t
t/016_union.t
t/017_affix_build.t
t/018_sv_type.t
t/019_fileio.t
t/020_deep_types.t
t/021_shakedown.t
t/022_stringlist.t
t/023_sockaddr.t

README.md  view on Meta::CPAN


## Callbacks & Functions

- **`Callback[ [$params] => $ret ]`**: Defines the signature of a C function pointer. Allows you to pass Perl subroutines into C functions.

    ```perl
    # C: void set_handler( void (*cb)(int) );
    affix $lib, 'set_handler', [ Callback[ [Int] => Void ] ] => Void;
    ```

- **`ThisCall( $cb_or_sig )`**: Helper for C++-style `__thiscall` callbacks. Prepends a `Pointer[Void]` (the `this` pointer) to the signature.

## Variadic Functions (VarArgs)

Affix supports C functions that take a variable number of arguments (e.g., `printf`, `ioctl`). When defining a
signature, use the `VarArgs` token at the end of the argument list.

### Basic Usage

```perl
# C: int printf(const char* format, ...);

README.md  view on Meta::CPAN

before starting any background threads or loops in the library.

Unsafe operations that you should never call from Callbacks or in a threaded context:

- `affix( ... )` - Binding new functions.
- `typedef( ... )` - Registering new types.

## 2. Callbacks

When passing a Perl subroutine as a `Callback`, avoid performing complex Perl operations like loading modules or
defining subs inside callbacks triggered on a foreign thread. Such callbacks should remain simple: process data, update
a shared variable, and return.

If the library executes the callback from a background thread (e.g., window managers, audio callbacks), Affix attempts
to attach a temporary Perl context to that thread. This should be sufficient but Perl is gonna be Perl.

# RECIPES & EXAMPLES

See [The Affix Cookbook](https://github.com/sanko/Affix.pm/discussions/categories/recipes) for comprehensive guides to
using Affix.

## Linked List Implementation

```perl

infix/include/infix/infix.h  view on Meta::CPAN

/**
 * @file infix.h
 * @brief The public interface for the infix FFI library.
 *
 * @mainpage infix FFI Library
 *
 * @section intro_sec Introduction
 *
 * `infix` is a powerful, modern, and lightweight C library for creating Foreign
 * Function Interface (FFI) trampolines at runtime. It allows you to dynamically
 * call C functions or create C callbacks from any language or environment, using
 * a simple, human-readable string-based syntax to describe function signatures.
 *
 * @section features_sec Key Features
 *
 * - **Simple Signature Syntax:** Describe complex C types, structs, and function
 *   prototypes with an intuitive string format.
 * - **Forward & Reverse Calls:** Create "forward" trampolines to call C from your
 *   code, and "reverse" trampolines (callbacks) to allow C to call back into your code.
 * - **Named Type Registry:** Define, reuse, and link complex, recursive, and
 *   mutually-dependent structs by name.
 * - **Cross-Platform:** Supports major architectures (x86-64, AArch64) and operating
 *   systems (Linux, macOS, Windows).
 * - **Secure:** Designed with modern security principles like W^X (Write XOR Execute)
 *   and hardened against memory corruption.
 * - **Lightweight & Embeddable:** A small, dependency-free library ideal for
 *   embedding in language runtimes, plugins, and other applications.
 *
 * @section usage_sec Basic Usage

infix/include/infix/infix.h  view on Meta::CPAN

 * C function is "bound" into the JIT-compiled code, offering higher performance.
 *
 * @param return_value_ptr A pointer to a buffer to receive the return value. Can be `nullptr` for `void` returns.
 * @param args_array An array of pointers, where each element points to an argument's value.
 */
typedef void (*infix_cif_func)(void *, void **);
/**
 * @brief A function pointer type for a generic closure handler.
 *
 * This handler is used with `infix_reverse_create_closure` and is ideal for language
 * bindings or stateful callbacks where the handler needs access to user-provided data.
 *
 * @param context The reverse trampoline context, from which `user_data` can be retrieved via
 * `infix_reverse_get_user_data`.
 * @param return_value_ptr A pointer to a buffer where the handler must write the function's return value.
 * @param args_array An array of pointers to the argument values passed by the native C caller.
 */
typedef void (*infix_closure_handler_fn)(infix_context_t *, void *, void **);
/**
 * @brief Enumerates the possible status codes returned by `infix` API functions.
 */

infix/include/infix/infix.h  view on Meta::CPAN

 * // and will return 50.
 *
 * infix_reverse_destroy(ctx);
 * @endcode
 */
INFIX_API INFIX_NODISCARD infix_status infix_reverse_create_callback(infix_reverse_t **,
                                                                     const char *,
                                                                     void *,
                                                                     infix_registry_t *);
/**
 * @brief Creates a generic reverse trampoline (closure) for stateful callbacks.
 *
 * @details This is the low-level API for reverse calls, designed for language bindings
 * and advanced use cases. The handler function has a generic signature
 * (`infix_closure_handler_fn`) and receives arguments as a `void**` array.
 * A `user_data` pointer can be provided to maintain state between calls, making it
 * a "closure".
 *
 * This is the most flexible way to handle callbacks, as it allows the handler
 * to be implemented in another language and to access state associated with the
 * callback object.
 *
 * @param[out] out_context A pointer to an `infix_reverse_t*` that will receive the
 *             created context handle.
 * @param[in] signature The signature of the function pointer to be created.
 * @param[in] user_callback_fn A pointer to a generic `infix_closure_handler_fn`.
 * @param[in] user_data A `void*` pointer to application-specific state. This pointer
 *             can be retrieved inside the handler via `infix_reverse_get_user_data(context)`.
 * @param[in] registry An optional type registry.

infix/src/common/infix_internals.h  view on Meta::CPAN

    infix_type * return_type;        /**< Deep copy of the function's return type. */
    infix_type ** arg_types;         /**< Deep copy of the function's argument types. */
    size_t num_args;                 /**< Total number of arguments. */
    size_t num_fixed_args;           /**< Number of non-variadic arguments. */
    bool is_variadic;                /**< `true` if the signature contains variadic arguments. */
    void * user_callback_fn;         /**< The user-provided handler function pointer (type-safe or generic). */
    void * user_data;                /**< The user-provided context pointer for closures. */
    infix_internal_dispatch_callback_fn
        internal_dispatcher; /**< Pointer to the universal C dispatcher implementation. */
    infix_forward_t *
        cached_forward_trampoline; /**< For type-safe callbacks, a pre-generated trampoline to call the C handler. */
};
/**
 * @struct infix_arena_t
 * @brief Internal definition of a memory arena.
 * @details An arena is a fast, region-based allocator. It pre-allocates a single
 * block of memory and serves subsequent small allocation requests by simply
 * "bumping" a pointer. All memory allocated from an arena is freed at once by
 * destroying the arena itself, eliminating the need to track individual allocations.
 */
struct infix_arena_t {

infix/src/jit/trampoline.c  view on Meta::CPAN

            goto cleanup;
        }
        for (size_t i = 0; i < num_args; ++i) {
            context->arg_types[i] = _copy_type_graph_to_arena(context->arena, arg_types[i]);
            if (arg_types[i] != nullptr && context->arg_types[i] == nullptr) {
                status = INFIX_ERROR_ALLOCATION_FAILED;
                goto cleanup;
            }
        }
    }
    // Special step for type-safe callbacks: generate and cache a forward trampoline
    // that will be used to call the user's type-safe C handler.
    if (is_callback) {
        status = infix_forward_create_manual(&context->cached_forward_trampoline,
                                             context->return_type,
                                             context->arg_types,
                                             context->num_args,
                                             context->num_fixed_args,
                                             user_callback_fn);
        if (status != INFIX_SUCCESS)
            goto cleanup;

lib/Affix.c  view on Meta::CPAN

    }
    if (!pin->pointer) {
        warn("Cannot dump a nullptr pointer");
        XSRETURN_EMPTY;
    }
    UV length = SvUV(ST(1));
    if (length == 0) {
        warn("Dump length cannot be zero");
        XSRETURN_EMPTY;
    }
    // PL_curcop may be nullptr during thread destruction or callbacks?
    const char * file = "Unknown";
    int line = 0;
    if (LIKELY(PL_curcop)) {
        file = OutCopFILE(PL_curcop);
        line = CopLINE(PL_curcop);
    }
    _DumpHex(aTHX_ pin->pointer, length, file, line);
    ST(0) = ST(0);
    XSRETURN(1);
}

lib/Affix.pod  view on Meta::CPAN


=head2 Callbacks & Functions

=over

=item * B<C<< Callback[ [$params] => $ret ] >>>: Defines the signature of a C function pointer. Allows you to pass Perl subroutines into C functions.

    # C: void set_handler( void (*cb)(int) );
    affix $lib, 'set_handler', [ Callback[ [Int] => Void ] ] => Void;

=item * B<C<ThisCall( $cb_or_sig )>>: Helper for C++-style C<__thiscall> callbacks. Prepends a C<Pointer[Void]> (the C<this> pointer) to the signature.

=back

=head2 Variadic Functions (VarArgs)

Affix supports C functions that take a variable number of arguments (e.g., C<printf>, C<ioctl>). When defining a
signature, use the C<VarArgs> token at the end of the argument list.

=head3 Basic Usage

lib/Affix.pod  view on Meta::CPAN


=item * C<affix( ... )> - Binding new functions.

=item * C<typedef( ... )> - Registering new types.

=back

=head2 2. Callbacks

When passing a Perl subroutine as a C<Callback>, avoid performing complex Perl operations like loading modules or
defining subs inside callbacks triggered on a foreign thread. Such callbacks should remain simple: process data, update
a shared variable, and return.

If the library executes the callback from a background thread (e.g., window managers, audio callbacks), Affix attempts
to attach a temporary Perl context to that thread. This should be sufficient but Perl is gonna be Perl.

=head1 RECIPES & EXAMPLES

See L<The Affix Cookbook|https://github.com/sanko/Affix.pm/discussions/categories/recipes> for comprehensive guides to
using Affix.

=head2 Linked List Implementation

    # C equivalent:

lib/Affix/Wrap.pod  view on Meta::CPAN

=head2 Affix::Wrap::Type::Pointer

Represents C<T*> types. Wraps another type object.

=head2 Affix::Wrap::Type::Array

Represents C<T[N]> fixed-size arrays. Wraps a type object and a count.

=head2 Affix::Wrap::Type::CodeRef

Represents function pointers (callbacks), e.g., C<void (*)(int)>.

=over

=item * C<ret>: Return type object.

=item * C<params>: ArrayRef of argument type objects.

=item * C<affix_type>: Returns string C<Callback[[Args] =E<gt> Ret]>.

=back

t/004_typedef.t  view on Meta::CPAN

};
subtest 'Advanced Structs and Unions' => sub {
    affix $lib_path, 'sum_point_by_val', '(@Point)->int';
    my $point_hash = { x => 10, y => 25 };
    is( sum_point_by_val($point_hash), 35, 'Correctly passed a struct by value' );
    affix $lib_path, 'read_union_int', '(@MyUnion)->int';
    my $union_hash = { i => 999 };
    is( read_union_int($union_hash), 999, 'Correctly read int member from a C union' );
};
subtest 'Advanced Callbacks (Reverse FFI) (with Typedefs)' => sub {
    diag 'Testing callbacks that send and receive structs by passing coderefs directly.';
    isa_ok my $harness1 = wrap( $lib_path, 'process_struct_with_cb', '(*@MyStruct, (*(@MyStruct))->float64)->float64' ), ['Affix'];
    my $struct_to_pass = { id => 100, value => 5.5, label => 'Callback Struct' };
    my $cb1            = sub ($struct_ref) {

        # Struct Pointer comes as a Pin (Scalar Ref). Dereference it.
        my $struct = $$struct_ref;
        return $struct->{value} * 2;
    };
    is $harness1->( $struct_to_pass, $cb1 ), 11.0, 'Callback coderef received struct pointer and returned correct value';
    isa_ok my $harness2 = wrap( $lib_path, 'check_returned_struct_from_cb', '( *(()->void  )->@Point )->int32' ), ['Affix'];

t/013_callbacks.t  view on Meta::CPAN

    is $seen_label, "Check", 'Callback received struct pointer correctly';
    #
    isa_ok my $chk_pt = wrap( $lib_path, 'check_point_gen', [ Callback [ [] => Point() ] ] => Int ), ['Affix'];
    my $sum = $chk_pt->(
        sub {
            return { x => 7, y => 8 };
        }
    );
    is $sum, 15, 'Callback returned struct by value correctly';
};
subtest 'unions passed to callbacks' => sub {
    ok typedef( MyUnion => Union [ i => SInt32, f => Float32, c => Array [ Char, 8 ] ] ), 'typedef @MyUnion';
    isa_ok my $invoke = wrap( $lib_path, 'invoke_union_cb', [ Callback [ [ Pointer [ MyUnion() ] ] => Void ] ] => Int ), ['Affix'];
    my $cb = sub($pin) {

        # Dereference the pin
        my $u = $$pin;
        is $u->{i}, 42, 'Read integer member from union pointer directly';    # magical

        # IEEE 754 2.0f is 0x40000000 (1073741824 decimal)
        $u->{f} = 2.0;



( run in 2.816 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )