Skip to content

Commit

Permalink
Merge pull request #64 from rbino/chore/cleanup
Browse files Browse the repository at this point in the history
chore(cleanup): remove dead code and add assertions
  • Loading branch information
rbino authored Mar 2, 2025
2 parents e98420e + c9489bf commit a87ef4c
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 229 deletions.
106 changes: 33 additions & 73 deletions src/beam.zig
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
// The code contained here is mostly taken from https://github.com/E-xyza/zigler. Since we're using a
// subset of all its features, we removed the dependency to be independent from API changes (and
// possibly to experiment ourselves with alternative APIs)
// The code contained here is taken and/or heavily inspired from https://github.com/E-xyza/zigler.
// Since we're using a subset of all its features, we removed the dependency to be independent
// from API changes (and to experiment ourselves with alternative APIs)

const std = @import("std");
const assert = std.debug.assert;
const e = @import("beam/erl_nif.zig");

pub const allocator = @import("beam/allocator.zig");
pub const nif = @import("beam/nif.zig");
pub const resource = @import("beam/resource.zig");
pub const process = @import("beam/process.zig");
pub const scheduler = @import("beam/scheduler.zig");

pub const Binary = @import("beam/Binary.zig");
pub const Env = ?*e.ErlNifEnv;
pub const Pid = e.ErlNifPid;
pub const ResourceType = ?*e.ErlNifResourceType;
Expand All @@ -27,21 +25,11 @@ pub const large_allocator = allocator.large_allocator;
/// A General Purpose Allocator backed by beam.large_allocator.
pub const general_purpose_allocator = allocator.general_purpose_allocator;

/// Raises a generic exception
pub fn raise(env: Env, reason: []const u8) Term {
return e.enif_raise_exception(env, make_atom(env, reason));
}

/// Raises a `:badarg` exception
pub fn raise_badarg(env: Env) Term {
return e.enif_make_badarg(env);
}

/// Raises a `:function_clause` exception
pub fn raise_function_clause_error(env: Env) Term {
return raise(env, "function_clause");
}

