Current section

Files

Jump to
zigler lib zig nif synchronous.ex
Raw

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, module) 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 = case nif.retval do
v when v in ["beam.term", "e.ErlNifTerm", "!beam.term", "!e.ErlNifTerm"] ->
"""
return nosuspend #{function_call nif, module}}
"""
v when v in ["void", "!void"] ->
"""
nosuspend #{function_call nif, module} return beam.make_ok(env);
}
"""
other ->
"""
var #{result_var nif} = nosuspend #{function_call nif, module} return #{Adapter.make_clause other, 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: "!" <> _}, module) do
"""
#{nif.name}(#{Adapter.args nif}) catch |err| {
return beam.raise_exception(env, "#{module}.ZigError", err, @errorReturnTrace());
};
"""
end
defp function_call(nif, _module), 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
body = quote context: Elixir do raise unquote(text) end
{:def, [context: Elixir, import: Kernel],
[
{name, [context: Elixir], args},
[do: body]
]}
end
end