Affix
view release on metacpan or search on metacpan
infix/src/jit/trampoline.c view on Meta::CPAN
/**
* Copyright (c) 2025 Sanko Robinson
*
* This source code is dual-licensed under the Artistic License 2.0 or the MIT License.
* You may choose to use this code under the terms of either license.
*
* 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 trampoline.c
* @brief The core JIT engine for generating forward and reverse trampolines.
* @ingroup internal_jit
*
* @details This module is the central orchestrator of the `infix` library. It brings
* together the type system, memory management, and ABI-specific logic to generate
* executable machine code at runtime.
*
* It implements both the high-level Signature API (e.g., `infix_forward_create`)
* and the low-level Manual API (e.g., `infix_forward_create_manual`). The high-level
* functions are convenient wrappers that use the signature parser to create the
* necessary `infix_type` objects before calling the core internal implementation.
*
* The core logic is encapsulated in `_infix_forward_create_internal` and
* `_infix_reverse_create_internal`. These functions follow a clear pipeline:
* 1. **Prepare:** Analyze the function signature with the appropriate ABI-specific
* `prepare_*_call_frame` function to create a layout blueprint.
* 2. **Generate:** Use the layout blueprint to call a sequence of ABI-specific
* `generate_*` functions, which emit machine code into a temporary `code_buffer`.
* 3. **Finalize:** Allocate executable memory, copy the generated code into it,
* create the final self-contained trampoline handle (deep-copying all type
* metadata), and make the code executable.
*/
#include "common/infix_internals.h"
#include "common/utility.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(INFIX_OS_MACOS)
#include <pthread.h>
#endif
#if defined(INFIX_OS_WINDOWS)
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
// Forward Declaration for Internal Creation Function
static infix_status _infix_reverse_create_internal(infix_reverse_t ** out_context,
infix_type * return_type,
infix_type ** arg_types,
size_t num_args,
size_t num_fixed_args,
void * user_callback_fn,
void * user_data,
bool is_callback);
// ABI Specification V-Table Declarations (extern to link to the specific implementations)
#if defined(INFIX_ABI_WINDOWS_X64)
extern const infix_forward_abi_spec g_win_x64_forward_spec;
extern const infix_reverse_abi_spec g_win_x64_reverse_spec;
extern const infix_direct_forward_abi_spec g_win_x64_direct_forward_spec;
#elif defined(INFIX_ABI_SYSV_X64)
extern const infix_forward_abi_spec g_sysv_x64_forward_spec;
extern const infix_reverse_abi_spec g_sysv_x64_reverse_spec;
extern const infix_direct_forward_abi_spec g_sysv_x64_direct_forward_spec;
#elif defined(INFIX_ABI_AAPCS64)
extern const infix_forward_abi_spec g_arm64_forward_spec;
extern const infix_reverse_abi_spec g_arm64_reverse_spec;
extern const infix_direct_forward_abi_spec g_arm64_direct_forward_spec;
#endif
/**
* @internal
* @brief Retrieves a pointer to the ABI specification v-table for forward calls.
* @details This function is the entry point to the ABI abstraction layer. It uses
* compile-time preprocessor macros (defined in `infix_config.h`) to select and
* return the correct v-table for the target platform.
* @return A pointer to the active `infix_forward_abi_spec`, or `nullptr` if the
* platform is unsupported.
*/
const infix_forward_abi_spec * get_current_forward_abi_spec() {
#if defined(INFIX_ABI_WINDOWS_X64)
return &g_win_x64_forward_spec;
#elif defined(INFIX_ABI_SYSV_X64)
return &g_sysv_x64_forward_spec;
infix/src/jit/trampoline.c view on Meta::CPAN
infix_arena_t * temp_arena = infix_arena_create(65536);
if (!temp_arena) {
_infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
return INFIX_ERROR_ALLOCATION_FAILED;
}
// --- Cache Lookup Stage ---
// Generate a canonical signature for deduplication.
char canonical_sig[8192];
infix_status sig_status = INFIX_SUCCESS;
{
// Construct a temporary argument array for the printer.
infix_function_argument * tmp_args = nullptr;
if (num_args > 0) {
tmp_args =
infix_arena_alloc(temp_arena, sizeof(infix_function_argument) * num_args, _Alignof(infix_type *));
if (!tmp_args) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
for (size_t i = 0; i < num_args; ++i) {
tmp_args[i].type = arg_types[i];
tmp_args[i].name = nullptr;
}
}
sig_status = infix_function_print(canonical_sig,
sizeof(canonical_sig),
"func",
return_type,
tmp_args,
num_args,
num_fixed_args,
INFIX_DIALECT_SIGNATURE);
}
if (sig_status == INFIX_SUCCESS) {
infix_forward_t * cached = _infix_cache_lookup(canonical_sig, target_fn, is_safe);
if (cached) {
*out_trampoline = cached;
status = INFIX_SUCCESS;
goto cleanup;
}
}
const infix_forward_abi_spec * spec = get_current_forward_abi_spec();
if (spec == nullptr) {
_infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNSUPPORTED_ABI, 0);
status = INFIX_ERROR_UNSUPPORTED_ABI;
goto cleanup;
}
code_buffer buf;
code_buffer_init(&buf, temp_arena);
// JIT Compilation Pipeline
// Prepare: Classify arguments and create the layout blueprint.
status = spec->prepare_forward_call_frame(
temp_arena, &layout, return_type, arg_types, num_args, num_fixed_args, target_fn);
if (status != INFIX_SUCCESS)
goto cleanup;
// 2. Generate: Emit machine code based on the layout.
status = spec->generate_forward_prologue(&buf, layout);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_forward_argument_moves(&buf, layout, arg_types, num_args, num_fixed_args);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_forward_call_instruction(&buf, layout);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_forward_epilogue(&buf, layout, return_type);
if (status != INFIX_SUCCESS)
goto cleanup;
if (buf.error || temp_arena->error) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
// Finalize Handle
handle = infix_calloc(1, sizeof(infix_forward_t));
if (handle == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
// "Estimate" stage: Calculate the exact size needed for the handle's private arena.
size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
if (target_arena) {
handle->arena = target_arena;
handle->is_external_arena = true;
}
else {
handle->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
handle->is_external_arena = false;
}
if (handle->arena == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
// "Copy" stage: Deep copy all type info into the handle's private arena.
handle->return_type = _copy_type_graph_to_arena(handle->arena, return_type);
if (num_args > 0) {
handle->arg_types = infix_arena_alloc(handle->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
if (handle->arg_types == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
for (size_t i = 0; i < num_args; ++i) {
handle->arg_types[i] = _copy_type_graph_to_arena(handle->arena, arg_types[i]);
// Check for allocation failure during copy
if (arg_types[i] != nullptr && handle->arg_types[i] == nullptr && !handle->arena->error) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
}
}
handle->num_args = num_args;
handle->num_fixed_args = num_fixed_args;
handle->target_fn = target_fn;
handle->is_safe = is_safe;
handle->ref_count = 1;
// Save the canonical signature for the cache key.
if (sig_status == INFIX_SUCCESS) {
size_t sig_len = strlen(canonical_sig) + 1;
handle->signature = infix_arena_alloc(handle->arena, sig_len, 1);
if (handle->signature)
infix_memcpy(handle->signature, canonical_sig, sig_len);
}
// Allocate and finalize executable memory.
handle->exec = infix_executable_alloc(buf.size);
if (handle->exec.rw_ptr == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
infix_memcpy(handle->exec.rw_ptr, buf.code, buf.size);
if (!infix_executable_make_executable(&handle->exec,
is_safe ? INFIX_EXECUTABLE_SAFE_FORWARD : INFIX_EXECUTABLE_FORWARD,
layout->prologue_size,
layout->epilogue_offset)) {
status = INFIX_ERROR_PROTECTION_FAILED;
goto cleanup;
}
infix_dump_hex(handle->exec.rx_ptr, handle->exec.size, "Forward Trampoline Machine Code");
*out_trampoline = handle;
// Cache the newly created trampoline.
// We ONLY cache if we own the arena. If the user provided an external arena,
// they control the lifetime, and we can't guarantee the signature string
// (which is in that arena) will remain valid as long as the cache entry exists.
if (handle->signature && !handle->is_external_arena)
_infix_cache_insert(handle);
cleanup:
// If any step failed, ensure the partially created handle is fully destroyed.
if (status != INFIX_SUCCESS && handle != nullptr)
infix_forward_destroy(handle);
// The temporary arena is always destroyed.
infix_arena_destroy(temp_arena);
return status;
}
/**
* @internal
* @brief The core implementation for creating a direct marshalling forward trampoline.
*
* This function orchestrates the JIT compilation pipeline for the direct marshalling
* feature. It is the internal counterpart to the public `infix_forward_create_direct`
* function and is called after the signature string has been parsed into a type graph.
*
* The pipeline is as follows:
* 1. Selects the appropriate `infix_direct_forward_abi_spec` v-table for the target platform.
* 2. Invokes `prepare_direct_forward_call_frame` to analyze the signature and handlers,
* producing a complete layout blueprint.
* 3. Calls the `generate_*` functions from the v-table in sequence. This emits machine code
* that includes direct calls to the user-provided marshaller and write-back functions.
* 4. Finalizes the `infix_forward_t` handle, marking it as a `is_direct_trampoline`.
* 5. Allocates executable memory, copies the generated code, and makes it executable.
*
* @param[out] out_trampoline Receives the created trampoline handle.
* @param[in] return_type The fully resolved return type.
* @param[in] arg_types An array of fully resolved argument types.
* @param[in] num_args Total number of arguments.
* @param[in] target_fn The target C function pointer.
* @param[in] handlers An array of handler structs provided by the user.
* @return `INFIX_SUCCESS` on success, or an error code on failure.
*/
static infix_status _infix_forward_create_direct_impl(infix_forward_t ** out_trampoline,
infix_type * return_type,
infix_type ** arg_types,
size_t num_args,
void * target_fn,
infix_direct_arg_handler_t * handlers) {
// Validation and Setup
if (!out_trampoline || !return_type || (!arg_types && num_args > 0) || !target_fn || !handlers) {
_infix_set_error(INFIX_CATEGORY_GENERAL, INFIX_CODE_NULL_POINTER, 0);
return INFIX_ERROR_INVALID_ARGUMENT;
}
const infix_direct_forward_abi_spec * spec = get_current_direct_forward_abi_spec();
if (spec == nullptr) {
_infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNSUPPORTED_ABI, 0);
return INFIX_ERROR_UNSUPPORTED_ABI;
}
infix_status status = INFIX_SUCCESS;
infix_direct_call_frame_layout * layout = nullptr;
infix_forward_t * handle = nullptr;
infix_arena_t * temp_arena = infix_arena_create(65536);
if (!temp_arena) {
_infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
return INFIX_ERROR_ALLOCATION_FAILED;
}
code_buffer buf;
code_buffer_init(&buf, temp_arena);
// 2. JIT Compilation Pipeline
status = spec->prepare_direct_forward_call_frame(
temp_arena, &layout, return_type, arg_types, num_args, handlers, target_fn);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_direct_forward_prologue(&buf, layout);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_direct_forward_argument_moves(&buf, layout);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_direct_forward_call_instruction(&buf, layout);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_direct_forward_epilogue(&buf, layout, return_type);
if (status != INFIX_SUCCESS)
goto cleanup;
if (buf.error || temp_arena->error) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
// 3. Finalize Handle
handle = infix_calloc(1, sizeof(infix_forward_t));
if (handle == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
handle->is_direct_trampoline = true; // Mark this as a direct marshalling trampoline.
size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
handle->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
handle->is_external_arena = false;
if (handle->arena == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
handle->return_type = _copy_type_graph_to_arena(handle->arena, return_type);
if (num_args > 0) {
handle->arg_types = infix_arena_alloc(handle->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
if (!handle->arg_types) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
for (size_t i = 0; i < num_args; ++i) {
handle->arg_types[i] = _copy_type_graph_to_arena(handle->arena, arg_types[i]);
if (arg_types[i] && !handle->arg_types[i] && !handle->arena->error) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
}
}
handle->num_args = num_args;
handle->num_fixed_args = num_args; // Direct trampolines are always fixed-arity.
handle->target_fn = target_fn;
handle->ref_count = 1;
// 4. Allocate and Finalize Executable Memory
handle->exec = infix_executable_alloc(buf.size);
if (handle->exec.rw_ptr == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
infix_memcpy(handle->exec.rw_ptr, buf.code, buf.size);
if (!infix_executable_make_executable(
&handle->exec, INFIX_EXECUTABLE_DIRECT, layout->prologue_size, layout->epilogue_offset)) {
status = INFIX_ERROR_PROTECTION_FAILED;
goto cleanup;
}
infix_dump_hex(handle->exec.rx_ptr, handle->exec.size, "Direct-Marshalling Forward Trampoline Machine Code");
*out_trampoline = handle;
cleanup:
if (status != INFIX_SUCCESS && handle != nullptr)
infix_forward_destroy(handle);
infix_arena_destroy(temp_arena);
return status;
}
/**
* @brief Creates a bound forward trampoline from `infix_type` objects (Manual API).
*
* @details This is the lower-level, programmatic way to create a bound forward trampoline.
* It bypasses the signature string parser, making it suitable for performance-critical
* applications or language bindings that construct type information dynamically.
*
* All `infix_type` objects passed to this function must be fully resolved and have
* a valid layout. They should be allocated from a user-managed `infix_arena_t`.
*
* @param[out] out_trampoline Receives the created trampoline handle.
* @param[in] return_type The `infix_type` for the function's return value.
* @param[in] arg_types An array of `infix_type*` for the function's arguments.
* @param[in] num_args The number of arguments.
* @param[in] num_fixed_args The number of non-variadic arguments.
* @param[in] target_function The address of the C function to call.
* @return `INFIX_SUCCESS` on success.
*/
INFIX_API c23_nodiscard infix_status infix_forward_create_manual(infix_forward_t ** out_trampoline,
infix_type * return_type,
infix_type ** arg_types,
size_t num_args,
size_t num_fixed_args,
void * target_function) {
// This is part of the "Manual API". It calls the internal implementation directly
// without involving the signature parser. `source_arena` is null because the
// types are assumed to be managed by the user.
_infix_clear_error();
return _infix_forward_create_impl(
out_trampoline, nullptr, return_type, arg_types, num_args, num_fixed_args, target_function, false);
}
/**
* @brief Creates an unbound forward trampoline from `infix_type` objects (Manual API).
*
* @details This is the lower-level, programmatic way to create an unbound forward trampoline.
* It bypasses the signature string parser.
*
* @param[out] out_trampoline Receives the created trampoline handle.
* @param[in] return_type The `infix_type` for the function's return value.
* @param[in] arg_types An array of `infix_type*` for the function's arguments.
* @param[in] num_args The number of arguments.
* @param[in] num_fixed_args The number of non-variadic arguments.
* @return `INFIX_SUCCESS` on success.
*/
INFIX_API c23_nodiscard infix_status infix_forward_create_unbound_manual(infix_forward_t ** out_trampoline,
infix_type * return_type,
infix_type ** arg_types,
infix/src/jit/trampoline.c view on Meta::CPAN
// Security Hardening: Allocate the context struct itself in special, page-aligned
// memory that can be made read-only after initialization.
size_t page_size = get_page_size();
size_t context_alloc_size = (sizeof(infix_reverse_t) + page_size - 1) & ~(page_size - 1);
prot = infix_protected_alloc(context_alloc_size);
if (prot.rw_ptr == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
context = (infix_reverse_t *)prot.rw_ptr;
infix_memset(context, 0, context_alloc_size);
// "Estimate" stage: Calculate the exact size needed for the context's private arena.
size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
// Create the context's private arena with the calculated size plus some headroom for safety.
context->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
if (context->arena == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
// Populate the context fields.
context->protected_ctx = prot;
context->num_args = num_args;
context->num_fixed_args = num_fixed_args;
context->is_variadic = (num_fixed_args < num_args);
context->user_callback_fn = user_callback_fn;
context->user_data = user_data;
context->internal_dispatcher = infix_internal_dispatch_callback_fn_impl;
context->cached_forward_trampoline = nullptr;
// "Copy" stage: deep copy all types into the context's private arena.
context->return_type = _copy_type_graph_to_arena(context->arena, return_type);
if (num_args > 0) {
context->arg_types = infix_arena_alloc(context->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
if (context->arg_types == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
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;
}
// JIT Compilation Pipeline for Reverse Stub
status = spec->prepare_reverse_call_frame(temp_arena, &layout, context);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_reverse_prologue(&buf, layout);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_reverse_argument_marshalling(&buf, layout, context);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_reverse_dispatcher_call(&buf, layout, context);
if (status != INFIX_SUCCESS)
goto cleanup;
status = spec->generate_reverse_epilogue(&buf, layout, context);
if (status != INFIX_SUCCESS)
goto cleanup;
// End of Pipeline
if (buf.error || temp_arena->error) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
context->exec = infix_executable_alloc(buf.size);
if (context->exec.rw_ptr == nullptr) {
status = INFIX_ERROR_ALLOCATION_FAILED;
goto cleanup;
}
infix_memcpy(context->exec.rw_ptr, buf.code, buf.size);
if (!infix_executable_make_executable(&context->exec, INFIX_EXECUTABLE_REVERSE, layout->prologue_size, 0)) {
status = INFIX_ERROR_PROTECTION_FAILED;
goto cleanup;
}
// Security Hardening: Make the context memory read-only to prevent runtime corruption.
if (!infix_protected_make_readonly(context->protected_ctx)) {
status = INFIX_ERROR_PROTECTION_FAILED;
goto cleanup;
}
infix_dump_hex(context->exec.rx_ptr, buf.size, "Reverse Trampoline Machine Code");
*out_context = context;
cleanup:
if (status != INFIX_SUCCESS) {
// If allocation of the context itself failed, prot.rw_ptr will be null.
if (prot.rw_ptr != nullptr)
infix_reverse_destroy(context);
}
infix_arena_destroy(temp_arena);
return status;
}
/**
* @brief Creates a type-safe reverse trampoline (callback) from `infix_type` objects (Manual API).
* @param[out] out_context Receives the created context handle.
* @param[in] return_type The function's return type.
* @param[in] arg_types An array of argument types.
* @param[in] num_args The number of arguments.
* @param[in] num_fixed_args The number of non-variadic arguments.
* @param[in] user_callback_fn A pointer to the type-safe C handler function.
* @return `INFIX_SUCCESS` on success.
*/
INFIX_API c23_nodiscard infix_status infix_reverse_create_callback_manual(infix_reverse_t ** out_context,
infix_type * return_type,
infix_type ** arg_types,
size_t num_args,
size_t num_fixed_args,
void * user_callback_fn) {
_infix_clear_error();
return _infix_reverse_create_internal(
out_context, return_type, arg_types, num_args, num_fixed_args, user_callback_fn, nullptr, true);
}
/**
* @brief Creates a generic reverse trampoline (closure) from `infix_type` objects (Manual API).
* @param[out] out_context Receives the created context handle.
* @param[in] return_type The function's return type.
* @param[in] arg_types An array of argument types.
* @param[in] num_args The number of arguments.
* @param[in] num_fixed_args The number of non-variadic arguments.
* @param[in] user_callback_fn A pointer to the generic `infix_closure_handler_fn`.
* @param[in] user_data A `void*` pointer to application-specific state.
* @return `INFIX_SUCCESS` on success.
*/
INFIX_API c23_nodiscard infix_status infix_reverse_create_closure_manual(infix_reverse_t ** out_context,
infix_type * return_type,
infix_type ** arg_types,
size_t num_args,
size_t num_fixed_args,
infix_closure_handler_fn user_callback_fn,
void * user_data) {
_infix_clear_error();
infix/src/jit/trampoline.c view on Meta::CPAN
const char * signature,
void * user_callback_fn,
infix_registry_t * registry) {
infix_arena_t * arena = nullptr;
infix_type * ret_type = nullptr;
infix_function_argument * args = nullptr;
size_t num_args = 0, num_fixed = 0;
infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
if (status != INFIX_SUCCESS) {
infix_arena_destroy(arena);
return status;
}
infix_type ** arg_types =
(num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *)) : nullptr;
if (num_args > 0 && !arg_types) {
infix_arena_destroy(arena);
_infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
return INFIX_ERROR_ALLOCATION_FAILED;
}
for (size_t i = 0; i < num_args; ++i)
arg_types[i] = args[i].type;
// Call the manual API with the parsed types.
status =
infix_reverse_create_callback_manual(out_context, ret_type, arg_types, num_args, num_fixed, user_callback_fn);
infix_arena_destroy(arena);
return status;
}
c23_nodiscard infix_status infix_reverse_create_closure(infix_reverse_t ** out_context,
const char * signature,
infix_closure_handler_fn user_callback_fn,
void * user_data,
infix_registry_t * registry) {
infix_arena_t * arena = nullptr;
infix_type * ret_type = nullptr;
infix_function_argument * args = nullptr;
size_t num_args = 0, num_fixed = 0;
infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
if (status != INFIX_SUCCESS) {
infix_arena_destroy(arena);
return status;
}
infix_type ** arg_types =
(num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *)) : nullptr;
if (num_args > 0 && !arg_types) {
infix_arena_destroy(arena);
_infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
return INFIX_ERROR_ALLOCATION_FAILED;
}
for (size_t i = 0; i < num_args; ++i)
arg_types[i] = args[i].type;
status = infix_reverse_create_closure_manual(
out_context, ret_type, arg_types, num_args, num_fixed, user_callback_fn, user_data);
infix_arena_destroy(arena);
return status;
}
// ============================================================================
// UNITY BUILD INCLUDES
// This section includes the actual ABI implementations at the end of the file.
// Because `trampoline.c` is the central translation unit, including the
// correct ABI-specific .c file here makes its functions (`g_win_x64_spec`, etc.)
// available without needing to add platform-specific logic to the build system.
// The `infix_config.h` header ensures only one of these #if blocks is active.
// ============================================================================
#if defined(INFIX_ABI_WINDOWS_X64)
#include "../arch/x64/abi_win_x64.c"
#include "../arch/x64/abi_x64_emitters.c"
#elif defined(INFIX_ABI_SYSV_X64)
#include "../arch/x64/abi_sysv_x64.c"
#include "../arch/x64/abi_x64_emitters.c"
#elif defined(INFIX_ABI_AAPCS64)
#include "../arch/aarch64/abi_arm64.c"
#include "../arch/aarch64/abi_arm64_emitters.c"
#else
#error "No supported ABI was selected for the unity build in trampoline.c."
#endif
( run in 1.296 second using v1.01-cache-2.11-cpan-97f6503c9c8 )