/// Creates a ref
pub fn make_ref(env: Env) Term {
return e.enif_make_ref(env);
Expand All @@ -67,11 +55,6 @@ pub fn make_ok_term(env: Env, val: Term) Term {
return e.enif_make_tuple(env, 2, make_ok(env), val);
}

/// Helper to create an `{:ok, atom}` tuple, taking the atom value from a slice
pub fn make_ok_atom(env: Env, atom_str: []const u8) Term {
return make_ok_term(env, make_atom(env, atom_str));
}

/// Creates a beam `:error` atom.
pub fn make_error(env: Env) Term {
return e.enif_make_atom(env, "error");
Expand Down Expand Up @@ -105,11 +88,6 @@ pub fn make_u8(env: Env, val: u8) Term {
return e.enif_make_uint(env, val);
}

/// Creates a u32 value term.
pub fn make_u32(env: Env, val: u32) Term {
return e.enif_make_uint(env, val);
}

/// Creates an BEAM tuple from a Zig tuple of terms
pub fn make_tuple(env: Env, tuple: anytype) Term {
const type_info = @typeInfo(@TypeOf(tuple));
Expand All @@ -132,7 +110,7 @@ pub fn get_char_slice(env: Env, src_term: Term) GetError![]u8 {
if (e.enif_inspect_binary(env, src_term, &bin) == 0) {
return GetError.ArgumentError;
}

assert(bin.data != null);
return bin.data[0..bin.size];
}

Expand All @@ -147,63 +125,45 @@ pub fn get_u128(env: Env, src_term: Term) GetError!u128 {
return std.mem.readInt(u128, bin[0..required_length], .little);
}

/// Extract a u64 from a term
pub fn get_u64(env: Env, src_term: Term) GetError!u64 {
var result: c_ulong = undefined;
if (e.enif_get_ulong(env, src_term, &result) == 0) {
return GetError.ArgumentError;
}

return @intCast(result);
/// Allocates a process independent environment
pub fn alloc_env() Env {
const env = e.enif_alloc_env();
assert(env != null);
return env;
}

/// Extract a u32 from a term
pub fn get_u32(env: Env, src_term: Term) GetError!u32 {
var result: c_uint = undefined;
if (e.enif_get_uint(env, src_term, &result) == 0) {
return GetError.ArgumentError;
}

return @intCast(result);
/// Clears a process independent environment
pub fn clear_env(env: Env) void {
e.enif_clear_env(env);
}

/// Extract a u16 from a term, checking it does not go outside the boundaries
pub fn get_u16(env: Env, src_term: Term) GetError!u16 {
var result: c_uint = undefined;
if (e.enif_get_uint(env, src_term, &result) == 0) {
return GetError.ArgumentError;
}

if (result > std.math.maxInt(u16)) {
return GetError.ArgumentError;
}

return @intCast(result);
/// Frees a process independent environment
pub fn free_env(env: Env) void {
e.enif_free_env(env);
}

pub const TermToBinaryError = error{OutOfMemory};
pub const SelfError = error{NotProcessBound};

/// Serializes a term to a beam.Binary
pub fn term_to_binary(env: Env, src_term: Term) TermToBinaryError!Binary {
var bin: e.ErlNifBinary = undefined;
if (e.enif_term_to_binary(env, src_term, &bin) == 0) {
return error.OutOfMemory;
pub fn self(env: Env) SelfError!Pid {
var result: Pid = undefined;
if (e.enif_self(env, &result) == null) {
return error.NotProcessBound;
}

return Binary{ .binary = bin };
return result;
}

/// Allocates a process independent environment
pub fn alloc_env() Env {
return e.enif_alloc_env();
}
pub const SendError = error{NotDelivered};

/// Clears a process independent environment
pub fn clear_env(env: Env) void {
e.enif_clear_env(env);
}
pub fn send(dest: Pid, msg_env: Env, msg: Term) SendError!void {
// Needed since enif_send is not const-correct
var to_pid = dest;

/// Frees a process independent environment
pub fn free_env(env: Env) void {
e.enif_free_env(env);
// Given our (only) use of the function, we make some assumptions, namely:
// - We're using a process independent env, so `caller_env` is null
// - We're clearing the env after the message is sent, so we pass `msg_env` instead of passing
// null to copy `msg`
if (e.enif_send(null, &to_pid, msg_env, msg) == 0) {
return error.NotDelivered;
}
}
24 changes: 0 additions & 24 deletions src/beam/Binary.zig

This file was deleted.

74 changes: 29 additions & 45 deletions src/beam/nif.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const std = @import("std");
const assert = std.debug.assert;

const e = @import("erl_nif.zig");
const beam = @import("../beam.zig");
const resource = @import("resource.zig");
const scheduler = @import("scheduler.zig");

pub const Nif = *const fn (beam.Env, argc: c_int, argv: [*c]const beam.Term) callconv(.C) beam.Term;
pub const NifLoadFn = *const fn (beam.Env, [*c]?*anyopaque, beam.Term) callconv(.C) c_int;
Expand All @@ -24,94 +25,77 @@ pub fn entrypoint(
comptime load_fn: NifLoadFn,
) Entrypoint {
return .{
.major = 2,
.minor = 16,
.major = e.ERL_NIF_MAJOR_VERSION,
.minor = e.ERL_NIF_MINOR_VERSION,
.name = name.ptr,
.num_of_funcs = exported_nifs.len,
.funcs = exported_nifs.ptr,
.load = load_fn,
.reload = null, // currently unsupported
.upgrade = null, // currently unsupported
.unload = null, // currently unsupported
.vm_variant = "beam.vanilla",
.vm_variant = e.ERL_NIF_VM_VARIANT,
.options = 1,
.sizeof_ErlNifResourceTypeInit = @sizeOf(e.ErlNifResourceTypeInit),
.min_erts = "erts-13.1.2",
.min_erts = e.ERL_NIF_MIN_ERTS_VERSION,
};
}

pub fn wrap(comptime nif_name: [:0]const u8, fun: anytype) FunctionEntry {
const nif = MakeWrappedNif(nif_name, fun);
const nif = MakeWrappedNif(fun);
return function_entry(nif_name, nif.arity, nif.wrapper);
}

fn MakeWrappedNif(comptime nif_name: [:0]const u8, comptime fun: anytype) type {
fn MakeWrappedNif(comptime fun: anytype) type {
const Function = @TypeOf(fun);

const function_info = switch (@typeInfo(Function)) {
.Fn => |f| f,
else => @compileError("Only functions can be wrapped"),
};

const params = function_info.params;
const with_env = params.len > 1 and params[0].type == beam.Env;
// Currently all our NIFs return a beam.Term
const ReturnType = function_info.return_type.?;
comptime assert(ReturnType == beam.Term);

// And since we need to construct a beam.Term, we always accept a beam.Env as first parameter
const params = function_info.params;
comptime assert(params[0].type == beam.Env);

return struct {
// Env is not counted towards the effective arity, subtract 1 if env is the first parameter
pub const arity = if (with_env) params.len - 1 else params.len;
// Env is not counted towards the effective arity, subtract 1 since env is the first parameter
pub const arity = params.len - 1;

pub fn wrapper(
env: beam.Env,
argc: c_int,
argv_ptr: [*c]const beam.Term,
) callconv(.C) beam.Term {
if (argc != arity) @panic(nif_name ++ " called with the wrong number of arguments");
assert(env != null);
assert(argc == arity);

const argv = @as([*]const beam.Term, @ptrCast(argv_ptr))[0..@intCast(argc)];
// If the first argument is env, we must adjust the offset between the input argv and
// The first argument is env, so we must adjust the offset between the input argv and
// the actual args of the function
const argv_offset = if (with_env) 1 else 0;
const argv_offset = 1;

var args: std.meta.ArgsTuple(Function) = undefined;
inline for (&args, 0..) |*arg, i| {
if (with_env and i == 0) {
// Put the env as first argument if the function accepts it
if (i == 0) {
// Put the env as first argument
arg.* = env;
} else {
// For all the other arguments, extract them based on their type
const argv_idx = i - argv_offset;
// Check that the function accepts only beam.Term arguments
const ArgType = @TypeOf(arg.*);
arg.* = get_arg_from_term(ArgType, env, argv[argv_idx]) catch
return beam.raise_badarg(env);
comptime assert(ArgType == beam.Term);
// Copy over input argv to the function call arguments, shifting by one
// due to env being the first argument
const argv_idx = i - argv_offset;
arg.* = argv[argv_idx];
}
}

// TODO: this currently assumes that if it's not an error union it's beam.Term, make
// this a little better
return switch (@typeInfo(ReturnType)) {
.ErrorUnion => @call(.auto, fun, args) catch |err| switch (err) {
error.Yield => scheduler.reschedule(env, nif_name.ptr, wrapper, argc, argv_ptr),
},
else => @call(.auto, fun, args),
};
return @call(.auto, fun, args);
}
};
}

fn get_arg_from_term(comptime T: type, env: beam.Env, term: beam.Term) !T {
// Special case: check if it's a resource
if (comptime resource.is_resource(T)) return try T.from_term_handle(env, term);

// These are what we currently need, the need to add new types to the switch should be caught
// by the compileError below
return switch (T) {
beam.Term => term,
u16 => try beam.get_u16(env, term),
u32 => try beam.get_u32(env, term),
u64 => try beam.get_u64(env, term),
u128 => try beam.get_u128(env, term),
[]const u8 => try beam.get_char_slice(env, term),
else => @compileError("Type " ++ @typeName(T) ++ " is not handled by get_arg_from_term"),
};
}
29 changes: 0 additions & 29 deletions src/beam/process.zig

This file was deleted.

13 changes: 0 additions & 13 deletions src/beam/scheduler.zig

This file was deleted.

Loading

0 comments on commit a87ef4c

Please sign in to comment.