Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discover NIFs at startup #613

Merged
merged 7 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rustler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ nif_version_2_17 = ["nif_version_2_16", "rustler_sys/nif_version_2_17"]
serde = ["dep:serde"]

[dependencies]
inventory = "0.3"
rustler_codegen = { path = "../rustler_codegen", version = "0.33.0", optional = true}
rustler_sys = { path = "../rustler_sys", version = "~2.4.1" }
num-bigint = { version = "0.4", optional = true }
Expand Down
3 changes: 3 additions & 0 deletions rustler/src/codegen_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use std::fmt;

use crate::{Encoder, Env, OwnedBinary, Term};

// Re-export of inventory
pub use inventory;

// Names used by the `rustler::init!` macro or other generated code.
pub use crate::wrapper::exception::raise_exception;
pub use crate::wrapper::{
Expand Down
32 changes: 22 additions & 10 deletions rustler/src/nif.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
use crate::codegen_runtime::{c_char, c_int, c_uint, DEF_NIF_FUNC, NIF_ENV, NIF_TERM};

pub trait Nif {
const NAME: *const c_char;
const ARITY: c_uint;
const FLAGS: c_uint;
const FUNC: DEF_NIF_FUNC;
const RAW_FUNC: unsafe extern "C" fn(
nif_env: NIF_ENV,
argc: c_int,
argv: *const NIF_TERM,
) -> NIF_TERM;
pub struct Nif {
pub name: *const c_char,
pub arity: c_uint,
pub flags: c_uint,
// pub func: DEF_NIF_FUNC,
pub raw_func:
unsafe extern "C" fn(nif_env: NIF_ENV, argc: c_int, argv: *const NIF_TERM) -> NIF_TERM,
}

impl Nif {
pub fn get_def(&self) -> DEF_NIF_FUNC {
DEF_NIF_FUNC {
arity: self.arity,
flags: self.flags,
function: self.raw_func,
name: self.name,
}
}
}

unsafe impl Sync for Nif {}

inventory::collect!(Nif);
1 change: 1 addition & 0 deletions rustler_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ syn = { version = "2.0", features = ["full", "extra-traits"] }
quote = "1.0"
heck = "0.5"
proc-macro2 = "1.0"
inventory = "0.3"

[dev-dependencies]
trybuild = "1.0"
44 changes: 18 additions & 26 deletions rustler_codegen/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Expr, Ident, Result, Token};
use syn::{Ident, Result, Token};

#[derive(Debug)]
pub struct InitMacroInput {
name: syn::Lit,
funcs: syn::ExprArray,
load: TokenStream,
}

impl Parse for InitMacroInput {
fn parse(input: ParseStream) -> Result<Self> {
let name = syn::Lit::parse(input)?;
let _comma = <syn::Token![,]>::parse(input)?;
let funcs = syn::ExprArray::parse(input)?;

if input.peek(syn::token::Comma) && input.peek2(syn::token::Bracket) {
let _ = syn::token::Comma::parse(input);
let _funcs = syn::ExprArray::parse(input);
// TODO: Generate deprecation warning
filmor marked this conversation as resolved.
Show resolved Hide resolved
}

let options = parse_expr_assigns(input);
let load = extract_option(options, "load");

Ok(InitMacroInput { name, funcs, load })
Ok(InitMacroInput { name, load })
}
}

Expand Down Expand Up @@ -55,20 +57,21 @@ fn extract_option(args: Vec<syn::ExprAssign>, name: &str) -> TokenStream {
impl From<InitMacroInput> for proc_macro2::TokenStream {
fn from(input: InitMacroInput) -> Self {
let name = input.name;
let num_of_funcs = input.funcs.elems.len();
let funcs = nif_funcs(input.funcs.elems);
let load = input.load;

let inner = quote! {
static mut NIF_ENTRY: Option<rustler::codegen_runtime::DEF_NIF_ENTRY> = None;
use rustler::Nif;
let nif_funcs: Box<[_]> =
rustler::codegen_runtime::inventory::iter::<rustler::Nif>()
.map(rustler::Nif::get_def)
.collect();

let entry = rustler::codegen_runtime::DEF_NIF_ENTRY {
major: rustler::codegen_runtime::NIF_MAJOR_VERSION,
minor: rustler::codegen_runtime::NIF_MINOR_VERSION,
name: concat!(#name, "\0").as_ptr() as *const rustler::codegen_runtime::c_char,
num_of_funcs: #num_of_funcs as rustler::codegen_runtime::c_int,
funcs: [#funcs].as_ptr(),
num_of_funcs: nif_funcs.len() as rustler::codegen_runtime::c_int,
funcs: nif_funcs.as_ptr(),
load: {
extern "C" fn nif_load(
env: rustler::codegen_runtime::NIF_ENV,
Expand All @@ -91,6 +94,9 @@ impl From<InitMacroInput> for proc_macro2::TokenStream {
};

unsafe {
// Leak nif_funcs
std::mem::forget(nif_funcs);

NIF_ENTRY = Some(entry);
NIF_ENTRY.as_ref().unwrap()
}
Expand All @@ -115,17 +121,3 @@ impl From<InitMacroInput> for proc_macro2::TokenStream {
}
}
}

fn nif_funcs(funcs: Punctuated<Expr, Comma>) -> TokenStream {
let mut tokens = TokenStream::new();

for func in funcs.iter() {
if let Expr::Path(_) = *func {
tokens.extend(quote!(#func::FUNC,));
} else {
panic!("Expected an expression, found: {}", stringify!(func));
}
}

tokens
}
85 changes: 39 additions & 46 deletions rustler_codegen/src/nif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,54 +55,47 @@ pub fn transcoder_decorator(nif_attributes: NifAttributes, fun: syn::ItemFn) ->
}

quote! {
#[allow(non_camel_case_types)]
pub struct #name;

impl rustler::Nif for #name {
const NAME: *const rustler::codegen_runtime::c_char = concat!(#erl_func_name, "\0").as_ptr() as *const rustler::codegen_runtime::c_char;
const ARITY: rustler::codegen_runtime::c_uint = #arity;
const FLAGS: rustler::codegen_runtime::c_uint = #flags as rustler::codegen_runtime::c_uint;
const RAW_FUNC: unsafe extern "C" fn(
nif_env: rustler::codegen_runtime::NIF_ENV,
argc: rustler::codegen_runtime::c_int,
argv: *const rustler::codegen_runtime::NIF_TERM
) -> rustler::codegen_runtime::NIF_TERM = {
unsafe extern "C" fn nif_func(
nif_env: rustler::codegen_runtime::NIF_ENV,
argc: rustler::codegen_runtime::c_int,
argv: *const rustler::codegen_runtime::NIF_TERM
) -> rustler::codegen_runtime::NIF_TERM {
let lifetime = ();
let env = rustler::Env::new(&lifetime, nif_env);

let terms = std::slice::from_raw_parts(argv, argc as usize)
.iter()
.map(|term| rustler::Term::new(env, *term))
.collect::<Vec<rustler::Term>>();

fn wrapper<'a>(
env: rustler::Env<'a>,
args: &[rustler::Term<'a>]
) -> rustler::codegen_runtime::NifReturned {
let result: std::thread::Result<_> = std::panic::catch_unwind(move || {
#decoded_terms
#function
Ok(#name(#argument_names))
});

rustler::codegen_runtime::handle_nif_result(result, env)
rustler::codegen_runtime::inventory::submit!(
rustler::Nif {
name: concat!(#erl_func_name, "\0").as_ptr()
as *const rustler::codegen_runtime::c_char,
arity: #arity,
flags: #flags as rustler::codegen_runtime::c_uint,
raw_func: {
unsafe extern "C" fn nif_func(
nif_env: rustler::codegen_runtime::NIF_ENV,
argc: rustler::codegen_runtime::c_int,
argv: *const rustler::codegen_runtime::NIF_TERM
) -> rustler::codegen_runtime::NIF_TERM {
let lifetime = ();
let env = rustler::Env::new(&lifetime, nif_env);

let terms = std::slice::from_raw_parts(argv, argc as usize)
.iter()
.map(|term| rustler::Term::new(env, *term))
.collect::<Vec<rustler::Term>>();

fn wrapper<'a>(
env: rustler::Env<'a>,
args: &[rustler::Term<'a>]
) -> rustler::codegen_runtime::NifReturned {
let result: std::thread::Result<_> =
std::panic::catch_unwind(move || {
#decoded_terms
#function
Ok(#name(#argument_names))
});

rustler::codegen_runtime::handle_nif_result(
result, env
)
}
wrapper(env, &terms).apply(env)
}
wrapper(env, &terms).apply(env)
nif_func
}
nif_func
};
const FUNC: rustler::codegen_runtime::DEF_NIF_FUNC = rustler::codegen_runtime::DEF_NIF_FUNC {
arity: Self::ARITY,
flags: Self::FLAGS,
function: Self::RAW_FUNC,
name: Self::NAME
};
}
}
);
}
}

Expand Down
1 change: 1 addition & 0 deletions rustler_sys/src/rustler_sys_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ unsafe impl Send for ErlNifEnv {}

/// See [ErlNifFunc](http://www.erlang.org/doc/man/erl_nif.html#ErlNifFunc) in the Erlang docs.
// #[allow(missing_copy_implementations)]
#[derive(Debug)]
#[repr(C)]
pub struct ErlNifFunc {
pub name: *const c_char,
Expand Down
19 changes: 1 addition & 18 deletions rustler_tests/native/rustler_serde_test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,7 @@ use crate::types::Animal;
use rustler::serde::{atoms, Deserializer, Error, Serializer};
use rustler::{types::tuple, Encoder, Env, NifResult, SerdeTerm, Term};

init! {
"Elixir.SerdeRustlerTests",
[
// json
json::decode_json,
json::decode_json_dirty,
json::encode_json_compact,
json::encode_json_compact_dirty,
json::encode_json_pretty,
json::encode_json_pretty_dirty,

// tests
readme,
test::test,
transcode,
transcode_dirty,
]
}
init!("Elixir.SerdeRustlerTests");

/// Implements the README example.
#[nif]
Expand Down
2 changes: 1 addition & 1 deletion rustler_tests/native/rustler_test/src/test_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub fn resource_make_binaries(
)
}

#[rustler::nif]
// #[rustler::nif]
filmor marked this conversation as resolved.
Show resolved Hide resolved
pub fn resource_make_binary_from_vec(env: Env, resource: ResourceArc<WithBinaries>) -> Binary {
resource.make_binary(env, |w| &w.b)
}
Loading