Current section
Files
Jump to
Current section
Files
lib/zig/nif/synchronous.ex
defmodule Zig.Nif.Synchronous do
@moduledoc """
Adapter code for synchronous nifs
"""
alias Zig.Nif.Adapter
alias Zig.Typespec
@behaviour Adapter
@impl true
def zig_adapter(nif) do
get_clauses = Adapter.get_clauses(nif, &bail/1, &"argv[#{&1}]")
head = """
export fn __#{nif.name}_shim__(env: beam.env, argc: c_int, argv: [*c] const beam.term) beam.term {
beam.yield_info = null;
"""
result = cond do
nif.retval in ["beam.term", "e.ErlNifTerm", "!beam.term", "!e.ErlNifTerm"] ->
"""
return nosuspend #{function_call nif}}
"""
nif.retval in ["void", "!void"] ->
"""
nosuspend #{function_call nif} return beam.make_nil(env);
}
"""
true ->
"""
var #{result_var nif} = nosuspend #{function_call nif} return #{Adapter.make_clause nif.retval, result_var(nif)};
}
"""
end
[head, "\n", get_clauses, result, "\n"]
end
defp bail(:oom), do: "return beam.raise_enomem(env)"
defp bail(:function_clause), do: "return beam.raise_function_clause_error(env)"
defp result_var(nif), do: "__#{nif.name}_result__"
# error return calls.
defp function_call(nif = %{retval: "!" <> _}) do
"""
#{nif.name}(#{Adapter.args nif}) catch {
return beam.make_error_term(env, beam.make_error_return_trace(env, @errorReturnTrace()));
};
"""
end
defp function_call(nif), do: "#{nif.name}(#{Adapter.args nif});\n"
@impl true
def nif_table_entries(nif) do
"""
e.ErlNifFunc{
.name = "#{nif.name}",
.arity = #{nif.arity},
.fptr = __#{nif.name}_shim__,
.flags = 0,
},
"""
end
@impl true
def beam_adapter(nif) do
typespec = Typespec.from_nif(nif)
quote do
unquote(typespec)
unquote(basic_fn(nif))
end
end
defp basic_fn(%{name: name, arity: arity}) do
text = "nif for function #{name}/#{arity} not bound"
args = if arity == 0 do
Elixir
else
for _ <- 1..arity, do: {:_, [], Elixir}
end
{:def, [context: Elixir, import: Kernel],
[
{name, [context: Elixir], args},
[do: {:raise, [context: Elixir, import: Kernel], [text]}]
]}
end
end