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!(); }
},
}
}
- /// 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();
}