use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
+use std::hash;
use proc_macro2::{TokenTree, Span};
EnumIgnored,
}
+// templates_defined is walked to write the C++ header, so if we use the default hashing it get
+// reordered on each genbindings run. Instead, we use SipHasher (which defaults to 0-keys) so that
+// the sorting is stable across runs. It is deprecated, but the "replacement" doesn't actually
+// accomplish the same goals, so we just ignore it.
+#[allow(deprecated)]
+type NonRandomHash = hash::BuildHasherDefault<hash::SipHasher>;
+
/// Top-level struct tracking everything which has been defined while walking the crate.
pub struct CrateTypes<'a> {
/// This may contain structs or enums, but only when either is mapped as
/// exists.
///
/// This is used at the end of processing to make C++ wrapper classes
- pub templates_defined: HashMap<String, bool>,
+ pub templates_defined: HashMap<String, bool, NonRandomHash>,
/// The output file for any created template container types, written to as we find new
/// template containers which need to be defined.
pub template_file: &'a mut File,
pub crate_types: &'mod_lifetime mut CrateTypes<'crate_lft>,
}
+/// Returned by write_empty_rust_val_check_suffix to indicate what type of dereferencing needs to
+/// happen to get the inner value of a generic.
+enum EmptyValExpectedTy {
+ /// A type which has a flag for being empty (eg an array where we treat all-0s as empty).
+ NonPointer,
+ /// A pointer that we want to dereference and move out of.
+ OwnedPointer,
+ /// A pointer which we want to convert to a reference.
+ ReferenceAsPointer,
+}
+
impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
pub fn new(orig_crate: &'a str, module_path: &'a str, crate_types: &'a mut CrateTypes<'c>) -> Self {
let mut imports = HashMap::new();
if let Some(t) = single_contained {
let mut v = Vec::new();
- let (needs_deref, ret_ref) = self.write_empty_rust_val_check_suffix(generics, &mut v, t);
+ let ret_ref = self.write_empty_rust_val_check_suffix(generics, &mut v, t);
let s = String::from_utf8(v).unwrap();
- if needs_deref && ret_ref {
- return Some(("if ", vec![
- (format!("{} {{ None }} else {{ Some(", s), format!("unsafe {{ &mut *{} }}", var_access))
- ], ") }"));
- } else if needs_deref {
- return Some(("if ", vec![
- (format!("{} {{ None }} else {{ Some(", s), format!("unsafe {{ *Box::from_raw({}) }}", var_access))
- ], ") }"));
- } else {
- return Some(("if ", vec![
- (format!("{} {{ None }} else {{ Some(", s), format!("{}", var_access))
- ], ") }"));
+ match ret_ref {
+ EmptyValExpectedTy::ReferenceAsPointer =>
+ return Some(("if ", vec![
+ (format!("{} {{ None }} else {{ Some(", s), format!("unsafe {{ &mut *{} }}", var_access))
+ ], ") }")),
+ EmptyValExpectedTy::OwnedPointer =>
+ return Some(("if ", vec![
+ (format!("{} {{ None }} else {{ Some(", s), format!("unsafe {{ *Box::from_raw({}) }}", var_access))
+ ], ") }")),
+ EmptyValExpectedTy::NonPointer =>
+ return Some(("if ", vec![
+ (format!("{} {{ None }} else {{ Some(", s), format!("{}", var_access))
+ ], ") }")),
}
} else { unreachable!(); }
},
// *** Original Rust Type Printing ***
// ***********************************
- fn write_rust_path<W: std::io::Write>(&self, w: &mut W, path: &syn::Path) {
- if let Some(resolved) = self.maybe_resolve_path(&path, None) {
+ fn in_rust_prelude(resolved_path: &str) -> bool {
+ match resolved_path {
+ "Vec" => true,
+ "Result" => true,
+ "Option" => true,
+ _ => false,
+ }
+ }
+
+ fn write_rust_path<W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, path: &syn::Path) {
+ if let Some(resolved) = self.maybe_resolve_path(&path, generics_resolver) {
if self.is_primitive(&resolved) {
write!(w, "{}", path.get_ident().unwrap()).unwrap();
} else {
- if resolved.starts_with("ln::") || resolved.starts_with("chain::") || resolved.starts_with("util::") {
- write!(w, "lightning::{}", resolved).unwrap();
+ // TODO: We should have a generic "is from a dependency" check here instead of
+ // checking for "bitcoin" explicitly.
+ if resolved.starts_with("bitcoin::") || Self::in_rust_prelude(&resolved) {
+ write!(w, "{}", resolved).unwrap();
+ // If we're printing a generic argument, it needs to reference the crate, otherwise
+ // the original crate:
+ } else if self.maybe_resolve_path(&path, None).as_ref() == Some(&resolved) {
+ write!(w, "{}::{}", self.orig_crate, resolved).unwrap();
} else {
- write!(w, "{}", resolved).unwrap(); // XXX: Probably doens't work, get_ident().unwrap()
+ write!(w, "crate::{}", resolved).unwrap();
}
}
if let syn::PathArguments::AngleBracketed(args) = &path.segments.iter().last().unwrap().arguments {
- self.write_rust_generic_arg(w, args.args.iter());
+ self.write_rust_generic_arg(w, generics_resolver, args.args.iter());
}
} else {
if path.leading_colon.is_some() {
if idx != 0 { write!(w, "::").unwrap(); }
write!(w, "{}", seg.ident).unwrap();
if let syn::PathArguments::AngleBracketed(args) = &seg.arguments {
- self.write_rust_generic_arg(w, args.args.iter());
+ self.write_rust_generic_arg(w, generics_resolver, args.args.iter());
}
}
}
}
- pub fn write_rust_generic_param<'b, W: std::io::Write>(&self, w: &mut W, generics: impl Iterator<Item=&'b syn::GenericParam>) {
+ pub fn write_rust_generic_param<'b, W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, generics: impl Iterator<Item=&'b syn::GenericParam>) {
let mut had_params = false;
for (idx, arg) in generics.enumerate() {
if idx != 0 { write!(w, ", ").unwrap(); } else { write!(w, "<").unwrap(); }
match bound {
syn::TypeParamBound::Trait(tb) => {
if tb.paren_token.is_some() || tb.lifetimes.is_some() { unimplemented!(); }
- self.write_rust_path(w, &tb.path);
+ self.write_rust_path(w, generics_resolver, &tb.path);
},
_ => unimplemented!(),
}
if had_params { write!(w, ">").unwrap(); }
}
- pub fn write_rust_generic_arg<'b, W: std::io::Write>(&self, w: &mut W, generics: impl Iterator<Item=&'b syn::GenericArgument>) {
+ pub fn write_rust_generic_arg<'b, W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, generics: impl Iterator<Item=&'b syn::GenericArgument>) {
write!(w, "<").unwrap();
for (idx, arg) in generics.enumerate() {
if idx != 0 { write!(w, ", ").unwrap(); }
match arg {
- syn::GenericArgument::Type(t) => self.write_rust_type(w, t),
+ syn::GenericArgument::Type(t) => self.write_rust_type(w, generics_resolver, t),
_ => unimplemented!(),
}
}
write!(w, ">").unwrap();
}
- pub fn write_rust_type<W: std::io::Write>(&self, w: &mut W, t: &syn::Type) {
+ pub fn write_rust_type<W: std::io::Write>(&self, w: &mut W, generics: Option<&GenericTypes>, t: &syn::Type) {
match t {
syn::Type::Path(p) => {
if p.qself.is_some() || p.path.leading_colon.is_some() {
unimplemented!();
}
- self.write_rust_path(w, &p.path);
+ self.write_rust_path(w, generics, &p.path);
},
syn::Type::Reference(r) => {
write!(w, "&").unwrap();
if r.mutability.is_some() {
write!(w, "mut ").unwrap();
}
- self.write_rust_type(w, &*r.elem);
+ self.write_rust_type(w, generics, &*r.elem);
},
syn::Type::Array(a) => {
write!(w, "[").unwrap();
- self.write_rust_type(w, &a.elem);
+ self.write_rust_type(w, generics, &a.elem);
if let syn::Expr::Lit(l) = &a.len {
if let syn::Lit::Int(i) = &l.lit {
write!(w, "; {}]", i).unwrap();
}
syn::Type::Slice(s) => {
write!(w, "[").unwrap();
- self.write_rust_type(w, &s.elem);
+ self.write_rust_type(w, generics, &s.elem);
write!(w, "]").unwrap();
},
syn::Type::Tuple(s) => {
write!(w, "(").unwrap();
for (idx, t) in s.elems.iter().enumerate() {
if idx != 0 { write!(w, ", ").unwrap(); }
- self.write_rust_type(w, &t);
+ self.write_rust_type(w, generics, &t);
}
write!(w, ")").unwrap();
},
}
}
- /// Prints a suffix to determine if a variable is empty (ie was set by write_empty_rust_val),
- /// returning whether we need to dereference the inner value before using it (ie it is a
- /// pointer).
- pub fn write_empty_rust_val_check_suffix<W: std::io::Write>(&self, generics: Option<&GenericTypes>, w: &mut W, t: &syn::Type) -> (bool, bool) {
+ /// Prints a suffix to determine if a variable is empty (ie was set by write_empty_rust_val).
+ /// See EmptyValExpectedTy for information on return types.
+ fn write_empty_rust_val_check_suffix<W: std::io::Write>(&self, generics: Option<&GenericTypes>, w: &mut W, t: &syn::Type) -> EmptyValExpectedTy {
match t {
syn::Type::Path(p) => {
let resolved = self.resolve_path(&p.path, generics);
if self.crate_types.opaques.get(&resolved).is_some() {
write!(w, ".inner.is_null()").unwrap();
- (false, false)
+ EmptyValExpectedTy::NonPointer
} else {
if let Some(suffix) = self.empty_val_check_suffix_from_path(&resolved) {
write!(w, "{}", suffix).unwrap();
- (false, false) // We may eventually need to allow empty_val_check_suffix_from_path to specify if we need a deref or not
+ // We may eventually need to allow empty_val_check_suffix_from_path to specify if we need a deref or not
+ EmptyValExpectedTy::NonPointer
} else {
write!(w, " == std::ptr::null_mut()").unwrap();
- (true, false)
+ EmptyValExpectedTy::OwnedPointer
}
}
},
if let syn::Expr::Lit(l) = &a.len {
if let syn::Lit::Int(i) = &l.lit {
write!(w, " == [0; {}]", i.base10_digits()).unwrap();
- (false, false)
+ EmptyValExpectedTy::NonPointer
} else { unimplemented!(); }
} else { unimplemented!(); }
},
// Option<[]> always implies that we want to treat len() == 0 differently from
// None, so we always map an Option<[]> into a pointer.
write!(w, " == std::ptr::null_mut()").unwrap();
- (true, true)
+ EmptyValExpectedTy::ReferenceAsPointer
},
_ => unimplemented!(),
}
assert!(self.write_c_type_intern(w, gen, None, false, false, false));
}
writeln!(w, ") -> {} {{", mangled_container).unwrap();
- writeln!(w, "\t{} {{", mangled_container).unwrap();
+ write!(w, "\t{} {{ ", mangled_container).unwrap();
for idx in 0..args.len() {
- writeln!(w, "\t\t{}: Box::into_raw(Box::new({})),", ('a' as u8 + idx as u8) as char, ('a' as u8 + idx as u8) as char).unwrap();
+ write!(w, "{}, ", ('a' as u8 + idx as u8) as char).unwrap();
}
- writeln!(w, "\t}}\n}}\n").unwrap();
+ writeln!(w, "}}\n}}\n").unwrap();
} else {
writeln!(w, "").unwrap();
}
} else if in_crate {
write!(w, "{}", c_type).unwrap();
} else {
- self.write_rust_type(w, &t);
+ self.write_rust_type(w, None, &t);
}
} else {
// If we just write out resolved_generic, it may mostly work, however for