Affix

 view release on metacpan or  search on metacpan

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

    if (buf->error)
        return;
    if (len > SIZE_MAX - buf->size) {  // Overflow check
        buf->error = true;
        _infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_INTEGER_OVERFLOW, 0);
        return;
    }
    if (buf->size + len > buf->capacity) {
        size_t new_capacity = buf->capacity;
        while (new_capacity < buf->size + len) {
            if (new_capacity > SIZE_MAX / 2) {  // Overflow check
                buf->error = true;
                _infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_INTEGER_OVERFLOW, 0);
                return;
            }
            new_capacity *= 2;
        }
        void * new_code = infix_arena_alloc(buf->arena, new_capacity, 16);
        if (new_code == nullptr) {
            buf->error = true;
            // infix_arena_alloc already sets INFIX_CODE_OUT_OF_MEMORY, so we don't need to override it here
            return;
        }
        infix_memcpy(new_code, buf->code, buf->size);
        buf->code = new_code;
        buf->capacity = new_capacity;
    }
    infix_memcpy(buf->code + buf->size, data, len);
    buf->size += len;
}
/** @internal @brief Appends a single byte to the code buffer. */
void emit_byte(code_buffer * buf, uint8_t byte) { code_buffer_append(buf, &byte, 1); }
/** @internal @brief Appends a 32-bit integer (little-endian) to the code buffer. */
void emit_int32(code_buffer * buf, int32_t value) { code_buffer_append(buf, &value, 4); }
/** @internal @brief Appends a 64-bit integer (little-endian) to the code buffer. */
void emit_int64(code_buffer * buf, int64_t value) { code_buffer_append(buf, &value, 8); }
// Type Graph Validation
/** @internal A node for a visited list to detect cycles in `_is_type_graph_resolved_recursive`. */
typedef struct visited_node_t {
    const infix_type * type;
    struct visited_node_t * next;
} visited_node_t;
/**
 * @internal
 * @brief Recursively checks if a type graph is fully resolved (contains no named references).
 *
 * This is a critical pre-flight check before passing a type graph to the ABI
 * classification layer, which expects all types to have concrete size and
 * alignment information. An unresolved `@Name` node would cause it to fail.
 *
 * @param type The type to check.
 * @param visited_head A list to track visited nodes and prevent infinite recursion on cycles.
 * @return `true` if the graph is fully resolved, `false` otherwise.
 */
static bool _is_type_graph_resolved_recursive(const infix_type * type, visited_node_t * visited_head) {
    if (!type)
        return true;
    if (type->is_incomplete)
        return false;
    // Cycle detection: if we've seen this node before, we can assume it's resolved
    // for the purpose of this check, as we'll validate it on the first visit.
    for (visited_node_t * v = visited_head; v != NULL; v = v->next)
        if (v->type == type)
            return true;
    visited_node_t current_visited_node = {.type = type, .next = visited_head};
    switch (type->category) {
    case INFIX_TYPE_NAMED_REFERENCE:
        return false;  // Base case: an unresolved reference.
    case INFIX_TYPE_POINTER:
        return _is_type_graph_resolved_recursive(type->meta.pointer_info.pointee_type, &current_visited_node);
    case INFIX_TYPE_ARRAY:
        return _is_type_graph_resolved_recursive(type->meta.array_info.element_type, &current_visited_node);
    case INFIX_TYPE_STRUCT:
    case INFIX_TYPE_UNION:
        for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i)
            if (!_is_type_graph_resolved_recursive(type->meta.aggregate_info.members[i].type, &current_visited_node))
                return false;
        return true;
    case INFIX_TYPE_REVERSE_TRAMPOLINE:
        if (!_is_type_graph_resolved_recursive(type->meta.func_ptr_info.return_type, &current_visited_node))
            return false;
        for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i)
            if (!_is_type_graph_resolved_recursive(type->meta.func_ptr_info.args[i].type, &current_visited_node))
                return false;
        return true;
    default:
        return true;  // Primitives, void, etc., are always resolved.
    }
}
/**
 * @internal
 * @brief Public-internal wrapper for the recursive resolution check.
 */
static bool _is_type_graph_resolved(const infix_type * type) { return _is_type_graph_resolved_recursive(type, NULL); }
/**
 * @internal
 * @brief Estimates the memory required to store the type metadata for a function signature.
 *
 * This function iterates through the return and argument types, using the internal
 * _infix_estimate_graph_size utility to sum the required bytes for a deep copy
 * of all type information.
 *
 * @param temp_arena A temporary arena for the estimator's bookkeeping.
 * @param return_type The function's return type.
 * @param arg_types The array of argument types.
 * @param num_args The number of arguments.
 * @return The estimated size in bytes.
 */
static size_t _estimate_metadata_size(infix_arena_t * temp_arena,
                                      infix_type * return_type,
                                      infix_type ** arg_types,
                                      size_t num_args) {
    size_t total_size = 0;
    total_size += _infix_estimate_graph_size(temp_arena, return_type);
    if (arg_types != nullptr) {
        // Add space for the arg_types pointer array itself.
        total_size += sizeof(infix_type *) * num_args;
        for (size_t i = 0; i < num_args; ++i)
            total_size += _infix_estimate_graph_size(temp_arena, arg_types[i]);
    }
    return total_size;



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