diff --git a/rustler_tool/src/fake_symbols.rs b/rustler_tool/src/fake_symbols.rs index f445d799..e77c866d 100644 --- a/rustler_tool/src/fake_symbols.rs +++ b/rustler_tool/src/fake_symbols.rs @@ -1,4 +1,4 @@ -use std::alloc::{Layout, alloc, dealloc}; +use std::alloc::{alloc, dealloc, Layout}; const HEADER: usize = 8; const ALIGNMENT: usize = 8; @@ -26,6 +26,8 @@ pub unsafe extern "C" fn enif_free(ptr: *mut u8) { #[no_mangle] pub static enif_alloc_binary: usize = 0; #[no_mangle] +pub static enif_alloc_resource: usize = 0; +#[no_mangle] pub static enif_alloc_env: usize = 0; #[no_mangle] pub static enif_binary_to_term: usize = 0; @@ -98,6 +100,8 @@ pub static enif_is_ref: usize = 0; #[no_mangle] pub static enif_is_tuple: usize = 0; #[no_mangle] +pub static enif_keep_resource: usize = 0; +#[no_mangle] pub static enif_make_atom_len: usize = 0; #[no_mangle] pub static enif_make_badarg: usize = 0; @@ -132,6 +136,10 @@ pub static enif_make_new_map: usize = 0; #[no_mangle] pub static enif_make_reverse_list: usize = 0; #[no_mangle] +pub static enif_make_resource: usize = 0; +#[no_mangle] +pub static enif_make_resource_binary: usize = 0; +#[no_mangle] pub static enif_make_sub_binary: usize = 0; #[no_mangle] pub static enif_make_tuple_from_array: usize = 0; @@ -158,12 +166,20 @@ pub static enif_realloc_binary: usize = 0; #[no_mangle] pub static enif_release_binary: usize = 0; #[no_mangle] +pub static enif_release_resource: usize = 0; +#[no_mangle] pub static enif_schedule_nif: usize = 0; #[no_mangle] pub static enif_self: usize = 0; #[no_mangle] +pub static enif_send: usize = 0; +#[no_mangle] pub static enif_snprintf: usize = 0; #[no_mangle] pub static enif_term_to_binary: usize = 0; #[no_mangle] pub static enif_term_type: usize = 0; +#[no_mangle] +pub static enif_thread_type: usize = 0; +#[no_mangle] +pub static enif_whereis_pid: usize = 0; diff --git a/rustler_tool/src/main.rs b/rustler_tool/src/main.rs index b977e80c..62d92011 100644 --- a/rustler_tool/src/main.rs +++ b/rustler_tool/src/main.rs @@ -1,6 +1,8 @@ #[cfg(unix)] mod fake_symbols; mod nif; +mod nif_elixir; +mod nif_erlang; use std::path::PathBuf; @@ -15,24 +17,44 @@ struct Cli { command: Option, } +#[derive(clap::ValueEnum, Clone, Default, Debug)] +enum OutputFormat { + #[default] + Bare, + Erlang, + Elixir, +} + #[derive(Subcommand)] enum Commands { /// does testing things - Nif { path: PathBuf }, + Nif { + path: PathBuf, + #[arg(short, long, default_value_t, value_enum)] + format: OutputFormat, + }, } fn main() { let cli = Cli::parse(); match &cli.command { - Some(Commands::Nif { path }) => { - println!("Extracting nifs from {:?}", path); - - let lib = NifLibrary::load(&path).unwrap(); + Some(Commands::Nif { path, format }) => { + let lib = NifLibrary::load(path).unwrap(); - println!("Found library {} with nifs", lib.name); - for nif in lib.nifs { - println!(" {}/{}", nif.name, nif.arity); + match format { + OutputFormat::Bare => { + println!("{}", lib.name); + for nif in lib.nifs { + println!(" {}/{}", nif.name, nif.arity); + } + } + OutputFormat::Erlang => { + println!("{}", nif_erlang::LibAsErlang(lib)) + } + OutputFormat::Elixir => { + println!("{}", nif_elixir::LibAsElixir(lib)) + } } } None => { diff --git a/rustler_tool/src/nif.rs b/rustler_tool/src/nif.rs index 673e1204..443a710f 100644 --- a/rustler_tool/src/nif.rs +++ b/rustler_tool/src/nif.rs @@ -39,24 +39,26 @@ unsafe fn maybe_call_nif_init( impl NifLibrary { pub fn load(path: &Path) -> Result> { unsafe { - let lib = Library::new(&path)?; + let lib = Library::new(path)?; let entry = maybe_call_nif_init(&lib)?; let name = CStr::from_ptr((*entry).name).to_str()?.to_string(); let nif_array = std::slice::from_raw_parts((*entry).funcs, (*entry).num_of_funcs as usize); - let nifs = nif_array - .into_iter() + let mut nifs: Vec<_> = nif_array + .iter() .filter_map(|f| { Some(Nif { - name: CStr::from_ptr((*f).name).to_str().ok()?.to_string(), - arity: (*f).arity as usize, - flags: (*f).flags as usize, + name: CStr::from_ptr(f.name).to_str().ok()?.to_string(), + arity: f.arity as usize, + flags: f.flags as usize, }) }) .collect(); + nifs.sort_by_key(|x| x.name.clone()); + Ok(NifLibrary { path: path.to_path_buf(), name, diff --git a/rustler_tool/src/nif_elixir.rs b/rustler_tool/src/nif_elixir.rs new file mode 100644 index 00000000..db106af8 --- /dev/null +++ b/rustler_tool/src/nif_elixir.rs @@ -0,0 +1,62 @@ +use crate::NifLibrary; +use std::fmt; + +pub struct LibAsElixir(pub NifLibrary); + +impl fmt::Display for LibAsElixir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!( + f, + "defmodule {} do", + string_to_elixir_atom(&self.0.name, false) + )?; + + for nif in &self.0.nifs { + write!(f, " def {}(", string_to_elixir_atom(&nif.name, true))?; + for i in 0..nif.arity { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "_")?; + } + writeln!(f, "), do: :erlang.nif_error(not_loaded)")?; + } + writeln!(f, "end") + } +} + +fn string_to_elixir_atom(s: &str, func: bool) -> String { + match s { + "false" | "true" | "nil" => s.to_string(), + _ if s.starts_with("Elixir.") => s[7..].to_string(), + _ => { + let mut output = String::new(); + let mut needs_quotes = false; + for c in s.chars() { + match c { + 'a'..='z' | 'A'..='Z' | '0'..='9' | '@' | '_' => output.push(c), + '"' => { + needs_quotes = true; + output.push_str("\\\""); + } + '\\' => { + needs_quotes = true; + output.push_str(r"\\"); + } + _ => { + needs_quotes = true; + output.push(c); + } + } + } + + if needs_quotes { + format!(":\"{}\"", output).to_string() + } else if !func { + format!(":{}", output).to_string() + } else { + output + } + } + } +} diff --git a/rustler_tool/src/nif_erlang.rs b/rustler_tool/src/nif_erlang.rs new file mode 100644 index 00000000..8f0aa968 --- /dev/null +++ b/rustler_tool/src/nif_erlang.rs @@ -0,0 +1,72 @@ +use crate::NifLibrary; +use std::fmt; + +pub struct LibAsErlang(pub NifLibrary); + +impl fmt::Display for LibAsErlang { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "-module({}).\n\n", string_to_erlang_atom(&self.0.name))?; + writeln!(f, "-export([")?; + let count = self.0.nifs.len(); + for (n, nif) in self.0.nifs.iter().enumerate() { + write!(f, " {}/{}", string_to_erlang_atom(&nif.name), nif.arity)?; + if n == count - 1 { + write!(f, ",")?; + } + writeln!(f)?; + } + write!(f, "]).\n\n")?; + + // TODO: On Load function + + for nif in &self.0.nifs { + write!(f, "{}(", string_to_erlang_atom(&nif.name))?; + for i in 0..nif.arity { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "_")?; + } + write!(f, ") ->\n erlang:nif_error(not_loaded).\n\n")?; + } + + Ok(()) + } +} + +fn string_to_erlang_atom(input: &str) -> String { + let mut output = String::with_capacity(input.len()); + let mut needs_quotes = false; + + let mut first = true; + + for c in input.chars() { + match c { + 'A'..='Z' if first => { + needs_quotes = true; + output.push(c); + } + 'a'..='z' | 'A'..='Z' | '0'..='9' | '@' | '_' => output.push(c), + '\'' => { + needs_quotes = true; + output.push_str(r"\'"); + } + '\\' => { + needs_quotes = true; + output.push_str(r"\\"); + } + _ => { + needs_quotes = true; + output.push(c); + } + } + + first = false; + } + + if needs_quotes { + format!("'{}'", output).to_string() + } else { + output + } +}