Skip to content

Commit

Permalink
Fix allocator usage and implement simple module formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
filmor committed Jun 6, 2024
1 parent df18a0f commit 0e803af
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 15 deletions.
18 changes: 17 additions & 1 deletion rustler_tool/src/fake_symbols.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::alloc::{Layout, alloc, dealloc};
use std::alloc::{alloc, dealloc, Layout};

const HEADER: usize = 8;
const ALIGNMENT: usize = 8;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
38 changes: 30 additions & 8 deletions rustler_tool/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#[cfg(unix)]
mod fake_symbols;
mod nif;
mod nif_elixir;
mod nif_erlang;

use std::path::PathBuf;

Expand All @@ -15,24 +17,44 @@ struct Cli {
command: Option<Commands>,
}

#[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 => {
Expand Down
14 changes: 8 additions & 6 deletions rustler_tool/src/nif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,26 @@ unsafe fn maybe_call_nif_init(
impl NifLibrary {
pub fn load(path: &Path) -> Result<NifLibrary, Box<dyn std::error::Error>> {
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,
Expand Down
62 changes: 62 additions & 0 deletions rustler_tool/src/nif_elixir.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
}
}
72 changes: 72 additions & 0 deletions rustler_tool/src/nif_erlang.rs
Original file line number Diff line number Diff line change
@@ -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
}
}

0 comments on commit 0e803af

Please sign in to comment.