Affix

 view release on metacpan or  search on metacpan

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

    return _infix_forward_create_impl(
        out_trampoline, nullptr, return_type, arg_types, num_args, num_fixed_args, nullptr, false);
}
/**
 * @internal
 * @brief Internal implementation of forward trampoline destruction.
 */
void _infix_forward_destroy_internal(infix_forward_t * trampoline) {
    if (trampoline == nullptr)
        return;
    // Destroying the private arena frees all deep-copied type metadata and the signature string.
    if (trampoline->arena && !trampoline->is_external_arena)
        infix_arena_destroy(trampoline->arena);
    // Free the JIT-compiled executable code.
    infix_executable_free(trampoline->exec);
    // Free the handle struct itself.
    infix_free(trampoline);
}

/**
 * @brief Destroys a forward trampoline and frees all associated memory.
 * @details This function safely releases all resources owned by the trampoline,
 * including its JIT-compiled executable code and its private memory arena which
 * stores the deep-copied type information.
 * @param[in] trampoline The trampoline to destroy. Safe to call with `nullptr`.
 */
INFIX_API void infix_forward_destroy(infix_forward_t * trampoline) { _infix_cache_release(trampoline); }
// Reverse Trampoline API Implementation
/**
 * @internal
 * @brief Gets the system's memory page size in a portable way.
 * @return The page size in bytes.
 */
static size_t get_page_size() {
#if defined(INFIX_OS_WINDOWS)
    SYSTEM_INFO sysInfo;
    GetSystemInfo(&sysInfo);
    return sysInfo.dwPageSize;
#else
    // sysconf is the standard POSIX way to get system configuration values.
    return sysconf(_SC_PAGESIZE);
#endif
}
/**
 * @internal
 * @brief The core implementation for creating a reverse trampoline (callback or closure).
 *
 * @details This function orchestrates the JIT compilation pipeline for reverse calls.
 * It has a special `is_callback` flag that distinguishes between the two reverse
 * trampoline models:
 *
 * - **Type-safe Callback (`is_callback = true`):** In this model, the user provides a
 *   standard C function pointer with a matching signature. This function internally
 *   creates a *forward* trampoline (`cached_forward_trampoline`) that is used by the
 *   universal C dispatcher to call the user's handler in a type-safe way.
 *
 * - **Generic Closure (`is_callback = false`):** The user provides a generic handler of
 *   type `infix_closure_handler_fn`. The universal dispatcher calls this handler
 *   directly, without needing a cached forward trampoline.
 *
 * For security, the entire `infix_reverse_t` context struct is allocated in a
 * special page-aligned memory region that is made read-only after initialization.
 *
 * @param is_callback `true` to create a type-safe callback, `false` for a generic closure.
 * @return `INFIX_SUCCESS` on success.
 */
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) {
    if (out_context == nullptr || return_type == nullptr || num_fixed_args > num_args) {
        _infix_set_error(INFIX_CATEGORY_GENERAL, INFIX_CODE_NULL_POINTER, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    // Pre-flight check: ensure all types are fully resolved.
    if (!_is_type_graph_resolved(return_type)) {
        _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNRESOLVED_NAMED_TYPE, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    if (arg_types == nullptr && num_args > 0) {
        _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNRESOLVED_NAMED_TYPE, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    for (size_t i = 0; i < num_args; ++i) {
        if (arg_types[i] == nullptr || !_is_type_graph_resolved(arg_types[i])) {
            _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNRESOLVED_NAMED_TYPE, 0);
            return INFIX_ERROR_INVALID_ARGUMENT;
        }
    }
    const infix_reverse_abi_spec * spec = get_current_reverse_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_reverse_call_frame_layout * layout = nullptr;
    infix_reverse_t * context = nullptr;
    infix_arena_t * temp_arena = nullptr;
    infix_protected_t prot = {.rw_ptr = nullptr, .size = 0};
    code_buffer buf;
    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_init(&buf, temp_arena);
    // 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);

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

    return status;
}
INFIX_API c23_nodiscard infix_status infix_reverse_create_callback(infix_reverse_t ** out_context,
                                                                   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.783 second using v1.01-cache-2.11-cpan-39bf76dae61 )