Skip to content

Commit

Permalink
type ingress and egress basic checking
Browse files Browse the repository at this point in the history
  • Loading branch information
ityonemo committed May 3, 2024
1 parent 3921004 commit 217bc35
Show file tree
Hide file tree
Showing 17 changed files with 84 additions and 13 deletions.
11 changes: 9 additions & 2 deletions lib/zig/sema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,18 @@ defmodule Zig.Sema do
end

defp validate_nif!(nif) do
Enum.each(nif.params, &validate_param!/1)
Enum.each(nif.params, &validate_param!(&1, nif))
validate_return!(nif)
end

defp validate_param!(param), do: :ok
defp validate_param!({_, param}, nif) do
unless Type.param_allowed?(param.type) do
raise CompileError,
description: "functions cannot have #{Type.render_zig(param.type)} as a parameter",
file: nif.file,
line: nif.line
end
end

defp validate_return!(nif) do
unless Type.return_allowed?(nif.return.type) do
Expand Down
10 changes: 5 additions & 5 deletions lib/zig/templates/basic.zig.eex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<% needs_make? = @params |> Map.values |> Enum.any?(&Type.needs_make?/1) %>
<% 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 {
beam.context = .{
Expand All @@ -22,17 +22,17 @@ fn <%= @name %>(env: beam.env, argc: c_int, args: [*c] const e.ErlNifTerm) callc
var error_index: u8 = undefined;

const payload = beam.payload.build(nif.<%= @name %>, argc, args, &error_index, payload_opts) catch |err| {
<%= if @signature.arity == 0 do %>
<%= if needs_make? do %>
return e.enif_raise_exception(env, beam.make(.{err, error_index, error_info}, .{}).v);
<% else %>
_ = err;
unreachable;
<% else %>
return e.enif_raise_exception(env, beam.make(.{err, error_index, error_info}, .{}).v);
<% end %>
};

// defer beam.payload.cleanup(payload, cleanup_opts);

const result = @call(.auto, nif.<%= @name %>, payload) <%= Nif.maybe_catch(@signature.return) %>;
const result = @call(.auto, nif.<%= @name %>, payload) <%= Nif.maybe_catch(@return.type) %>;

<%= Return.render(@return) %>
};
Expand Down
10 changes: 10 additions & 0 deletions lib/zig/type.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ defprotocol Zig.Type do

# validations:

@spec param_allowed?(t) :: boolean
def param_allowed?(type)

@spec return_allowed?(t) :: boolean
def return_allowed?(type)

Expand Down Expand Up @@ -259,9 +262,16 @@ end
defimpl Zig.Type, for: Atom do
alias Zig.Type

def param_allowed?(type), do: type in ~w(term erl_nif_term pid)a
def return_allowed?(type), do: type in ~w(term erl_nif_term pid void)a
def can_cleanup?(_), do: false

def render_zig(:term), do: "beam.term"
def render_zig(:erl_nif_term), do: "e.erl_nif_term"
def render_zig(:pid), do: "beam.pid"
def render_zig(:env), do: "beam.env"
def render_zig(atom), do: "#{atom}"

def render_return(:void, _), do: "_ = result; break :result_block beam.make(.ok, .{}).v;"
def render_return(_, _), do: Type._default_return()

Expand Down
3 changes: 2 additions & 1 deletion lib/zig/type/array.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ defmodule Zig.Type.Array do
}
end

def param_allowed?(array), do: Type.param_allowed?(array.child)
def return_allowed?(array), do: Type.return_allowed?(array.child)
def can_cleanup?(_), do: false

def render_payload_options(type, index, _), do: Type._default_payload_options()
def render_payload_options(_, _, _), do: Type._default_payload_options()
def marshal_param(_, _, _, _), do: Type._default_marshal()
def marshal_return(_, _, _), do: Type._default_marshal()
def render_return(_, opts), do: Type._default_return(opts)
Expand Down
1 change: 1 addition & 0 deletions lib/zig/type/bool.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule Zig.Type.Bool do

def from_json(_), do: %__MODULE__{}

def param_allowed?(_), do: true
def return_allowed?(_), do: true
def can_cleanup?(_), do: false

