From 723c60479a24e7485c027cbe376480b7c7d0b27b Mon Sep 17 00:00:00 2001 From: isaac yonemoito Date: Fri, 10 May 2024 11:29:05 -0500 Subject: [PATCH] gets basic resource and basic cleanup up and running --- lib/zig/compiler.ex | 25 +++-- lib/zig/type.ex | 3 + lib/zig/type/resource.ex | 5 + mix.exs | 4 +- mix.lock | 18 ++-- test/integration/resource/basic_test.exs | 115 ++++++++++---------- test/integration/resource/cleanup_test.exs | 117 ++++++++++----------- 7 files changed, 148 insertions(+), 139 deletions(-) diff --git a/lib/zig/compiler.ex b/lib/zig/compiler.ex index 95dcb9e3..51ad6ac3 100644 --- a/lib/zig/compiler.ex +++ b/lib/zig/compiler.ex @@ -35,23 +35,30 @@ defmodule Zig.Compiler do end defp adjust_elixir_options(opts) do - Map.update!(opts, :nifs, &replace_nif_dots/1) + Map.update!(opts, :nifs, &nif_substitution/1) end # if the elixir `nif` option contains `...` then this should be converted - # into `{:auto, }`. This function will reverse the list, but - # since order doesn't matter for this option, it is okay. - defp replace_nif_dots({:auto, _} = auto), do: auto - - defp replace_nif_dots(opts) do + # into `{:auto, }`. Also, if the nif entry is just an atom, + # converts that entry into `{nif, []}` + # + # This function will reverse the list, but since order doesn't matter for this + # option, it is okay. + defp nif_substitution({:auto, _} = auto), do: auto + + defp nif_substitution(opts) do Enum.reduce(opts, [], fn - {:..., _, _}, {:auto, list} -> {:auto, list} + {:..., _, _}, {:auto, list} = so_far -> so_far {:..., _, _}, list -> {:auto, list} - other, {:auto, list} -> {:auto, [other | list]} - other, list -> [other | list] + other, so_far -> prepend_nif(so_far, other) end) end + defp prepend_nif({:auto, so_far}, nif_name) when is_atom(nif_name), do: {:auto, [{nif_name, []} | so_far]} + defp prepend_nif(so_far, nif_name) when is_atom(nif_name), do: [{nif_name, []} | so_far] + defp prepend_nif({:auto, so_far}, nif_info), do: {:auto, [nif_info | so_far]} + defp prepend_nif(so_far, nif_info), do: [nif_info | so_far] + # note that this function is made public so that it can be both accessed # from the :zigler entrypoint for erlang parse transforms, as well as the # __before_compile__ entrypoint for Elixir diff --git a/lib/zig/type.ex b/lib/zig/type.ex index fd5d7058..255f709a 100644 --- a/lib/zig/type.ex +++ b/lib/zig/type.ex @@ -290,4 +290,7 @@ defimpl Zig.Type, for: Atom do term() end end + + def marshal_param(_, _, _, _), do: Type._default_marshal() + def marshal_return(_, _, _), do: Type._default_marshal() end diff --git a/lib/zig/type/resource.ex b/lib/zig/type/resource.ex index 1cb10992..86798915 100644 --- a/lib/zig/type/resource.ex +++ b/lib/zig/type/resource.ex @@ -32,4 +32,9 @@ defmodule Zig.Type.Resource do def return_allowed?(_resource), do: true def param_allowed?(_), do: true + + 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() end diff --git a/mix.exs b/mix.exs index 97627db3..2f117e69 100644 --- a/mix.exs +++ b/mix.exs @@ -102,12 +102,12 @@ defmodule Zigler.MixProject do {:jason, "~> 1.4"}, # zig parser is pinned to a version of zig parser because versions of zig parser # are pinned to zig versions - {:zig_parser, "~> 0.3.0"}, + {:zig_parser, "~> 0.4.0"}, # utility to help manage type protocols {:protoss, "~> 0.1.1"}, # documentation {:ex_doc, "~> 0.30.0", only: :dev, runtime: false}, - {:zig_doc, "~> 0.3.1", only: :dev, runtime: false} + #{:zig_doc, "~> 0.3.1", only: :dev, runtime: false} ] end end diff --git a/mix.lock b/mix.lock index ba548a0e..57e33c67 100644 --- a/mix.lock +++ b/mix.lock @@ -1,17 +1,17 @@ %{ - "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, - "credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"}, + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "credo": {:hex, :credo, "1.7.6", "b8f14011a5443f2839b04def0b252300842ce7388f3af177157c86da18dfbeea", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "146f347fb9f8cbc5f7e39e3f22f70acbef51d441baa6d10169dd604bfbc55296"}, "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm", "6c32a70ed5d452c6650916555b1f96c79af5fc4bf286997f8b15f213de786f73"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.37", "2ad73550e27c8946648b06905a57e4d454e4d7229c2dafa72a0348c99d8be5f7", [:mix], [], "hexpm", "6b19783f2802f039806f375610faa22da130b8edc21209d0bff47918bb48360e"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"}, - "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, - "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, + "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "pegasus": {:hex, :pegasus, "0.2.4", "3d8d5a2c89552face9c7ca14f959cc6c6d2cd645db1df85940db4c77c3b21a24", [:mix], [{:nimble_parsec, "~> 1.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2d21e2b6b946fe3cd441544bf9856e7772f29050332f0255e166a13cdbe65bb4"}, "protoss": {:hex, :protoss, "0.1.1", "c698bc4e08b5fc01fc23fc61ed44fd9fbf4536743748e28fa8df408284e14c2f", [:mix], [], "hexpm", "56abeda35c04d56a1419486b3c8f32e8f5ec3fd296668b13418002e8c509910f"}, "zig_doc": {:hex, :zig_doc, "0.3.1", "62db35ee8da67f2835f8229c91762836a0fe06d1a88e53fbfa7764e0e6d56d59", [:mix], [{:ex_doc, "~> 0.30.9", [hex: :ex_doc, repo: "hexpm", optional: false]}, {:zig_parser, "~> 0.3.0", [hex: :zig_parser, repo: "hexpm", optional: false]}], "hexpm", "566bd4375536e81f6157811d875cb413b3b73eb52b12bbc162291085e63f7091"}, - "zig_parser": {:hex, :zig_parser, "0.3.0", "2a597ae6990447e70e46691d9ca7073afb3c3e13ab143dce1df0865a764c48f4", [:mix], [{:pegasus, "~> 0.2.4", [hex: :pegasus, repo: "hexpm", optional: false]}], "hexpm", "6b90ee53cf23e53824dbdad2d2ca597a1cb8d882b0748638921bddda1563a736"}, + "zig_parser": {:hex, :zig_parser, "0.4.0", "5230576fcea30c061f08f6053448ad3dc5194a45485065564a7f8047bb351ce9", [:mix], [{:pegasus, "~> 0.2.4", [hex: :pegasus, repo: "hexpm", optional: false]}], "hexpm", "ec54cf14e80a1485e29a80b42756d0421426db81eb9e2630721fd46ab5c21bcb"}, } diff --git a/test/integration/resource/basic_test.exs b/test/integration/resource/basic_test.exs index 37005acc..0480a40a 100644 --- a/test/integration/resource/basic_test.exs +++ b/test/integration/resource/basic_test.exs @@ -1,63 +1,60 @@ defmodule ZiglerTest.Resource.BasicTest do use ZiglerTest.IntegrationCase, async: true - @moduletag :resource - test "restore" - -# use Zig, otp_app: :zigler, resources: [:StructResource, :U64Resource] -# -# ~Z""" -# const beam = @import("beam"); -# const Resource = beam.Resource; -# const root = @import("root"); -# -# pub const Struct = struct { -# payload: u64, -# }; -# -# pub const StructResource = Resource(Struct, root, .{}); -# pub const U64Resource = Resource(u64, root, .{}); -# -# pub fn new_scalar(resource: u64) U64Resource { -# return U64Resource.create(resource, .{}) catch unreachable; -# } -# -# pub fn unpack_scalar(resource: U64Resource) u64 { -# return resource.unpack(); -# } -# -# pub fn increment_scalar(resource: U64Resource) void { -# const to_increment = resource.unpack(); -# return resource.update(to_increment + 1); -# } -# -# pub fn new_struct(s: Struct) StructResource { -# return StructResource.create(s, .{}) catch unreachable; -# } -# -# pub fn unpack_struct(resource: StructResource) Struct { -# return resource.unpack(); -# } -# -# pub fn increment_struct(resource: StructResource) void { -# const to_increment_struct = resource.unpack(); -# return resource.update(.{.payload = to_increment_struct.payload + 1}); -# } -# """ -# -# test "basic scalar resources work" do -# res = new_scalar(47) -# assert is_reference(res) -# assert 47 = unpack_scalar(res) -# increment_scalar(res) -# assert 48 = unpack_scalar(res) -# end -# -# test "basic struct resources work" do -# res = new_struct(%{payload: 47}) -# assert is_reference(res) -# assert %{payload: 47} = unpack_struct(res) -# increment_struct(res) -# assert %{payload: 48} = unpack_struct(res) -# end + use Zig, otp_app: :zigler, resources: [:StructResource, :U64Resource] + + ~Z""" + const beam = @import("beam"); + const Resource = beam.Resource; + const root = @import("root"); + + pub const Struct = struct { + payload: u64, + }; + + pub const StructResource = Resource(Struct, root, .{}); + pub const U64Resource = Resource(u64, root, .{}); + + pub fn new_scalar(resource: u64) U64Resource { + return U64Resource.create(resource, .{}) catch unreachable; + } + + pub fn unpack_scalar(resource: U64Resource) u64 { + return resource.unpack(); + } + + pub fn increment_scalar(resource: U64Resource) void { + const to_increment = resource.unpack(); + return resource.update(to_increment + 1); + } + + pub fn new_struct(s: Struct) StructResource { + return StructResource.create(s, .{}) catch unreachable; + } + + pub fn unpack_struct(resource: StructResource) Struct { + return resource.unpack(); + } + + pub fn increment_struct(resource: StructResource) void { + const to_increment_struct = resource.unpack(); + return resource.update(.{.payload = to_increment_struct.payload + 1}); + } + """ + + test "basic scalar resources work" do + res = new_scalar(47) + assert is_reference(res) + assert 47 = unpack_scalar(res) + increment_scalar(res) + assert 48 = unpack_scalar(res) + end + + test "basic struct resources work" do + res = new_struct(%{payload: 47}) + assert is_reference(res) + assert %{payload: 47} = unpack_struct(res) + increment_struct(res) + assert %{payload: 48} = unpack_struct(res) + end end diff --git a/test/integration/resource/cleanup_test.exs b/test/integration/resource/cleanup_test.exs index c1084d00..0cc97399 100644 --- a/test/integration/resource/cleanup_test.exs +++ b/test/integration/resource/cleanup_test.exs @@ -1,63 +1,60 @@ defmodule ZiglerTest.Resource.CleanupTest do use ZiglerTest.IntegrationCase, async: true - - @moduletag :resource - test "restore" - #use Zig, - # otp_app: :zigler, - # resources: [:PidResource], - # nifs: [ - # :create_released, - # maybe_release: [args: [[cleanup: false], []]] - # ] -# - #~Z""" - #const beam = @import("beam"); - #const std = @import("std"); - #const Resource = beam.Resource; -# - #pub const PidResource = Resource(beam.pid, @import("root"), .{.Callbacks = PidResourceCallbacks}); -# - #pub const PidResourceCallbacks = struct { - # pub fn dtor(pid: *beam.pid) void { - # _ = beam.send(pid.*, .cleaned, .{.clean = false}) catch unreachable; - # } - #}; -# - #pub fn create_released(pid: beam.pid) PidResource { - # const resource = PidResource.create(pid, .{}) catch unreachable; - # return resource; - #} -# - #pub fn maybe_release(resource: PidResource, release: bool) void { - # if (release) { - # resource.release(); - # } - #} - #""" -# - #test "a function call will keep resources, with no cleanup it doesn't release" do - # this = self() -# - # spawn(fn -> - # this - # |> create_released() - # |> maybe_release(false) - # end) -# - # refute_receive :cleaned, 500 - #end -# - #test "a function call will keep resources, you can manually release" do - # this = self() -# - # spawn(fn -> - # this - # |> create_released() - # |> maybe_release(true) - # end) -# - # assert_receive :cleaned, 100 - #end -end + use Zig, + otp_app: :zigler, + resources: [:PidResource], + nifs: [ + :create_released, + maybe_release: [args: [[cleanup: false], []]] + ] + + ~Z""" + const beam = @import("beam"); + const std = @import("std"); + const Resource = beam.Resource; + + pub const PidResource = Resource(beam.pid, @import("root"), .{.Callbacks = PidResourceCallbacks}); + + pub const PidResourceCallbacks = struct { + pub fn dtor(pid: *beam.pid) void { + _ = beam.send(pid.*, .cleaned, .{.clean = false}) catch unreachable; + } + }; + + pub fn create_released(pid: beam.pid) PidResource { + const resource = PidResource.create(pid, .{}) catch unreachable; + return resource; + } + + pub fn maybe_release(resource: PidResource, release: bool) void { + if (release) { + resource.release(); + } + } + """ + + test "a function call will keep resources, with no cleanup it doesn't release" do + this = self() + + spawn(fn -> + this + |> create_released() + |> maybe_release(false) + end) + + refute_receive :cleaned, 500 + end + + test "a function call will keep resources, you can manually release" do + this = self() + + spawn(fn -> + this + |> create_released() + |> maybe_release(true) + end) + + assert_receive :cleaned, 100 + end +end \ No newline at end of file