view release on metacpan or search on metacpan
CONTRIBUTING.md view on Meta::CPAN
2. **Fork the Repository**: Start by forking the main repository to your own GitHub account.
3. **Create a Branch**: Create a new branch for your feature or bugfix from the `main` branch. Please use a descriptive name.
```bash
# For a new feature:
git checkout -b feature/add-riscv-support
# For a bug fix:
git checkout -b fix/struct-classification-bug
```
4. **Make Your Changes**: Write your code. Please adhere to the existing coding style and add comments to new or complex logic.
5. **Test Your Changes**: A pull request is far more likely to be accepted if it includes tests. If you add new functionality, please add a corresponding test case.
6. **Update the Changelog**: Add an entry to the `[Unreleased]` section of `CHANGELOG.md` describing your change.
7. **Submit a Pull Request**: Push your branch to your fork and open a pull request against the `main` branch of the original repository. Please provide a clear description of your changes and link to the relevant issue (e.g., `Fixes #123`).
### Added
- Float16 support
- Added the Float16 keyword to Affix.pm.
- Implemented float_to_half and half_to_float conversion logic in Affix.c (IEEE 754).
- Added optimized opcodes (OP_PUSH_FLOAT16, OP_RET_FLOAT16) to the internal VM dispatcher for high-performance marshalling.
- Bitfield Support:
- Enhanced Struct [...] syntax in Affix.pm to support bitfield widths (e.g., a => UInt32, 3).
- Implemented bitmask-based marshalling in Affix.c to correctly pack and unpack C-style bitfields within structs.
- 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.
## 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, ...);
infix/src/common/compat_c23.h view on Meta::CPAN
*
* @internal
*/
#pragma once
#include "common/infix_config.h"
#include <infix/infix.h>
#include <stdbool.h>
#include <stddef.h>
/**
* @def nullptr
* @brief Defines `nullptr` as a standard C-style null pointer constant (`(void*)0`).
*
* @details This provides a consistent null pointer constant across C and C++
* compilation environments. While `NULL` is standard in C, `nullptr` is preferred
* in modern C++ and is being adopted in newer C standards. This macro ensures
* the codebase can use `nullptr` consistently without causing compilation errors
* in a strict C11/C17 environment.
*/
#if !defined(nullptr) && !defined(__cplusplus)
#define nullptr ((void *)0)
#endif
infix/src/common/double_tap.h view on Meta::CPAN
#endif
#if defined(__GNUC__) || defined(__clang__)
#define DBLTAP_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define DBLTAP_NOINLINE __declspec(noinline)
#else
#define DBLTAP_NOINLINE
#endif
// Compiler-specific attribute for printf-style format checking.
#if defined(__GNUC__) || defined(__clang__)
#define DBLTAP_PRINTF_FORMAT(fmt_index, arg_index) __attribute__((format(printf, fmt_index, arg_index)))
#else
#define DBLTAP_PRINTF_FORMAT(fmt_index, arg_index)
#endif
// Public Test Harness Functions (wrapped by macros for convenience)
void tap_init(void);
void tap_plan(size_t count);
int tap_done(void);
infix/src/core/error.c view on Meta::CPAN
* @param code The specific error code.
* @param position For parser errors, the byte offset into the signature string where the error occurred.
*/
void _infix_set_error(infix_error_category_t category, infix_error_code_t code, size_t position) {
g_infix_last_error.category = category;
g_infix_last_error.code = code;
g_infix_last_error.position = position;
g_infix_last_error.system_error_code = 0;
// Check if we can generate a rich parser error message.
if (category == INFIX_CATEGORY_PARSER && g_infix_last_signature_context != nullptr) {
// Generate a rich, GCC-style error message for parser failures.
const char * signature = g_infix_last_signature_context;
size_t sig_len = strlen(signature);
const size_t radius = 20; // Number of characters to show around the error position.
// Calculate the start and end of the snippet to display.
size_t start = (position > radius) ? (position - radius) : 0;
size_t end = (position + radius < sig_len) ? (position + radius) : sig_len;
// Add indicators if the snippet is truncated.
const char * start_indicator = (start > 0) ? "... " : "";
const char * end_indicator = (end < sig_len) ? " ..." : "";
size_t start_indicator_len = (start > 0) ? 4 : 0;
infix/src/core/signature.c view on Meta::CPAN
* @param[in,out] state The current parser state.
* @param[in] code The error code to set.
*/
INFIX_INTERNAL void _infix_set_parser_error(parser_state * state, infix_error_code_t code) {
_infix_set_error(INFIX_CATEGORY_PARSER, code, (size_t)(state->p - state->start));
}
INFIX_INTERNAL void skip_whitespace(parser_state * state);
/**
* @internal
* @brief Advances the parser's cursor past any whitespace or C-style line comments.
* @param[in,out] state The parser state to modify.
*/
INFIX_INTERNAL void skip_whitespace(parser_state * state) {
while (true) {
while (isspace((unsigned char)*state->p))
state->p++;
if (*state->p == '#') // C-style line comments
while (*state->p != '\n' && *state->p != '\0')
state->p++;
else
break;
}
}
/**
* @internal
* @brief Parses an unsigned integer from the string, used for array/vector sizes.
* @param[in,out] state The parser state.
infix/src/core/signature.c view on Meta::CPAN
if (val == (unsigned long long)SIZE_MAX) {
_infix_set_parser_error(state, INFIX_CODE_INTEGER_OVERFLOW);
return false;
}
*out_val = (size_t)val;
state->p = end;
return true;
}
/**
* @internal
* @brief Parses a C-style identifier from the string.
* @details This is used for member names, named types, and function argument names.
* It handles simple identifiers (`my_var`) and C++-style namespaces (`NS::Name`).
* @param[in,out] state The parser state.
* @return An arena-allocated string for the identifier, or `nullptr` on failure.
*/
static const char * parse_identifier(parser_state * state) {
skip_whitespace(state);
const char * start = state->p;
if (!isalpha((unsigned char)*start) && *start != '_')
return nullptr;
while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
if (*state->p == ':' && state->p[1] != ':')
infix/src/core/signature.c view on Meta::CPAN
for (size_t i = 0; i < num_members; ++i)
total_size += members[i].type->size;
infix_status status =
infix_type_create_packed_struct(state->arena, &packed_type, total_size, alignment, members, num_members);
if (status != INFIX_SUCCESS)
return nullptr;
return packed_type;
}
/**
* @internal
* @brief Helper to preserve the semantic name of C-style primitive aliases.
* @param state The parser's state.
* @param id The primitive type id.
* @param name The string the user used to describe this type.
*/
static infix_type * _create_named_primitive(parser_state * state, infix_primitive_type_id id, const char * name) {
infix_type * t = infix_arena_calloc(state->arena, 1, sizeof(infix_type), _Alignof(infix_type));
if (!t)
return nullptr;
*t = *infix_type_create_primitive(id);
infix/src/core/signature.c view on Meta::CPAN
if (consume_keyword(state, "float16"))
return infix_type_create_primitive(INFIX_PRIMITIVE_FLOAT16);
if (consume_keyword(state, "float32"))
return infix_type_create_primitive(INFIX_PRIMITIVE_FLOAT);
if (consume_keyword(state, "float64"))
return infix_type_create_primitive(INFIX_PRIMITIVE_DOUBLE);
if (consume_keyword(state, "bool"))
return infix_type_create_primitive(INFIX_PRIMITIVE_BOOL);
if (consume_keyword(state, "void"))
return infix_type_create_void();
// C-style convenience aliases
if (consume_keyword(state, "uchar"))
return infix_type_create_primitive(INFIX_PRIMITIVE_UINT8);
if (consume_keyword(state, "char"))
return infix_type_create_primitive(INFIX_PRIMITIVE_SINT8);
if (consume_keyword(state, "ushort"))
return infix_type_create_primitive(INFIX_PRIMITIVE_UINT16);
if (consume_keyword(state, "short"))
return infix_type_create_primitive(INFIX_PRIMITIVE_SINT16);
if (consume_keyword(state, "uint"))
return infix_type_create_primitive(INFIX_PRIMITIVE_UINT32);
infix/src/core/signature.c view on Meta::CPAN
// MSVC mangling state
const infix_type * msvc_types[10]; /**< First 10 encountered types for back-referencing. */
size_t msvc_type_count; /**< Number of types encountered. */
size_t depth; /**< Current recursion depth, to bound cyclic type graphs. */
} printer_state;
/**
* @internal
* @brief A safe `vsnprintf` wrapper for building the signature string.
* Updates the printer state and sets an error on buffer overflow.
* @param[in,out] state The printer state.
* @param[in] fmt The `printf`-style format string.
* @param[in] ... Arguments for the format string.
*/
static void _print(printer_state * state, const char * fmt, ...) {
if (state->status != INFIX_SUCCESS)
return;
va_list args;
va_start(args, fmt);
int written = vsnprintf(state->p, state->remaining, fmt, args);
va_end(args);
if (written < 0 || (size_t)written >= state->remaining)
infix/src/core/type_registry.c view on Meta::CPAN
* @details This is distinct from the main signature `parser_state` because the
* grammar for definitions (`@Name=...;`) is much simpler and can be handled
* with a simpler, non-recursive parser.
*/
typedef struct {
const char * p; /**< The current position in the definition string. */
const char * start; /**< The start of the definition string for error reporting. */
} _registry_parser_state_t;
/**
* @internal
* @brief Skips whitespace and C++-style line comments in a definition string.
* @param state The parser state to advance.
*/
static void _registry_parser_skip_whitespace(_registry_parser_state_t * state) {
while (1) {
while (isspace((unsigned char)*state->p))
state->p++;
if (*state->p == '#') // Skip comments
while (*state->p != '\n' && *state->p != '\0')
state->p++;
else
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/Wrap.pm view on Meta::CPAN
return undef unless defined $expr;
1 while $expr =~ s/^\((.*)\)$/$1/; # Strip outer parens
# Resolve bitwise and arithmetic expressions
if ( $expr =~ /[|&<>+\-*\/]/ ) {
my $evaluable = $expr;
# Using {} delimiters so the // operator doesn't break the regex parser
$evaluable =~ s{\b([a-zA-Z_]\w*)\b}{ $resolve->($1) // $1 }ge;
# Clean up any C-style suffixes that might have survived
$evaluable =~ s/\b(\d+)L+\b/$1/g;
# If everything is now numeric/operators/whitespace, we can safely eval
if ( $evaluable =~ /^[0-9\s|&<>\+\-\*\/\(\).xXa-fA-F]+$/ ) {
# Using a string eval here to let Perl's engine handle C-like precedence
my $res = eval $evaluable;
return $cache{$token} = $res if defined $res;
}
}
t/025_affix_wrap.t view on Meta::CPAN
[ 'Evil::system("echo PWNED")', 'parens in name' ],
[ 'Evil->pkg', 'arrow instead of ::' ],
);
for my $t (@borderline) {
my ( $bad, $desc ) = @$t;
ok dies { $binder->generate( 'lib', $bad, '/dev/null' ) }, "rejects $desc: $bad";
}
};
# $lib with backslashes (Windows paths) must be handled
subtest 'Windows-style lib paths' => sub {
my $win_lib = 'C:\Users\Test\lib.dll';
my $pm_file = $dir->child('winpath.pm');
lives { $binder->generate( $win_lib, 'WinPathTest', $pm_file->stringify ) }
or bail_out 'generate() died on Windows path';
my $content = $pm_file->slurp_utf8;
# Backslashes are doubled when escaped for q[...] (\\ -> \\\\)
like $content, qr/q\[.*C:\\\\Users\\\\Test\\\\lib\.dll\]/, 'Windows path safely quoted';
my ( undef, undef, $exit ) = capture { system $^X, '-Ilib', '-c', $pm_file->stringify };
is $exit >> 8, 0, 'Generated code compiles with Windows path';
t/035_magic_struct.t view on Meta::CPAN
# $root goes out of scope here.
# In the new system, $element_ref's magic keeps the root alive.
}
# ASSERT: We can still read and write to the element
is $$element_ref, 12345, 'Magical element SV kept root memory alive';
$$element_ref = 54321;
is $$element_ref, 54321, 'Magical element SV still writable after root object scope ended';
};
subtest 'Out of bounds (C-style)' => sub {
# In C, pointers don't have bounds. Our magical system should behave like C.
# To prevent ACTUAL heap corruption (which crashes the Perl allocator
# during global destruction), we allocate enough backing memory here.
my $ptr = Affix::malloc( 15 * sizeof(Int) );
$ptr = cast( $ptr, Pointer [Int] );
# This is "dangerous" but should work without a Perl exception
# because it's just pointer arithmetic.
#~ ok lives { $ptr->[10] = 0 }, 'Accessing out-of-bounds index does not throw Perl exception';
t/070_security_fixes.t view on Meta::CPAN
# We can't easily test the regex directly since \Q is consumed by qr//,
# but we CAN test that malicious library names don't produce false matches.
# Verify the regex is compiled (won't die when constructed with evil name)
my $evil_name = q[m; rm -rf /];
my $regex = qr[-l\Q$evil_name\E\.[^\s]+.+\s*=>\s*(.+)$];
# The escaped name should NOT match a legitimate ldconfig output line
unlike '-lm.1 => /lib/libm.so.1', $regex, 'Evil name does not match legitimate ldconfig line';
# A safe name should match ldconfig-style output
my $safe_name = 'm';
my $safe_regex = qr[-l\Q$safe_name\E\.[^\s]+.+\s*=>\s*(.+)$];
like '-lm.1 => /lib/libm.so.1', $safe_regex, 'Safe name matches ldconfig output format';
};
#
subtest 'H7/H8: Macro name validation prevents symbol table injection' => sub {
use Affix::Wrap;
# Valid macro
my $valid = Affix::Wrap::Macro->new( name => 'SAFE_CONST', value => '42' );