Expand Down
1 change: 1 addition & 0 deletions lib/zig/type/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule Zig.Type.Enum do
~s(%Zig.Type.Enum{name: "#{enum.name}", tags: #{Kernel.inspect(enum.tags, opts)}})
end

def param_allowed?(_), do: true
def return_allowed?(_), do: true
def can_cleanup?(_), do: false

Expand Down
1 change: 1 addition & 0 deletions lib/zig/type/float.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ defmodule Zig.Type.Float do

def spec(_, _, _), do: Type.spec(:float)

def param_allowed?(_), do: true
def return_allowed?(_), do: true
def can_cleanup?(_), do: false

Expand Down
1 change: 1 addition & 0 deletions lib/zig/type/integer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ defmodule Zig.Type.Integer do
defp typemax(%{signedness: :signed, bits: 1}), do: 0
defp typemax(%{signedness: :signed, bits: bits}), do: Bitwise.<<<(1, bits - 1) - 1

def param_allowed?(_), do: true
def return_allowed?(_), do: true
def can_cleanup?(_), do: false

Expand Down
5 changes: 4 additions & 1 deletion lib/zig/type/manypointer.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Zig.Type.Manypointer do
alias Zig.Type
alias Zig.Type.Optional

use Type

import Type, only: :macros
Expand All @@ -25,6 +25,7 @@ defmodule Zig.Type.Manypointer do
}
end

def param_allowed?(pointer), do: Type.return_allowed?(pointer.child)
def return_allowed?(pointer), do: pointer.has_sentinel? and Type.return_allowed?(pointer.child)
def can_cleanup?(_), do: true

Expand All @@ -37,8 +38,10 @@ defmodule Zig.Type.Manypointer do
case type do
%{has_sentinel?: false} ->
"[*]#{Type.render_zig(type.child)}"

%{child: ~t(u8)} ->
"[*:0]u8"

%{child: %Optional{}} ->
"[*:null]#{Type.render_zig(type.child)}"
end
Expand Down
1 change: 1 addition & 0 deletions lib/zig/type/optional.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ defmodule Zig.Type.Optional do
def from_json(%{"child" => child}, module),
do: %__MODULE__{child: Type.from_json(child, module)}

def param_allowed?(optional), do: Type.param_allowed?(optional.child)
def return_allowed?(optional), do: Type.return_allowed?(optional.child)
def can_cleanup?(optional), do: Type.can_cleanup?(optional.child)

Expand Down
1 change: 1 addition & 0 deletions lib/zig/type/slice.ex
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ defmodule Zig.Type.Slice do

# ETC

def param_allowed?(slice), do: Type.param_allowed?(slice.child)
def return_allowed?(slice), do: Type.return_allowed?(slice.child)
def can_cleanup?(_), do: true

Expand Down
5 changes: 4 additions & 1 deletion lib/zig/type/struct.ex
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ defmodule Zig.Type.Struct do
|> Enum.sort()
end

# for now. Later, we will need to do more sophisticated checks
def param_allowed?(_), do: true

def return_allowed?(struct) do
struct.required
|> Map.values()
Expand All @@ -119,7 +122,7 @@ defmodule Zig.Type.Struct do

def can_cleanup?(_), do: false

def render_payload_options(type, index, _), do: Type._default_payload_options()
def render_payload_options(_, _, _), do: Type._default_payload_options()
def render_return(_, _), do: Type._default_return()
def marshal_param(_, _, _, _), do: Type._default_marshal()
def marshal_return(_, _, _), do: Type._default_marshal()
Expand Down
10 changes: 10 additions & 0 deletions test/integration/types/errors/beam_env_fails.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule ZiglerTest.Types.Errors.BeamEnvFails do
use Zig, otp_app: :zigler

~Z"""
const beam = @import("beam");
pub fn forbidden(env: beam.env) void {
_ = env;
}
"""
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule ZiglerTest.Types.ManypointerForbiddenOutput do
defmodule ZiglerTest.Types.Errors.ManypointerReturnFails do
use Zig, otp_app: :zigler

~Z"""
Expand Down
4 changes: 2 additions & 2 deletions test/integration/types/manypointer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ defmodule ZiglerTest.Types.ManypointerTest do
describe "for normal manypointers" do
test "having a return value is prohibited" do
assert_raise CompileError,
"test/integration/types/_manypointer_forbidden_output.exs:5: functions returning [*]u8 cannot be nifs",
"test/integration/types/errors/manypointer_return_fails.exs:5: functions returning [*]u8 cannot be nifs",
fn ->
Code.compile_file("_manypointer_forbidden_output.exs", __DIR__)
Code.compile_file("errors/manypointer_return_fails.exs", __DIR__)
end
end
end
Expand Down
13 changes: 13 additions & 0 deletions test/integration/types/other_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule ZiglerTest.Types.OtherTest do
use ZiglerTest.IntegrationCase, async: true

describe "beam.env" do
test "cannot generally be used as a parameter" do
assert_raise CompileError,
"test/integration/types/errors/beam_env_fails.exs:6: functions cannot have beam.env as a parameter",
fn ->
Code.compile_file("errors/beam_env_fails.exs", __DIR__)
end
end
end
end
18 changes: 18 additions & 0 deletions test/integration/types/term_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule ZiglerTest.Types.TermTest do
use ZiglerTest.IntegrationCase, async: true

use Zig, otp_app: :zigler

~Z"""
const beam = @import("beam");
pub fn term_test(term: beam.term) beam.term {
return term;
}
"""

describe "for a generic term" do
test "you can return a term" do
assert 1 == term_test(1)
end
end
end

0 comments on commit 217bc35

Please sign in to comment.