<% 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_debug_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;
}
