view release on metacpan or search on metacpan
- SIMD Vector Improvements:
- Added M512, M512d, and M512i type helpers.
- Ensured compatibility with infix's refined vector alignment and passing rules.
- [infix] Added support for half-precision floating-point (`float16`).
- [infix] Implemented C++ exception propagation through JIT frames on Linux (x86-64 and ARM64) using manual DWARF `.eh_frame` generation and `__register_frame`.
- [infix] Implemented Structured Exception Handling (SEH) for Windows x64 and ARM64 for C++ exception propagation through trampolines.
- [infix] Added `infix_forward_create_safe` API to establish an exception boundary that catches native exceptions and returns a dedicated error code (`INFIX_CODE_NATIVE_EXCEPTION`).
- [infix] Added support for 256-bit (AVX) and 512-bit (AVX-512) vectors in the System V ABI.
- [infix] Added support for receiving bitfield structs in reverse call trampolines.
- [infix] Added trampoline caching. Identical signatures and targets now share the same JIT-compiled code and metadata via internal reference counting, significantly reducing memory overhead and initialization time.
- [infix] Added a new opt-in build mode (`--sanity`) that emits extra JIT instructions to verify stack pointer consistency around user-provided marshaller calls, making it easier to debug corrupting language bindings.
### Changed
- Pull infix v0.1.6.
- [infix] Explicitly enabled 16-byte stack alignment in Windows x64 trampolines to ensure SIMD compatibility.
- [infix] Updated `infix_type_create_vector` to use the vector's full size for its natural alignment (e.g., 32-byte alignment for `__m256`).
- [infix] Refined the Windows x64 ABI to pass all vector types by reference (pointer in GPR). This ensures compatibility with MSVC which expects even 128-bit vectors to be passed via pointer in many scenarios, while still returning them by value in `...
- [infix] Move to a pre-calculated hash field in `_infix_registry_entry_t`. Lookups and rehashing now use this stored hash, significantly reducing string hashing overhead during type resolution and registry scaling.
- [infix] Optimized Type Registry memory management: Internal hash table buckets are now heap-allocated and freed during rehashes, preventing memory "leaks" within the registry's arena.
- 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
- [[infix]] Fixed a critical file descriptor leak on POSIX platforms (Linux/FreeBSD) where the file descriptor returned by `shm_open` was kept open for the lifetime of the trampoline, eventually hitting the process file descriptor limit (EMFILE). T...
- Fixed memory leaks that occurred when trampoline creation failed midway (cleaning up partial arenas, strings, and backend structures).
## [v1.0.1] - 2025-12-13
### Changed
# 2. Read the memory immediately as an integer value
my $val = cast($void_ptr, Int); # Returns 99
```
# POINTER UTILITIES
### `address( $ptr )`
Returns the virtual memory address of the pointer as a Perl Unsigned Integer (`UInt64`). Useful for passing addresses
to other FFI libraries or debugging.
```
say sprintf("Address: 0x%X", address($ptr));
```
### `ptr_add( $ptr, $offset_bytes )`
Returns a new **unmanaged alias Pin** offset by `$offset_bytes`.
```perl
builder/Affix/Builder.pm view on Meta::CPAN
use JSON::PP 2 qw[encode_json decode_json];
use File::Temp qw[tempfile];
# Not in CORE
use Path::Tiny qw[path cwd];
use ExtUtils::Helpers 0.028 qw[make_executable split_like_shell detildefy];
# infix and Affix stuff
use Config qw[%Config];
field $force : param //= 0;
field $debug : param = 0;
field $libver;
field $cflags;
field $ldflags;
field $cppver = 'c++17'; # https://en.wikipedia.org/wiki/C%2B%2B20#Compiler_support
field $cver = 'c17'; # https://en.wikipedia.org/wiki/C17_(C_standard_revision)
field $make : param //= $Config{make};
#
field $action : param //= 'build';
field $meta : reader = CPAN::Meta->load_file('META.json');
builder/Affix/Builder.pm view on Meta::CPAN
field $prefix : param //= '';
#
ADJUST {
-e 'META.json' or die "No META information provided\n";
# Configure Flags
my $is_bsd = $^O =~ /bsd/i;
my $is_win = $^O =~ /MSWin32/i;
$cflags = $is_bsd ? '' : '-fPIC ';
$ldflags = $is_bsd ? '' : ' -flto=auto ';
if ( $debug > 0 ) {
$cflags
.= '-DDEBUG=' .
$debug .
' -g3 -gdwarf-4 ' .
' -Wno-deprecated -pipe ' .
' -Wall -Wextra -Wpedantic -Wvla -Wnull-dereference ' .
' -Wswitch-enum -Wduplicated-cond ' .
' -Wduplicated-branches';
$cflags .= ' -fvar-tracking-assignments' unless $Config{osname} eq 'darwin';
}
elsif ( !$is_win ) {
$cflags
.= ' -DNDEBUG -DBOOST_DISABLE_ASSERTS -Ofast -ftree-vectorize -ffast-math -fno-align-functions -fno-align-loops -fno-omit-frame-pointer -flto=auto';
builder/Affix/Builder.pm view on Meta::CPAN
quiet => 0,
'C++' => $cxx,
source => $source->stringify,
defines => { VERSION => qq/"$version"/, XS_VERSION => qq/"$version"/ },
include_dirs => [
cwd->stringify, cwd->child('infix')->realpath->stringify,
cwd->child('infix')->child('include')->realpath->stringify, cwd->child('infix')->child('src')->realpath->stringify,
$source->dirname, $pre->child( $meta->name, 'include' )->stringify
],
extra_compiler_flags =>
( '-fPIC -std=' . ( $cxx ? $cppver : $cver ) . ' ' . $cflags . ( $debug ? ' -ggdb3 -g -Wall -Wextra -pedantic' : '' ) )
) :
$obj;
}
# Point to the Architecture-specific build lib
my $infix_build_lib = cwd->absolute->child('infix')->child( 'build_lib', $Config{archname} )->stringify;
# Check for -lrt requirement
my $lrt_flag = $self->check_for_lrt();
my $data = {
infix/include/infix/infix.h view on Meta::CPAN
*/
typedef struct infix_registry_iterator_t {
const infix_registry_t * registry; /**< The registry being iterated. */
size_t _bucket_index; /**< Internal: current hash bucket. */
void * _current_entry; /**< Internal: opaque pointer to current entry. */
} infix_registry_iterator_t;
/**
* @brief Serializes all defined types within a registry into a single, human-readable string.
*
* The output format is a sequence of definitions (e.g., `@Name = { ... };`) separated
* by newlines, suitable for logging, debugging, or saving to a file. This function
* will not print forward declarations that have not been fully defined.
*
* @param[out] buffer The output buffer to write the string into.
* @param[in] buffer_size The size of the output buffer.
* @param[in] registry The registry to serialize.
* @return `INFIX_SUCCESS` on success, or `INFIX_ERROR_INVALID_ARGUMENT` if the buffer is too small.
*/
INFIX_API INFIX_NODISCARD infix_status infix_registry_print(char *, size_t, const infix_registry_t *);
/**
* @brief Initializes an iterator for traversing the types in a registry.
infix/src/common/utility.h view on Meta::CPAN
*
* SPDX-License-Identifier: (Artistic-2.0 OR MIT)
*
* The documentation blocks within this file are licensed under the
* Creative Commons Attribution 4.0 International License (CC BY 4.0).
*
* SPDX-License-Identifier: CC-BY-4.0
*/
/**
* @file utility.h
* @brief A header for conditionally compiled debugging utilities.
* @ingroup internal_utils
*
* @internal
* This header is the central point for the library's internal debugging infrastructure.
* Its primary feature is that it behaves differently based on the `INFIX_DEBUG_ENABLED`
* preprocessor macro. This allows debugging code to be seamlessly integrated
* during development without affecting the performance or size of the final
* production binary.
*
* - **When `INFIX_DEBUG_ENABLED` is defined and non-zero (Debug Mode):**
* - It declares the `infix_dump_hex` function for detailed memory inspection.
* - It defines the `INFIX_DEBUG_PRINTF` macro, which integrates with the `double_tap`
* test harness's logging system (`note()`) if available, or falls back to a
* standard `printf`. This allows debug messages from the core library to appear
* cleanly within the test output.
*
* - **When `INFIX_DEBUG_ENABLED` is not defined or is zero (Release Mode):**
* - All debugging macros are defined as no-ops (`((void)0)`).
* - The `infix_dump_hex` function is defined as an empty `static inline` function.
* - This design ensures that all debugging code and calls are completely compiled
* out by the optimizer, resulting in zero overhead in release builds.
* @endinternal
*/
#include "common/compat_c23.h"
#include <stddef.h>
// Check if INFIX_DEBUG_ENABLED is defined and set to a non-zero value.
#if defined(INFIX_DEBUG_ENABLED) && INFIX_DEBUG_ENABLED
#include <stdio.h>
/**
* @internal
* @def INFIX_DEBUG_PRINTF(...)
* @brief A macro for printing formatted debug messages during a debug build.
* @details This macro falls back to a standard `printf`, ensuring that debug messages are still visible.
* We use a '#' prefix to ensure these messages are treated as comments by TAP consumers.
*/
#define INFIX_DEBUG_PRINTF(...) \
do { \
printf("# INFIX_DEBUG: " __VA_ARGS__); \
printf("\n"); \
fflush(stdout); \
} while (0)
/**
* @internal
* @brief Declares the function prototype for `infix_dump_hex` for use in debug builds.
* @details This function is an invaluable tool for inspecting the raw machine code generated
* by the JIT compiler or examining the memory layout of complex structs. It prints
* a detailed hexadecimal and ASCII dump of a memory region to the standard
* output, formatted for readability.
*
* @param data A pointer to the start of the memory block to dump.
* @param size The number of bytes to dump.
* @param title A descriptive title to print before and after the hex dump.
*/
void infix_dump_hex(const void * data, size_t size, const char * title);
/**
* @internal
* @brief Declares the function prototype for `infix_dump_state` for use in debug builds.
* @details This function is an invaluable tool for inspecting the processor state.
*
* @param file The file name where the function is being called.
* @param title The line number.
*/
void infix_dump_state(const char * file, int line);
/**
* @internal
* @def INFIX_DUMP_STATE(...)
* @brief Automatically pass on the current file and line to `infix_dump_state`.
*/
#define INFIX_DUMP_STATE() infix_dump_state(__FILE__, __LINE__)
#else // INFIX_DEBUG_ENABLED is NOT defined or is zero (Release Mode)
/**
* @internal
* @def INFIX_DEBUG_PRINTF(...)
* @brief A no-op macro for printing debug messages in release builds.
* @details In release builds, this macro is defined as `((void)0)`, a standard C idiom
* for creating a statement that does nothing and has no side effects. The
* compiler will completely remove any calls to it, ensuring zero performance impact.
*/
#define INFIX_DEBUG_PRINTF(...) ((void)0)
/**
* @internal
* @brief A no-op version of `infix_dump_hex` for use in release builds.
* @details This function is defined as an empty `static inline` function.
* - `static`: Prevents linker errors if this header is included in multiple files.
infix/src/core/signature.c view on Meta::CPAN
* @details This module is responsible for two key functionalities that form the
* user-facing API of the library:
*
* 1. **Parsing:** It contains a hand-written recursive descent parser that transforms a
* human-readable signature string (e.g., `"({int, *char}) -> void"`) into an
* unresolved `infix_type` object graph. This is the **"Parse"** stage of the core
* data pipeline. The internal entry point for the "Parse" stage is `_infix_parse_type_internal`.
*
* 2. **Printing:** It provides functions to serialize a fully resolved `infix_type`
* graph back into a canonical signature string. This is crucial for introspection,
* debugging, and verifying the library's understanding of a type.
*
* The public functions `infix_type_from_signature` and `infix_signature_parse`
* are high-level orchestrators. They manage the entire **"Parse -> Copy -> Resolve -> Layout"**
* pipeline, providing the user with a fully validated, self-contained, and ready-to-use
* type object that is safe to use for the lifetime of its returned arena.
*/
#include "common/infix_internals.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
infix/src/core/signature.c view on Meta::CPAN
}
}
else if (buffer_size > 0)
buffer[buffer_size - 1] = '\0';
return state.status;
}
/**
* @brief Serializes all defined types within a registry into a single, human-readable string.
*
* @details The output format is a sequence of definitions (e.g., `@Name = { ... };`) separated
* by newlines, suitable for logging, debugging, or saving to a file. This function
* will not print forward declarations that have not been fully defined. The order of
* definitions in the output string is not guaranteed.
*
* @param[out] buffer The output buffer to write the string into.
* @param[in] buffer_size The size of the output buffer.
* @param[in] registry The registry to serialize.
* @return `INFIX_SUCCESS` on success, or `INFIX_ERROR_INVALID_ARGUMENT` if the buffer is too small
* or another error occurs.
*/
c23_nodiscard infix_status infix_registry_print(char * buffer, size_t buffer_size, const infix_registry_t * registry) {
infix/src/core/utility.c view on Meta::CPAN
*
* SPDX-License-Identifier: (Artistic-2.0 OR MIT)
*
* The documentation blocks within this file are licensed under the
* Creative Commons Attribution 4.0 International License (CC BY 4.0).
*
* SPDX-License-Identifier: CC-BY-4.0
*/
/**
* @file utility.c
* @brief Implements internal debugging utilities.
* @ingroup internal_core
*
* @details This file provides the implementation for debugging functions that are
* conditionally compiled only when the `INFIX_DEBUG_ENABLED` preprocessor macro
* is defined and set to a non-zero value.
*
* In release builds, this entire translation unit is effectively empty, ensuring
* that debugging code has no impact on the final binary's size or performance.
* The corresponding function declarations in `utility.h` become empty inline
* stubs, which are optimized away by the compiler.
*/
// This file is only compiled if debugging is enabled.
#if defined(INFIX_DEBUG_ENABLED) && INFIX_DEBUG_ENABLED
// Use the double-tap test harness's `note` macro for debug printing if available.
// This integrates the debug output seamlessly into the TAP test logs.
#if defined(DBLTAP_ENABLE) && defined(DBLTAP_IMPLEMENTATION)
#include "common/double_tap.h"
#else
// If not building as part of a test, fall back to a standard printf implementation.
#include <stdio.h>
#ifndef note
#define note(...) \
do { \
printf("# " __VA_ARGS__); \
printf("\n"); \
} while (0)
#endif
#endif // DBLTAP_ENABLE
#include "common/utility.h"
#include <inttypes.h>
/**
* @internal
* @brief Dumps a block of memory to standard output in a standard hexadecimal format.
*
* @details This is an essential debugging tool for inspecting the machine code generated
* by the JIT engine or for examining the memory layout of complex data structures.
* The output is formatted similarly to common hex dump utilities, with a 16-byte
* width, address offsets, hexadecimal bytes, and an ASCII representation.
*
* This function is only compiled in debug builds.
*
* @param data A pointer to the memory to dump.
* @param size The number of bytes to dump.
* @param title A descriptive title to print before the hex dump.
*/
void infix_dump_hex(const void * data, size_t size, const char * title) {
const uint8_t * byte = (const uint8_t *)data;
char line_buf[256];
char * buf_ptr;
size_t remaining_len;
infix/src/core/utility.c view on Meta::CPAN
remaining_len -= written;
}
}
print_line:
note(" %s", line_buf);
}
note("End of %s", title);
}
/**
* @internal
* @brief Declares the function prototype for `infix_dump_state` for use in debug builds.
* @details This function is an invaluable tool for inspecting the processor state.
*
* This function is only compiled in debug builds.
*
* @param file The file name where the function is being called.
* @param title The line number.
*/
void infix_dump_state(const char * file, int line) {
#if defined(INFIX_ARCH_X64)
printf("# Dumping x64 Register State at %s:%d\n", file, line);
volatile unsigned long long stack_dump[16];
register long long rsp __asm__("rsp");
infix/src/infix.c view on Meta::CPAN
* rest of the source tree. It is the entry point for the build system.
* @endinternal
*/
// 1. Error Handling: Provides the thread-local error reporting system.
// (No dependencies on other infix modules).
#include "core/error.c"
// 2. Arena Allocator: The fundamental memory management component.
// (Depends only on malloc/free).
#include "core/arena.c"
// 3. OS Executor: Handles OS-level memory management for executable code.
// (Depends on error handling, debugging utilities).
#include "jit/executor.c"
// 4. Type Registry: Manages named types.
// (Depends on arena for storage and signature parser for definitions).
#include "core/type_registry.c"
// 5. Signature Parser: Implements the high-level string-based API.
// (Depends on types, arena, and registry).
#include "core/signature.c"
// 6. Dynamic Library Loader: Implements cross-platform `dlopen`/`dlsym`.
// (Depends on error handling, types, and arena).
#include "core/loader.c"
infix/src/jit/executor.c view on Meta::CPAN
#endif
/**
* @internal
* @brief Frees a block of executable memory with use-after-free hardening.
*
* @details Before freeing the memory, this function first attempts to change the
* memory protection to be inaccessible (`PROT_NONE` or `PAGE_NOACCESS`). This
* creates a "guard page" that will cause an immediate, safe crash if a dangling
* pointer to the freed trampoline is ever used, making use-after-free bugs
* much easier to detect and debug.
*
* @param exec The executable memory block to free.
*/
void infix_executable_free(infix_executable_t exec) {
if (exec.size == 0)
return;
#if defined(INFIX_OS_WINDOWS)
#if defined(INFIX_ARCH_X64) || defined(INFIX_ARCH_AARCH64)
if (exec.seh_registration)
RtlDeleteFunctionTable((PRUNTIME_FUNCTION)exec.seh_registration);
lib/Affix.pm view on Meta::CPAN
use Carp qw[];
use Config qw[%Config];
use File::Spec::Functions qw[rel2abs canonpath curdir path catdir];
use File::Basename qw[basename dirname];
use File::Find qw[find];
use File::Temp qw[tempdir];
my $okay = 0;
BEGIN {
use XSLoader;
$DynaLoad::dl_debug = $DynaLoad::dl_debug = 1;
$okay = XSLoader::load();
my $platform
= 'Affix::Platform::' .
( ( $^O eq 'MSWin32' ) ? 'Windows' :
$^O eq 'darwin' ? 'MacOS' :
( $^O eq 'freebsd' || $^O eq 'openbsd' || $^O eq 'netbsd' || $^O eq 'dragonfly' ) ? 'BSD' :
'Unix' );
#~ warn $platform;
#~ use base $platform;
lib/Affix.pod view on Meta::CPAN
$int_ptr->[0] = 99;
# 2. Read the memory immediately as an integer value
my $val = cast($void_ptr, Int); # Returns 99
=head1 POINTER UTILITIES
=head3 C<address( $ptr )>
Returns the virtual memory address of the pointer as a Perl Unsigned Integer (C<UInt64>). Useful for passing addresses
to other FFI libraries or debugging.
say sprintf("Address: 0x%X", address($ptr));
=head3 C<ptr_add( $ptr, $offset_bytes )>
Returns a new B<unmanaged alias Pin> offset by C<$offset_bytes>.
my $int_arr = calloc(10, Int);
my $next_elem = ptr_add($int_arr, sizeof(Int));
lib/Affix/Build.pm view on Meta::CPAN
use ExtUtils::MakeMaker;
use Text::ParseWords;
class Affix::Build {
# Public Parameters
field $os : param : reader //= $^O;
field $clean : param : reader //= 0;
field $build_dir : param : reader //= Path::Tiny->tempdir( CLEANUP => $clean );
field $name : param : reader //= 'affix_lib';
field $debug : param : reader //= 0;
field $version : param : reader //= ();
# Global flags applied to all compilations of that type
# cflags, cxxflags, ldflags, rustflags, etc.
field $flags : param : reader //= {};
# Internal State
field @sources;
field $libname : reader;
field $linker : reader;
lib/Affix/Build.pm view on Meta::CPAN
elsif ( $l =~ /^(nim)$/ ) { return '_build_nim'; }
elsif ( $l =~ /^(d|dlang)$/ ) { return '_build_d'; }
elsif ( $l =~ /^(swift)$/ ) { return '_build_swift'; }
elsif ( $l =~ /^(v|vlang)$/ ) { return '_build_v'; }
# Fallback to C
return '_build_c';
}
method _run (@cmd) {
print STDERR "[Affix] Exec: @cmd\n" if $debug;
# use Data::Dump;
# ddx \@cmd;
my ( $stdout, $stderr, $exit ) = capture {
system @cmd;
};
if ( !!$exit ) {
warn $stdout if $stdout;
warn $stderr if $stderr;
my $rc = $exit >> 8;
lib/Affix/Build.pod view on Meta::CPAN
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 { a + b }
RUST
my $rust_dll = $rust->link();
# Example 2: Polyglot (C and Go)
my $mix = Affix::Build->new(
name => 'hybrid_lib',
flags => { cflags => '-O3', ldflags => '-L/opt/lib' },
debug => 1
);
# Add file with per-file compiler overrides
$mix->add('src/wrapper.c', flags => '-DDEBUG');
$mix->add('src/core.go');
my $poly_dll = $mix->link();
=head1 DESCRIPTION
lib/Affix/Build.pod view on Meta::CPAN
=over
=item * B<C<name>>: The base name of the resulting library (default: C<'affix_lib'>). The compiler will automatically append the OS-specific extension (C<.dll> on Windows, C<.dylib> on macOS, C<.so> on Linux).
=item * B<C<version>>: Optional version string to append to the library name.
=item * B<C<build_dir>>: The directory where intermediate artifacts and the final library will be written. If not provided, a temporary directory is created via L<Path::Tiny>.
=item * B<C<clean>>: If true, the generated C<build_dir> and all its contents will be deleted when the object is destroyed (default: C<0>).
=item * B<C<debug>>: If true, the exact system commands executed by the compiler will be printed to C<STDERR> (default: C<0>).
=item * B<C<flags>>: A HashRef of global flags applied across all files. Keys can be C<cflags>, C<cxxflags>, and C<ldflags>.
=item * B<C<os>>: Override the detected operating system (defaults to C<$^O>).
=back
=head1 METHODS
=head2 C<add( $input, %args )>
lib/Affix/Platform/Windows.pm view on Meta::CPAN
$clibname = 'msvcrt';
}
elsif ( $version <= 13 ) {
$clibname = sprintf( 'msvcr%d', $version * 10 );
}
else {
# CRT not directly loadable (see python/cpython#23606)
return undef;
}
# Check for debug build
my $debug_suffix = '_d'; # Assuming debug suffix is '_d'
my $suffixes = join '|', map quotemeta, @DynaLoader::dl_extensions;
if ( $debug_suffix =~ /$suffixes/ ) {
$clibname .= $debug_suffix;
}
return "$clibname.dll";
}
sub get_msvcrt_version() {
open( my $pipe, '-|', 'dumpbin /headers msvcrt.dll', 'r' ) or return;
my $dumpbin_output;
$dumpbin_output .= $_ while <$pipe>;
close $pipe;
return $1 if $dumpbin_output && $dumpbin_output =~ /FileVersion\s+(\d+\.\d+\.\d+\.\d+)/;
lib/Test2/Tools/Affix.pm view on Meta::CPAN
$line++;
$filename =~ s[\\][\\\\]g; # Windows...
$opt->spew_utf8(qq[#line $line "$filename"\r\n$name]);
}
if ( !$opt ) {
$c->fail('Failed to locate test source');
$c->release;
return ();
}
$aggs->{cflags} .= ' -I' . $Inc;
my $compiler = Affix::Build->new( debug => 0, name => 'testing', version => '1.0', flags => $aggs );
$compiler->add( $opt->canonpath );
$compiler->link;
push @cleanup, $opt->canonpath, $compiler->link unless $keep;
$c->ok( 1, 'build lib: ' . $compiler->link );
$c->release;
$compiler->link;
}
sub affix_ok ( $lib, $name, $args, $ret ) {
my $c = context;