<% needs_make? = @params |> Map.values |> Enum.any?(&Type.needs_make?(&1.type)) %>

fn <%= @name %>(env: beam.env, argc: c_int, args: [*c] const e.ErlNifTerm) callconv(.C) e.ErlNifTerm {
    <%= cond do %>
    <% allocator = @allocator -> %>
    const allocator = nif.<%= allocator %>();
    <% @leak_check -> %>
    var gpa = beam.make_general_purpose_allocator_instance();
    const allocator = gpa.allocator(); 
    <% :else -> %>
    const allocator = beam.allocator;
    <% end %>

    beam.context = .{
        .env = env,
        .mode = .<%= context(@concurrency) %>,
        .allocator = allocator
    };

    <%= if needs_make? do %>
    var error_info = beam.make_empty_list(.{}); 
    <% end %>

    <%= for {index, param} <- @params, do: Parameter.render_accessory_variables(param, index) %>
 
    const return_value = execution_block: {
        const payload_opts = .{
            <%= for {index, param} <- @params, do: Parameter.render_payload_options(param, index) %>
        };

        var error_index: u8 = undefined;
        const payload = beam.payload.build(nif.<%= @alias || @name %>, argc, args, &error_index, payload_opts) catch |err| {
            if (err == error.OutOfMemory) return e.enif_raise_exception(env, beam.make(.{err, error_index}, .{}).v);

            <%= if needs_make? do %>
                return e.enif_raise_exception(env, beam.make(.{err, error_index, error_info}, .{}).v);
            <% else %>
                unreachable;
            <% end %> 
        };

        defer { 
            beam.payload.cleanup(payload, .{
            <%= for {index, param} <- @params, do: Parameter.render_cleanup(param, index) %>
            });
        }
    
        const result = @call(.auto, nif.<%= @alias || @name %>, payload) <%= Nif.maybe_catch(@return.type) %>;

        <%= Return.render_return(@return) %>
    };

    <%= if @leak_check do %>
    if (gpa.deinit() == .leak) {
        return e.enif_raise_exception(env, beam.make(.{
            .__struct__ = .@"Elixir.RuntimeError", 
            .message = "memory leak detected in function `<%= inspect @module %>.<%= @alias || @name %>/<%= map_size(@params) %>`", 
            .__exception__ = true
        }, .{}).v);
    }
    <% end %>

    return return_value;
}