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}]")
env = if Adapter.uses_env?(nif), do: "env", else: "_"
argv = if Adapter.uses_argv?(nif), do: "argv", else: "_"
head = """
export fn __#{nif.name}_shim__(#{env}: beam.env, _: 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 ->
{_used?, result_var} = Adapter.make_clause(other, result_var(nif))
"""
var #{result_var(nif)} = nosuspend #{function_call(nif, module)} return #{result_var};
}
"""
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