X-Git-Url: http://git.bitcoin.ninja/index.cgi?p=ldk-c-bindings;a=blobdiff_plain;f=c-bindings-gen%2Fsrc%2Ftypes.rs;h=c8b71d6b71b93cd99a302db51496b2a247f0c9c5;hp=dfc1e4112351193c72142bdd26e41f93b6f1eecc;hb=4b3c6041d34566b1f398cb3277482e505a6a36aa;hpb=9d4a50249197353575a3ab2dea753b2b93a8f8c5 diff --git a/c-bindings-gen/src/types.rs b/c-bindings-gen/src/types.rs index dfc1e41..c8b71d6 100644 --- a/c-bindings-gen/src/types.rs +++ b/c-bindings-gen/src/types.rs @@ -6,6 +6,7 @@ // You may not use this file except in accordance with one or both of these // licenses. +use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::fs::File; use std::io::Write; @@ -14,6 +15,8 @@ use std::hash; use crate::blocks::*; use proc_macro2::{TokenTree, Span}; +use quote::format_ident; +use syn::parse_quote; // The following utils are used purely to build our known types maps - they break down all the // types we need to resolve to include the given object, and no more. @@ -82,12 +85,21 @@ pub fn export_status(attrs: &[syn::Attribute]) -> ExportStatus { if i == "any" { // #[cfg(any(test, feature = ""))] if let TokenTree::Group(g) = iter.next().unwrap() { - if let TokenTree::Ident(i) = g.stream().into_iter().next().unwrap() { - if i == "test" || i == "feature" { - // If its cfg(feature(...)) we assume its test-only - return ExportStatus::TestOnly; + let mut all_test = true; + for token in g.stream().into_iter() { + if let TokenTree::Ident(i) = token { + match format!("{}", i).as_str() { + "test" => {}, + "feature" => {}, + _ => all_test = false, + } + } else if let TokenTree::Literal(lit) = token { + if format!("{}", lit) != "fuzztarget" { + all_test = false; + } } } + if all_test { return ExportStatus::TestOnly; } } } else if i == "test" || i == "feature" { // If its cfg(feature(...)) we assume its test-only @@ -150,23 +162,21 @@ pub fn is_enum_opaque(e: &syn::ItemEnum) -> bool { /// It maps both direct types as well as Deref, mapping them via the provided /// TypeResolver's resolve_path function (ie traits map to the concrete jump table, structs to the /// concrete C container struct, etc). -pub struct GenericTypes<'a> { - typed_generics: Vec)>>, +#[must_use] +pub struct GenericTypes<'a, 'b> { + parent: Option<&'b GenericTypes<'b, 'b>>, + typed_generics: HashMap<&'a syn::Ident, (String, Option<&'a syn::Path>)>, } -impl<'a> GenericTypes<'a> { +impl<'a, 'p: 'a> GenericTypes<'a, 'p> { pub fn new() -> Self { - Self { typed_generics: vec![HashMap::new()], } + Self { parent: None, typed_generics: HashMap::new(), } } /// push a new context onto the stack, allowing for a new set of generics to be learned which /// will override any lower contexts, but which will still fall back to resoltion via lower /// contexts. - pub fn push_ctx(&mut self) { - self.typed_generics.push(HashMap::new()); - } - /// pop the latest context off the stack. - pub fn pop_ctx(&mut self) { - self.typed_generics.pop(); + pub fn push_ctx<'c>(&'c self) -> GenericTypes<'a, 'c> { + GenericTypes { parent: Some(self), typed_generics: HashMap::new(), } } /// Learn the generics in generics in the current context, given a TypeResolver. @@ -186,13 +196,14 @@ impl<'a> GenericTypes<'a> { assert_simple_bound(&trait_bound); if let Some(mut path) = types.maybe_resolve_path(&trait_bound.path, None) { if types.skip_path(&path) { continue; } + if path == "Sized" { continue; } if non_lifetimes_processed { return false; } non_lifetimes_processed = true; let new_ident = if path != "std::ops::Deref" { path = "crate::".to_string() + &path; Some(&trait_bound.path) } else { None }; - self.typed_generics.last_mut().unwrap().insert(&type_param.ident, (path, new_ident)); + self.typed_generics.insert(&type_param.ident, (path, new_ident)); } else { return false; } } } @@ -208,13 +219,16 @@ impl<'a> GenericTypes<'a> { if p.qself.is_some() { return false; } if p.path.leading_colon.is_some() { return false; } let mut p_iter = p.path.segments.iter(); - if let Some(gen) = self.typed_generics.last_mut().unwrap().get_mut(&p_iter.next().unwrap().ident) { + if let Some(gen) = self.typed_generics.get_mut(&p_iter.next().unwrap().ident) { if gen.0 != "std::ops::Deref" { return false; } if &format!("{}", p_iter.next().unwrap().ident) != "Target" { return false; } let mut non_lifetimes_processed = false; for bound in t.bounds.iter() { if let syn::TypeParamBound::Trait(trait_bound) = bound { + if let Some(id) = trait_bound.path.get_ident() { + if format!("{}", id) == "Sized" { continue; } + } if non_lifetimes_processed { return false; } non_lifetimes_processed = true; assert_simple_bound(&trait_bound); @@ -227,7 +241,7 @@ impl<'a> GenericTypes<'a> { } } } - for (_, (_, ident)) in self.typed_generics.last().unwrap().iter() { + for (_, (_, ident)) in self.typed_generics.iter() { if ident.is_none() { return false; } } true @@ -253,7 +267,7 @@ impl<'a> GenericTypes<'a> { path = "crate::".to_string() + &path; Some(&tr.path) } else { None }; - self.typed_generics.last_mut().unwrap().insert(&t.ident, (path, new_ident)); + self.typed_generics.insert(&t.ident, (path, new_ident)); } else { unimplemented!(); } }, _ => unimplemented!(), @@ -267,21 +281,21 @@ impl<'a> GenericTypes<'a> { /// Attempt to resolve an Ident as a generic parameter and return the full path. pub fn maybe_resolve_ident<'b>(&'b self, ident: &syn::Ident) -> Option<&'b String> { - for gen in self.typed_generics.iter().rev() { - if let Some(res) = gen.get(ident).map(|(a, _)| a) { - return Some(res); - } + if let Some(res) = self.typed_generics.get(ident).map(|(a, _)| a) { + return Some(res); + } + if let Some(parent) = self.parent { + parent.maybe_resolve_ident(ident) + } else { + None } - None } /// Attempt to resolve a Path as a generic parameter and return the full path. as both a string /// and syn::Path. pub fn maybe_resolve_path<'b>(&'b self, path: &syn::Path) -> Option<(&'b String, &'a syn::Path)> { if let Some(ident) = path.get_ident() { - for gen in self.typed_generics.iter().rev() { - if let Some(res) = gen.get(ident).map(|(a, b)| (a, b.unwrap())) { - return Some(res); - } + if let Some(res) = self.typed_generics.get(ident).map(|(a, b)| (a, b.unwrap())) { + return Some(res); } } else { // Associated types are usually specified as "Self::Generic", so we check for that @@ -289,14 +303,16 @@ impl<'a> GenericTypes<'a> { let mut it = path.segments.iter(); if path.segments.len() == 2 && format!("{}", it.next().unwrap().ident) == "Self" { let ident = &it.next().unwrap().ident; - for gen in self.typed_generics.iter().rev() { - if let Some(res) = gen.get(ident).map(|(a, b)| (a, b.unwrap())) { - return Some(res); - } + if let Some(res) = self.typed_generics.get(ident).map(|(a, b)| (a, b.unwrap())) { + return Some(res); } } } - None + if let Some(parent) = self.parent { + parent.maybe_resolve_path(path) + } else { + None + } } } @@ -311,33 +327,48 @@ pub enum DeclType<'a> { } pub struct ImportResolver<'mod_lifetime, 'crate_lft: 'mod_lifetime> { + crate_name: &'mod_lifetime str, + dependencies: &'mod_lifetime HashSet, module_path: &'mod_lifetime str, imports: HashMap, declared: HashMap>, priv_modules: HashSet, } impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'crate_lft> { - fn process_use_intern(imports: &mut HashMap, u: &syn::UseTree, partial_path: &str, mut path: syn::punctuated::Punctuated) { + fn process_use_intern(crate_name: &str, dependencies: &HashSet, imports: &mut HashMap, + u: &syn::UseTree, partial_path: &str, mut path: syn::punctuated::Punctuated) { + + let new_path; + macro_rules! push_path { + ($ident: expr, $path_suffix: expr) => { + if partial_path == "" && !dependencies.contains(&$ident) { + new_path = format!("{}::{}{}", crate_name, $ident, $path_suffix); + let crate_name_ident = format_ident!("{}", crate_name); + path.push(parse_quote!(#crate_name_ident)); + } else { + new_path = format!("{}{}{}", partial_path, $ident, $path_suffix); + } + let ident = &$ident; + path.push(parse_quote!(#ident)); + } + } match u { syn::UseTree::Path(p) => { - let new_path = format!("{}{}::", partial_path, p.ident); - path.push(syn::PathSegment { ident: p.ident.clone(), arguments: syn::PathArguments::None }); - Self::process_use_intern(imports, &p.tree, &new_path, path); + push_path!(p.ident, "::"); + Self::process_use_intern(crate_name, dependencies, imports, &p.tree, &new_path, path); }, syn::UseTree::Name(n) => { - let full_path = format!("{}{}", partial_path, n.ident); - path.push(syn::PathSegment { ident: n.ident.clone(), arguments: syn::PathArguments::None }); - imports.insert(n.ident.clone(), (full_path, syn::Path { leading_colon: Some(syn::Token![::](Span::call_site())), segments: path })); + push_path!(n.ident, ""); + imports.insert(n.ident.clone(), (new_path, syn::Path { leading_colon: Some(syn::Token![::](Span::call_site())), segments: path })); }, syn::UseTree::Group(g) => { for i in g.items.iter() { - Self::process_use_intern(imports, i, partial_path, path.clone()); + Self::process_use_intern(crate_name, dependencies, imports, i, partial_path, path.clone()); } }, syn::UseTree::Rename(r) => { - let full_path = format!("{}{}", partial_path, r.ident); - path.push(syn::PathSegment { ident: r.ident.clone(), arguments: syn::PathArguments::None }); - imports.insert(r.rename.clone(), (full_path, syn::Path { leading_colon: Some(syn::Token![::](Span::call_site())), segments: path })); + push_path!(r.ident, ""); + imports.insert(r.rename.clone(), (new_path, syn::Path { leading_colon: Some(syn::Token![::](Span::call_site())), segments: path })); }, syn::UseTree::Glob(_) => { eprintln!("Ignoring * use for {} - this may result in resolution failures", partial_path); @@ -345,24 +376,23 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr } } - fn process_use(imports: &mut HashMap, u: &syn::ItemUse) { + fn process_use(crate_name: &str, dependencies: &HashSet, imports: &mut HashMap, u: &syn::ItemUse) { if let syn::Visibility::Public(_) = u.vis { // We actually only use these for #[cfg(fuzztarget)] eprintln!("Ignoring pub(use) tree!"); return; } if u.leading_colon.is_some() { eprintln!("Ignoring leading-colon use!"); return; } - Self::process_use_intern(imports, &u.tree, "", syn::punctuated::Punctuated::new()); + Self::process_use_intern(crate_name, dependencies, imports, &u.tree, "", syn::punctuated::Punctuated::new()); } fn insert_primitive(imports: &mut HashMap, id: &str) { - let ident = syn::Ident::new(id, Span::call_site()); - let mut path = syn::punctuated::Punctuated::new(); - path.push(syn::PathSegment { ident: ident.clone(), arguments: syn::PathArguments::None }); - imports.insert(ident, (id.to_owned(), syn::Path { leading_colon: Some(syn::Token![::](Span::call_site())), segments: path })); + let ident = format_ident!("{}", id); + let path = parse_quote!(#ident); + imports.insert(ident, (id.to_owned(), path)); } - pub fn new(module_path: &'mod_lifetime str, contents: &'crate_lft [syn::Item]) -> Self { + pub fn new(crate_name: &'mod_lifetime str, dependencies: &'mod_lifetime HashSet, module_path: &'mod_lifetime str, contents: &'crate_lft [syn::Item]) -> Self { let mut imports = HashMap::new(); // Add primitives to the "imports" list: Self::insert_primitive(&mut imports, "bool"); @@ -385,7 +415,7 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr for item in contents.iter() { match item { - syn::Item::Use(u) => Self::process_use(&mut imports, &u), + syn::Item::Use(u) => Self::process_use(crate_name, dependencies, &mut imports, &u), syn::Item::Struct(s) => { if let syn::Visibility::Public(_) = s.vis { match export_status(&s.attrs) { @@ -403,10 +433,7 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr else { process_alias = false; } } if process_alias { - match &*t.ty { - syn::Type::Path(_) => { declared.insert(t.ident.clone(), DeclType::StructImported); }, - _ => {}, - } + declared.insert(t.ident.clone(), DeclType::StructImported); } } }, @@ -431,7 +458,7 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr } } - Self { module_path, imports, declared, priv_modules } + Self { crate_name, dependencies, module_path, imports, declared, priv_modules } } pub fn get_declared_type(&self, ident: &syn::Ident) -> Option<&DeclType<'crate_lft>> { @@ -469,9 +496,14 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr } else { p_arg }; if p.leading_colon.is_some() { - Some(p.segments.iter().enumerate().map(|(idx, seg)| { + let mut res: String = p.segments.iter().enumerate().map(|(idx, seg)| { format!("{}{}", if idx == 0 { "" } else { "::" }, seg.ident) - }).collect()) + }).collect(); + let firstseg = p.segments.iter().next().unwrap(); + if !self.dependencies.contains(&firstseg.ident) { + res = self.crate_name.to_owned() + "::" + &res; + } + Some(res) } else if let Some(id) = p.get_ident() { self.maybe_resolve_ident(id) } else { @@ -484,7 +516,10 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr let remaining: String = seg_iter.map(|seg| { format!("::{}", seg.ident) }).collect(); - if let Some((imp, _)) = self.imports.get(&first_seg.ident) { + let first_seg_str = format!("{}", first_seg.ident); + if first_seg_str == "std" { + Some(first_seg_str + &remaining) + } else if let Some((imp, _)) = self.imports.get(&first_seg.ident) { if remaining != "" { Some(imp.clone() + &remaining) } else { @@ -500,11 +535,20 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr pub fn resolve_imported_refs(&self, mut ty: syn::Type) -> syn::Type { match &mut ty { syn::Type::Path(p) => { - if let Some(ident) = p.path.get_ident() { - if let Some((_, newpath)) = self.imports.get(ident) { - p.path = newpath.clone(); +eprintln!("rir {:?}", p); + if p.path.segments.len() != 1 { unimplemented!(); } + let mut args = p.path.segments[0].arguments.clone(); + if let syn::PathArguments::AngleBracketed(ref mut generics) = &mut args { + for arg in generics.args.iter_mut() { + if let syn::GenericArgument::Type(ref mut t) = arg { + *t = self.resolve_imported_refs(t.clone()); + } } - } else { unimplemented!(); } + } + if let Some((_, newpath)) = self.imports.get(single_ident_generic_path_to_ident(&p.path).unwrap()) { + p.path = newpath.clone(); + } + p.path.segments[0].arguments = args; }, syn::Type::Reference(r) => { r.elem = Box::new(self.resolve_imported_refs((*r.elem).clone())); @@ -530,6 +574,59 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr #[allow(deprecated)] pub type NonRandomHash = hash::BuildHasherDefault; +/// A public module +pub struct ASTModule { + pub attrs: Vec, + pub items: Vec, + pub submods: Vec, +} +/// A struct containing the syn::File AST for each file in the crate. +pub struct FullLibraryAST { + pub modules: HashMap, + pub dependencies: HashSet, +} +impl FullLibraryAST { + fn load_module(&mut self, module: String, attrs: Vec, mut items: Vec) { + let mut non_mod_items = Vec::with_capacity(items.len()); + let mut submods = Vec::with_capacity(items.len()); + for item in items.drain(..) { + match item { + syn::Item::Mod(m) if m.content.is_some() => { + if export_status(&m.attrs) == ExportStatus::Export { + if let syn::Visibility::Public(_) = m.vis { + let modident = format!("{}", m.ident); + let modname = if module != "" { + module.clone() + "::" + &modident + } else { + modident.clone() + }; + self.load_module(modname, m.attrs, m.content.unwrap().1); + submods.push(modident); + } else { + non_mod_items.push(syn::Item::Mod(m)); + } + } + }, + syn::Item::Mod(_) => panic!("--pretty=expanded output should never have non-body modules"), + syn::Item::ExternCrate(c) => { + if export_status(&c.attrs) == ExportStatus::Export { + self.dependencies.insert(c.ident); + } + }, + _ => { non_mod_items.push(item); } + } + } + self.modules.insert(module, ASTModule { attrs, items: non_mod_items, submods }); + } + + pub fn load_lib(lib: syn::File) -> Self { + assert_eq!(export_status(&lib.attrs), ExportStatus::Export); + let mut res = Self { modules: HashMap::default(), dependencies: HashSet::new() }; + res.load_module("".to_owned(), lib.attrs, lib.items); + res + } +} + /// 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 @@ -547,22 +644,45 @@ pub struct CrateTypes<'a> { /// exists. /// /// This is used at the end of processing to make C++ wrapper classes - pub templates_defined: HashMap, + pub templates_defined: RefCell>, /// 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, + template_file: RefCell<&'a mut File>, /// Set of containers which are clonable - pub clonable_types: HashSet, + clonable_types: RefCell>, /// Key impls Value pub trait_impls: HashMap>, + /// The full set of modules in the crate(s) + pub lib_ast: &'a FullLibraryAST, +} + +impl<'a> CrateTypes<'a> { + pub fn new(template_file: &'a mut File, libast: &'a FullLibraryAST) -> Self { + CrateTypes { + opaques: HashMap::new(), mirrored_enums: HashMap::new(), traits: HashMap::new(), + type_aliases: HashMap::new(), reverse_alias_map: HashMap::new(), + templates_defined: RefCell::new(HashMap::default()), + clonable_types: RefCell::new(HashSet::new()), trait_impls: HashMap::new(), + template_file: RefCell::new(template_file), lib_ast: &libast, + } + } + pub fn set_clonable(&self, object: String) { + self.clonable_types.borrow_mut().insert(object); + } + pub fn is_clonable(&self, object: &str) -> bool { + self.clonable_types.borrow().contains(object) + } + pub fn write_new_template(&self, mangled_container: String, has_destructor: bool, created_container: &[u8]) { + self.template_file.borrow_mut().write(created_container).unwrap(); + self.templates_defined.borrow_mut().insert(mangled_container, has_destructor); + } } /// A struct which tracks resolving rust types into C-mapped equivalents, exists for one specific /// module but contains a reference to the overall CrateTypes tracking. pub struct TypeResolver<'mod_lifetime, 'crate_lft: 'mod_lifetime> { - pub orig_crate: &'mod_lifetime str, pub module_path: &'mod_lifetime str, - pub crate_types: &'mod_lifetime mut CrateTypes<'crate_lft>, + pub crate_types: &'mod_lifetime CrateTypes<'crate_lft>, types: ImportResolver<'mod_lifetime, 'crate_lft>, } @@ -592,8 +712,8 @@ enum ContainerPrefixLocation { } impl<'a, 'c: 'a> TypeResolver<'a, 'c> { - pub fn new(orig_crate: &'a str, module_path: &'a str, types: ImportResolver<'a, 'c>, crate_types: &'a mut CrateTypes<'c>) -> Self { - Self { orig_crate, module_path, types, crate_types } + pub fn new(module_path: &'a str, types: ImportResolver<'a, 'c>, crate_types: &'a CrateTypes<'c>) -> Self { + Self { module_path, types, crate_types } } // ************************************************* @@ -627,7 +747,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } } pub fn is_clonable(&self, ty: &str) -> bool { - if self.crate_types.clonable_types.contains(ty) { return true; } + if self.crate_types.is_clonable(ty) { return true; } if self.is_primitive(ty) { return true; } match ty { "()" => true, @@ -661,15 +781,19 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "String" if is_ref => Some("crate::c_types::Str"), "std::time::Duration" => Some("u64"), + "std::io::Error" => Some("crate::c_types::IOError"), - "bitcoin::secp256k1::key::PublicKey" => Some("crate::c_types::PublicKey"), + "bitcoin::secp256k1::key::PublicKey"|"bitcoin::secp256k1::PublicKey" + => Some("crate::c_types::PublicKey"), "bitcoin::secp256k1::Signature" => Some("crate::c_types::Signature"), - "bitcoin::secp256k1::key::SecretKey" if is_ref => Some("*const [u8; 32]"), - "bitcoin::secp256k1::key::SecretKey" if !is_ref => Some("crate::c_types::SecretKey"), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if is_ref => Some("*const [u8; 32]"), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if !is_ref => Some("crate::c_types::SecretKey"), "bitcoin::secp256k1::Error" if !is_ref => Some("crate::c_types::Secp256k1Error"), "bitcoin::blockdata::script::Script" if is_ref => Some("crate::c_types::u8slice"), "bitcoin::blockdata::script::Script" if !is_ref => Some("crate::c_types::derived::CVec_u8Z"), - "bitcoin::blockdata::transaction::OutPoint" => Some("crate::chain::transaction::OutPoint"), + "bitcoin::blockdata::transaction::OutPoint" => Some("crate::lightning::chain::transaction::OutPoint"), "bitcoin::blockdata::transaction::Transaction" => Some("crate::c_types::Transaction"), "bitcoin::blockdata::transaction::TxOut" if !is_ref => Some("crate::c_types::TxOut"), "bitcoin::network::constants::Network" => Some("crate::bitcoin::network::Network"), @@ -682,15 +806,15 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "bitcoin::hash_types::BlockHash" if is_ref => Some("*const [u8; 32]"), "bitcoin::hash_types::BlockHash" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), "bitcoin::secp256k1::Message" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), - "ln::channelmanager::PaymentHash" if is_ref => Some("*const [u8; 32]"), - "ln::channelmanager::PaymentHash" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), - "ln::channelmanager::PaymentPreimage" if is_ref => Some("*const [u8; 32]"), - "ln::channelmanager::PaymentPreimage" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), - "ln::channelmanager::PaymentSecret" if is_ref => Some("crate::c_types::ThirtyTwoBytes"), - "ln::channelmanager::PaymentSecret" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), + "lightning::ln::channelmanager::PaymentHash" if is_ref => Some("*const [u8; 32]"), + "lightning::ln::channelmanager::PaymentHash" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), + "lightning::ln::channelmanager::PaymentPreimage" if is_ref => Some("*const [u8; 32]"), + "lightning::ln::channelmanager::PaymentPreimage" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), + "lightning::ln::channelmanager::PaymentSecret" if is_ref => Some("crate::c_types::ThirtyTwoBytes"), + "lightning::ln::channelmanager::PaymentSecret" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), // Override the default since Records contain an fmt with a lifetime: - "util::logger::Record" => Some("*const std::os::raw::c_char"), + "lightning::util::logger::Record" => Some("*const std::os::raw::c_char"), _ => None, } @@ -726,12 +850,16 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "std::time::Duration" => Some("std::time::Duration::from_secs("), - "bitcoin::secp256k1::key::PublicKey" if is_ref => Some("&"), - "bitcoin::secp256k1::key::PublicKey" => Some(""), + "bitcoin::secp256k1::key::PublicKey"|"bitcoin::secp256k1::PublicKey" + if is_ref => Some("&"), + "bitcoin::secp256k1::key::PublicKey"|"bitcoin::secp256k1::PublicKey" + => Some(""), "bitcoin::secp256k1::Signature" if is_ref => Some("&"), "bitcoin::secp256k1::Signature" => Some(""), - "bitcoin::secp256k1::key::SecretKey" if is_ref => Some("&::bitcoin::secp256k1::key::SecretKey::from_slice(&unsafe { *"), - "bitcoin::secp256k1::key::SecretKey" if !is_ref => Some(""), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if is_ref => Some("&::bitcoin::secp256k1::key::SecretKey::from_slice(&unsafe { *"), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if !is_ref => Some(""), "bitcoin::blockdata::script::Script" if is_ref => Some("&::bitcoin::blockdata::script::Script::from(Vec::from("), "bitcoin::blockdata::script::Script" if !is_ref => Some("::bitcoin::blockdata::script::Script::from("), "bitcoin::blockdata::transaction::Transaction" if is_ref => Some("&"), @@ -745,11 +873,11 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "bitcoin::hash_types::Txid" if is_ref => Some("&::bitcoin::hash_types::Txid::from_slice(&unsafe { &*"), "bitcoin::hash_types::Txid" if !is_ref => Some("::bitcoin::hash_types::Txid::from_slice(&"), "bitcoin::hash_types::BlockHash" => Some("::bitcoin::hash_types::BlockHash::from_slice(&"), - "ln::channelmanager::PaymentHash" if !is_ref => Some("::lightning::ln::channelmanager::PaymentHash("), - "ln::channelmanager::PaymentHash" if is_ref => Some("&::lightning::ln::channelmanager::PaymentHash(unsafe { *"), - "ln::channelmanager::PaymentPreimage" if !is_ref => Some("::lightning::ln::channelmanager::PaymentPreimage("), - "ln::channelmanager::PaymentPreimage" if is_ref => Some("&::lightning::ln::channelmanager::PaymentPreimage(unsafe { *"), - "ln::channelmanager::PaymentSecret" => Some("::lightning::ln::channelmanager::PaymentSecret("), + "lightning::ln::channelmanager::PaymentHash" if !is_ref => Some("::lightning::ln::channelmanager::PaymentHash("), + "lightning::ln::channelmanager::PaymentHash" if is_ref => Some("&::lightning::ln::channelmanager::PaymentHash(unsafe { *"), + "lightning::ln::channelmanager::PaymentPreimage" if !is_ref => Some("::lightning::ln::channelmanager::PaymentPreimage("), + "lightning::ln::channelmanager::PaymentPreimage" if is_ref => Some("&::lightning::ln::channelmanager::PaymentPreimage(unsafe { *"), + "lightning::ln::channelmanager::PaymentSecret" => Some("::lightning::ln::channelmanager::PaymentSecret("), // List of traits we map (possibly during processing of other files): "crate::util::logger::Logger" => Some(""), @@ -781,10 +909,13 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "std::time::Duration" => Some(")"), - "bitcoin::secp256k1::key::PublicKey" => Some(".into_rust()"), + "bitcoin::secp256k1::key::PublicKey"|"bitcoin::secp256k1::PublicKey" + => Some(".into_rust()"), "bitcoin::secp256k1::Signature" => Some(".into_rust()"), - "bitcoin::secp256k1::key::SecretKey" if !is_ref => Some(".into_rust()"), - "bitcoin::secp256k1::key::SecretKey" if is_ref => Some("}[..]).unwrap()"), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if !is_ref => Some(".into_rust()"), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if is_ref => Some("}[..]).unwrap()"), "bitcoin::blockdata::script::Script" if is_ref => Some(".to_slice()))"), "bitcoin::blockdata::script::Script" if !is_ref => Some(".into_rust())"), "bitcoin::blockdata::transaction::Transaction" => Some(".into_bitcoin()"), @@ -797,11 +928,11 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "bitcoin::hash_types::Txid" if is_ref => Some(" }[..]).unwrap()"), "bitcoin::hash_types::Txid" => Some(".data[..]).unwrap()"), "bitcoin::hash_types::BlockHash" if !is_ref => Some(".data[..]).unwrap()"), - "ln::channelmanager::PaymentHash" if !is_ref => Some(".data)"), - "ln::channelmanager::PaymentHash" if is_ref => Some(" })"), - "ln::channelmanager::PaymentPreimage" if !is_ref => Some(".data)"), - "ln::channelmanager::PaymentPreimage" if is_ref => Some(" })"), - "ln::channelmanager::PaymentSecret" => Some(".data)"), + "lightning::ln::channelmanager::PaymentHash" if !is_ref => Some(".data)"), + "lightning::ln::channelmanager::PaymentHash" if is_ref => Some(" })"), + "lightning::ln::channelmanager::PaymentPreimage" if !is_ref => Some(".data)"), + "lightning::ln::channelmanager::PaymentPreimage" if is_ref => Some(" })"), + "lightning::ln::channelmanager::PaymentSecret" => Some(".data)"), // List of traits we map (possibly during processing of other files): "crate::util::logger::Logger" => Some(""), @@ -826,7 +957,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { // Override the default since Records contain an fmt with a lifetime: // TODO: We should include the other record fields - "util::logger::Record" => Some(("std::ffi::CString::new(format!(\"{}\", ", ".args)).unwrap()")), + "lightning::util::logger::Record" => Some(("std::ffi::CString::new(format!(\"{}\", ", ".args)).unwrap()")), _ => None, }.map(|s| s.to_owned()) } @@ -853,11 +984,15 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "String" => Some(""), "std::time::Duration" => Some(""), + "std::io::Error" if !is_ref => Some("crate::c_types::IOError::from_rust("), - "bitcoin::secp256k1::key::PublicKey" => Some("crate::c_types::PublicKey::from_rust(&"), + "bitcoin::secp256k1::key::PublicKey"|"bitcoin::secp256k1::PublicKey" + => Some("crate::c_types::PublicKey::from_rust(&"), "bitcoin::secp256k1::Signature" => Some("crate::c_types::Signature::from_rust(&"), - "bitcoin::secp256k1::key::SecretKey" if is_ref => Some(""), - "bitcoin::secp256k1::key::SecretKey" if !is_ref => Some("crate::c_types::SecretKey::from_rust("), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if is_ref => Some(""), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if !is_ref => Some("crate::c_types::SecretKey::from_rust("), "bitcoin::secp256k1::Error" if !is_ref => Some("crate::c_types::Secp256k1Error::from_rust("), "bitcoin::blockdata::script::Script" if is_ref => Some("crate::c_types::u8slice::from_slice(&"), "bitcoin::blockdata::script::Script" if !is_ref => Some(""), @@ -875,14 +1010,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "bitcoin::hash_types::BlockHash" if is_ref => Some(""), "bitcoin::hash_types::BlockHash" => Some("crate::c_types::ThirtyTwoBytes { data: "), "bitcoin::secp256k1::Message" if !is_ref => Some("crate::c_types::ThirtyTwoBytes { data: "), - "ln::channelmanager::PaymentHash" if is_ref => Some("&"), - "ln::channelmanager::PaymentHash" if !is_ref => Some("crate::c_types::ThirtyTwoBytes { data: "), - "ln::channelmanager::PaymentPreimage" if is_ref => Some("&"), - "ln::channelmanager::PaymentPreimage" => Some("crate::c_types::ThirtyTwoBytes { data: "), - "ln::channelmanager::PaymentSecret" if !is_ref => Some("crate::c_types::ThirtyTwoBytes { data: "), + "lightning::ln::channelmanager::PaymentHash" if is_ref => Some("&"), + "lightning::ln::channelmanager::PaymentHash" if !is_ref => Some("crate::c_types::ThirtyTwoBytes { data: "), + "lightning::ln::channelmanager::PaymentPreimage" if is_ref => Some("&"), + "lightning::ln::channelmanager::PaymentPreimage" => Some("crate::c_types::ThirtyTwoBytes { data: "), + "lightning::ln::channelmanager::PaymentSecret" if !is_ref => Some("crate::c_types::ThirtyTwoBytes { data: "), // Override the default since Records contain an fmt with a lifetime: - "util::logger::Record" => Some("local_"), + "lightning::util::logger::Record" => Some("local_"), _ => None, }.map(|s| s.to_owned()) @@ -911,11 +1046,15 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "String" if is_ref => Some(".as_str().into()"), "std::time::Duration" => Some(".as_secs()"), + "std::io::Error" if !is_ref => Some(")"), - "bitcoin::secp256k1::key::PublicKey" => Some(")"), + "bitcoin::secp256k1::key::PublicKey"|"bitcoin::secp256k1::PublicKey" + => Some(")"), "bitcoin::secp256k1::Signature" => Some(")"), - "bitcoin::secp256k1::key::SecretKey" if !is_ref => Some(")"), - "bitcoin::secp256k1::key::SecretKey" if is_ref => Some(".as_ref()"), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if !is_ref => Some(")"), + "bitcoin::secp256k1::key::SecretKey"|"bitcoin::secp256k1::SecretKey" + if is_ref => Some(".as_ref()"), "bitcoin::secp256k1::Error" if !is_ref => Some(")"), "bitcoin::blockdata::script::Script" if is_ref => Some("[..])"), "bitcoin::blockdata::script::Script" if !is_ref => Some(".into_bytes().into()"), @@ -933,14 +1072,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "bitcoin::hash_types::BlockHash" if is_ref => Some(".as_inner()"), "bitcoin::hash_types::BlockHash" => Some(".into_inner() }"), "bitcoin::secp256k1::Message" if !is_ref => Some(".as_ref().clone() }"), - "ln::channelmanager::PaymentHash" if is_ref => Some(".0"), - "ln::channelmanager::PaymentHash" => Some(".0 }"), - "ln::channelmanager::PaymentPreimage" if is_ref => Some(".0"), - "ln::channelmanager::PaymentPreimage" => Some(".0 }"), - "ln::channelmanager::PaymentSecret" if !is_ref => Some(".0 }"), + "lightning::ln::channelmanager::PaymentHash" if is_ref => Some(".0"), + "lightning::ln::channelmanager::PaymentHash" => Some(".0 }"), + "lightning::ln::channelmanager::PaymentPreimage" if is_ref => Some(".0"), + "lightning::ln::channelmanager::PaymentPreimage" => Some(".0 }"), + "lightning::ln::channelmanager::PaymentSecret" if !is_ref => Some(".0 }"), // Override the default since Records contain an fmt with a lifetime: - "util::logger::Record" => Some(".as_ptr()"), + "lightning::util::logger::Record" => Some(".as_ptr()"), _ => None, }.map(|s| s.to_owned()) @@ -948,7 +1087,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { fn empty_val_check_suffix_from_path(&self, full_path: &str) -> Option<&str> { match full_path { - "ln::channelmanager::PaymentSecret" => Some(".data == [0; 32]"), + "lightning::ln::channelmanager::PaymentSecret" => Some(".data == [0; 32]"), "bitcoin::secp256k1::key::PublicKey" => Some(".is_null()"), "bitcoin::secp256k1::Signature" => Some(".is_null()"), _ => None @@ -970,14 +1109,41 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "crate::c_types" } - /// Returns true if this is a "transparent" container, ie an Option or a container which does + /// Returns true if the path containing the given args is a "transparent" container, ie an + /// Option or a container which does not require a generated continer class. + fn is_transparent_container<'i, I: Iterator>(&self, full_path: &str, _is_ref: bool, mut args: I) -> bool { + if full_path == "Option" { + let inner = args.next().unwrap(); + assert!(args.next().is_none()); + match inner { + syn::Type::Reference(_) => true, + syn::Type::Path(p) => { + if let Some(resolved) = self.maybe_resolve_path(&p.path, None) { + if self.is_primitive(&resolved) { false } else { true } + } else { true } + }, + syn::Type::Tuple(_) => false, + _ => unimplemented!(), + } + } else { false } + } + /// Returns true if the path is a "transparent" container, ie an Option or a container which does /// not require a generated continer class. - fn is_transparent_container(&self, full_path: &str, _is_ref: bool) -> bool { - full_path == "Option" + fn is_path_transparent_container(&self, full_path: &syn::Path, generics: Option<&GenericTypes>, is_ref: bool) -> bool { + let inner_iter = match &full_path.segments.last().unwrap().arguments { + syn::PathArguments::None => return false, + syn::PathArguments::AngleBracketed(args) => args.args.iter().map(|arg| { + if let syn::GenericArgument::Type(ref ty) = arg { + ty + } else { unimplemented!() } + }), + syn::PathArguments::Parenthesized(_) => unimplemented!(), + }; + self.is_transparent_container(&self.resolve_path(full_path, generics), is_ref, inner_iter) } /// Returns true if this is a known, supported, non-transparent container. fn is_known_container(&self, full_path: &str, is_ref: bool) -> bool { - (full_path == "Result" && !is_ref) || (full_path == "Vec" && !is_ref) || full_path.ends_with("Tuple") + (full_path == "Result" && !is_ref) || (full_path == "Vec" && !is_ref) || full_path.ends_with("Tuple") || full_path == "Option" } fn to_c_conversion_container_new_var<'b>(&self, generics: Option<&GenericTypes>, full_path: &str, is_ref: bool, single_contained: Option<&syn::Type>, var_name: &syn::Ident, var_access: &str) // Returns prefix + Vec<(prefix, var-name-to-inline-convert)> + suffix @@ -998,7 +1164,13 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { }, "Option" => { if let Some(syn::Type::Path(p)) = single_contained { - if self.c_type_has_inner_from_path(&self.resolve_path(&p.path, generics)) { + let inner_path = self.resolve_path(&p.path, generics); + if self.is_primitive(&inner_path) { + return Some(("if ", vec![ + (format!(".is_none() {{ {}::COption_{}Z::None }} else {{ ", Self::generated_container_path(), inner_path), + format!("{}::COption_{}Z::Some({}.unwrap())", Self::generated_container_path(), inner_path, var_access)) + ], " }", ContainerPrefixLocation::NoPrefix)); + } else if self.c_type_has_inner_from_path(&inner_path) { if is_ref { return Some(("if ", vec![ (".is_none() { std::ptr::null() } else { ".to_owned(), format!("({}.as_ref().unwrap())", var_access)) @@ -1044,7 +1216,10 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { }, "Option" => { if let Some(syn::Type::Path(p)) = single_contained { - if self.c_type_has_inner_from_path(&self.resolve_path(&p.path, generics)) { + let inner_path = self.resolve_path(&p.path, generics); + if self.is_primitive(&inner_path) { + return Some(("if ", vec![(".is_some() { Some(".to_string(), format!("{}.take()", var_access))], ") } else { None }", ContainerPrefixLocation::NoPrefix)) + } else if self.c_type_has_inner_from_path(&inner_path) { if is_ref { return Some(("if ", vec![(".inner.is_null() { None } else { Some((*".to_string(), format!("{}", var_access))], ").clone()) }", ContainerPrefixLocation::PerConv)) } else { @@ -1054,22 +1229,34 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } if let Some(t) = single_contained { - let mut v = Vec::new(); - let ret_ref = self.write_empty_rust_val_check_suffix(generics, &mut v, t); - let s = String::from_utf8(v).unwrap(); - match ret_ref { - EmptyValExpectedTy::ReferenceAsPointer => - return Some(("if ", vec![ - (format!("{} {{ None }} else {{ Some(", s), format!("unsafe {{ &mut *{} }}", var_access)) - ], ") }", ContainerPrefixLocation::NoPrefix)), - EmptyValExpectedTy::OwnedPointer => - return Some(("if ", vec![ - (format!("{} {{ None }} else {{ Some(", s), format!("unsafe {{ *Box::from_raw({}) }}", var_access)) - ], ") }", ContainerPrefixLocation::NoPrefix)), - EmptyValExpectedTy::NonPointer => - return Some(("if ", vec![ - (format!("{} {{ None }} else {{ Some(", s), format!("{}", var_access)) - ], ") }", ContainerPrefixLocation::PerConv)), + match t { + syn::Type::Reference(_)|syn::Type::Path(_)|syn::Type::Slice(_) => { + let mut v = Vec::new(); + let ret_ref = self.write_empty_rust_val_check_suffix(generics, &mut v, t); + let s = String::from_utf8(v).unwrap(); + match ret_ref { + EmptyValExpectedTy::ReferenceAsPointer => + return Some(("if ", vec![ + (format!("{} {{ None }} else {{ Some(", s), format!("unsafe {{ &mut *{} }}", var_access)) + ], ") }", ContainerPrefixLocation::NoPrefix)), + EmptyValExpectedTy::OwnedPointer => { + if let syn::Type::Slice(_) = t { + panic!(); + } + return Some(("if ", vec![ + (format!("{} {{ None }} else {{ Some(", s), format!("unsafe {{ *Box::from_raw({}) }}", var_access)) + ], ") }", ContainerPrefixLocation::NoPrefix)); + } + EmptyValExpectedTy::NonPointer => + return Some(("if ", vec![ + (format!("{} {{ None }} else {{ Some(", s), format!("{}", var_access)) + ], ") }", ContainerPrefixLocation::PerConv)), + } + }, + syn::Type::Tuple(_) => { + return Some(("if ", vec![(".is_some() { Some(".to_string(), format!("{}.take()", var_access))], ") } else { None }", ContainerPrefixLocation::PerConv)) + }, + _ => unimplemented!(), } } else { unreachable!(); } }, @@ -1129,7 +1316,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { // 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(); + write!(w, "{}", resolved).unwrap(); } else { write!(w, "crate::{}", resolved).unwrap(); } @@ -1265,12 +1452,29 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } } + fn is_real_type_array(&self, resolved_type: &str) -> Option { + if let Some(real_ty) = self.c_type_from_path(&resolved_type, true, false) { + if real_ty.ends_with("]") && real_ty.starts_with("*const [u8; ") { + let mut split = real_ty.split("; "); + split.next().unwrap(); + let tail_str = split.next().unwrap(); + assert!(split.next().is_none()); + let len = &tail_str[..tail_str.len() - 1]; + Some(parse_quote!([u8; #len])) + } else { None } + } else { None } + } + /// 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(&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 let Some(arr_ty) = self.is_real_type_array(&resolved) { + write!(w, ".data").unwrap(); + return self.write_empty_rust_val_check_suffix(generics, w, &arr_ty); + } if self.crate_types.opaques.get(&resolved).is_some() { write!(w, ".inner.is_null()").unwrap(); EmptyValExpectedTy::NonPointer @@ -1649,7 +1853,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { write!(w, "{} {{ ", pfx).unwrap(); let new_var_name = format!("{}_{}", ident, idx); - let new_var = self.write_conversion_new_var_intern(w, &syn::Ident::new(&new_var_name, Span::call_site()), + let new_var = self.write_conversion_new_var_intern(w, &format_ident!("{}", new_var_name), &var_access, conv_ty, generics, contains_slice || (is_ref && ty_has_inner), ptr_for_ref, to_c, path_lookup, container_lookup, var_prefix, var_suffix); if new_var { write!(w, " ").unwrap(); } @@ -1700,7 +1904,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { if let Some(aliased_type) = self.crate_types.type_aliases.get(&resolved_path) { return self.write_conversion_new_var_intern(w, ident, var, aliased_type, None, is_ref, ptr_for_ref, to_c, path_lookup, container_lookup, var_prefix, var_suffix); } - if self.is_known_container(&resolved_path, is_ref) || self.is_transparent_container(&resolved_path, is_ref) { + if self.is_known_container(&resolved_path, is_ref) || self.is_path_transparent_container(&p.path, generics, is_ref) { if let syn::PathArguments::AngleBracketed(args) = &p.path.segments.iter().next().unwrap().arguments { convert_container!(resolved_path, args.args.len(), || args.args.iter().map(|arg| { if let syn::GenericArgument::Type(ty) = arg { @@ -1737,7 +1941,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } else if let syn::Type::Reference(ty) = &*s.elem { let tyref = [&*ty.elem]; is_ref = true; - convert_container!("Slice", 1, || tyref.iter()); + convert_container!("Slice", 1, || tyref.iter().map(|t| *t)); unimplemented!("convert_container should return true as container_lookup should succeed for slices"); } else if let syn::Type::Tuple(t) = &*s.elem { // When mapping into a temporary new var, we need to own all the underlying objects. @@ -1774,7 +1978,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { for (idx, elem) in t.elems.iter().enumerate() { if let syn::Type::Path(p) = elem { let v_name = format!("orig_{}_{}", ident, idx); - let tuple_elem_ident = syn::Ident::new(&v_name, Span::call_site()); + let tuple_elem_ident = format_ident!("{}", &v_name); if self.write_conversion_new_var_intern(w, &tuple_elem_ident, &v_name, elem, generics, false, ptr_for_ref, to_c, path_lookup, container_lookup, var_prefix, var_suffix) { @@ -1850,13 +2054,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { // *** C Container Type Equivalent and alias Printing *** // ****************************************************** - fn write_template_generics<'b, W: std::io::Write>(&mut self, w: &mut W, args: &mut dyn Iterator, generics: Option<&GenericTypes>, is_ref: bool) -> bool { - assert!(!is_ref); // We don't currently support outer reference types + fn write_template_generics<'b, W: std::io::Write>(&self, w: &mut W, args: &mut dyn Iterator, generics: Option<&GenericTypes>, is_ref: bool) -> bool { for (idx, t) in args.enumerate() { if idx != 0 { write!(w, ", ").unwrap(); } if let syn::Type::Reference(r_arg) = t { + assert!(!is_ref); // We don't currently support outer reference types for non-primitive inners + if !self.write_c_type_intern(w, &*r_arg.elem, generics, false, false, false) { return false; } // While write_c_type_intern, above is correct, we don't want to blindly convert a @@ -1867,14 +2072,24 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { assert!(self.crate_types.opaques.get(&resolved).is_some() || self.c_type_from_path(&resolved, true, true).is_some(), "Template generics should be opaque or have a predefined mapping"); } else { unimplemented!(); } + } else if let syn::Type::Path(p_arg) = t { + if let Some(resolved) = self.maybe_resolve_path(&p_arg.path, generics) { + if !self.is_primitive(&resolved) { + assert!(!is_ref); // We don't currently support outer reference types for non-primitive inners + } + } else { + assert!(!is_ref); // We don't currently support outer reference types for non-primitive inners + } + if !self.write_c_type_intern(w, t, generics, false, false, false) { return false; } } else { + assert!(!is_ref); // We don't currently support outer reference types for non-primitive inners if !self.write_c_type_intern(w, t, generics, false, false, false) { return false; } } } true } - fn check_create_container(&mut self, mangled_container: String, container_type: &str, args: Vec<&syn::Type>, generics: Option<&GenericTypes>, is_ref: bool) -> bool { - if !self.crate_types.templates_defined.get(&mangled_container).is_some() { + fn check_create_container(&self, mangled_container: String, container_type: &str, args: Vec<&syn::Type>, generics: Option<&GenericTypes>, is_ref: bool) -> bool { + if !self.crate_types.templates_defined.borrow().get(&mangled_container).is_some() { let mut created_container: Vec = Vec::new(); if container_type == "Result" { @@ -1905,7 +2120,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { let is_clonable = self.is_clonable(&ok_str) && self.is_clonable(&err_str); write_result_block(&mut created_container, &mangled_container, &ok_str, &err_str, is_clonable); if is_clonable { - self.crate_types.clonable_types.insert(Self::generated_container_path().to_owned() + "::" + &mangled_container); + self.crate_types.set_clonable(Self::generated_container_path().to_owned() + "::" + &mangled_container); } } else if container_type == "Vec" { let mut a_ty: Vec = Vec::new(); @@ -1914,7 +2129,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { let is_clonable = self.is_clonable(&ty); write_vec_block(&mut created_container, &mangled_container, &ty, is_clonable); if is_clonable { - self.crate_types.clonable_types.insert(Self::generated_container_path().to_owned() + "::" + &mangled_container); + self.crate_types.set_clonable(Self::generated_container_path().to_owned() + "::" + &mangled_container); } } else if container_type.ends_with("Tuple") { let mut tuple_args = Vec::new(); @@ -1930,14 +2145,21 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } write_tuple_block(&mut created_container, &mangled_container, &tuple_args, is_clonable); if is_clonable { - self.crate_types.clonable_types.insert(Self::generated_container_path().to_owned() + "::" + &mangled_container); + self.crate_types.set_clonable(Self::generated_container_path().to_owned() + "::" + &mangled_container); + } + } else if container_type == "Option" { + let mut a_ty: Vec = Vec::new(); + if !self.write_template_generics(&mut a_ty, &mut args.iter().map(|t| *t), generics, is_ref) { return false; } + let ty = String::from_utf8(a_ty).unwrap(); + let is_clonable = self.is_clonable(&ty); + write_option_block(&mut created_container, &mangled_container, &ty, is_clonable); + if is_clonable { + self.crate_types.set_clonable(Self::generated_container_path().to_owned() + "::" + &mangled_container); } } else { unreachable!(); } - self.crate_types.templates_defined.insert(mangled_container.clone(), true); - - self.crate_types.template_file.write(&created_container).unwrap(); + self.crate_types.write_new_template(mangled_container.clone(), true, &created_container); } true } @@ -1947,9 +2169,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } else { unimplemented!(); } } fn write_c_mangled_container_path_intern - (&mut self, w: &mut W, args: Vec<&syn::Type>, generics: Option<&GenericTypes>, ident: &str, is_ref: bool, is_mut: bool, ptr_for_ref: bool, in_type: bool) -> bool { + (&self, w: &mut W, args: Vec<&syn::Type>, generics: Option<&GenericTypes>, ident: &str, is_ref: bool, is_mut: bool, ptr_for_ref: bool, in_type: bool) -> bool { let mut mangled_type: Vec = Vec::new(); - if !self.is_transparent_container(ident, is_ref) { + if !self.is_transparent_container(ident, is_ref, args.iter().map(|a| *a)) { write!(w, "C{}_", ident).unwrap(); write!(mangled_type, "C{}_", ident).unwrap(); } else { assert_eq!(args.len(), 1); } @@ -1957,22 +2179,22 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { macro_rules! write_path { ($p_arg: expr, $extra_write: expr) => { if let Some(subtype) = self.maybe_resolve_path(&$p_arg.path, generics) { - if self.is_transparent_container(ident, is_ref) { - // We dont (yet) support primitives or containers inside transparent - // containers, so check for that first: - if self.is_primitive(&subtype) { return false; } - if self.is_known_container(&subtype, is_ref) { return false; } + if self.is_transparent_container(ident, is_ref, args.iter().map(|a| *a)) { if !in_type { if self.c_type_has_inner_from_path(&subtype) { if !self.write_c_path_intern(w, &$p_arg.path, generics, is_ref, is_mut, ptr_for_ref) { return false; } } else { - // Option needs to be converted to a *mut T, ie mut ptr-for-ref - if !self.write_c_path_intern(w, &$p_arg.path, generics, true, true, true) { return false; } + if let Some(arr_ty) = self.is_real_type_array(&subtype) { + if !self.write_c_type_intern(w, &arr_ty, generics, false, true, false) { return false; } + } else { + // Option needs to be converted to a *mut T, ie mut ptr-for-ref + if !self.write_c_path_intern(w, &$p_arg.path, generics, true, true, true) { return false; } + } } } else { write!(w, "{}", $p_arg.path.segments.last().unwrap().ident).unwrap(); } - } else if self.is_known_container(&subtype, is_ref) || self.is_transparent_container(&subtype, is_ref) { + } else if self.is_known_container(&subtype, is_ref) || self.is_path_transparent_container(&$p_arg.path, generics, is_ref) { if !self.write_c_mangled_container_path_intern(w, Self::path_to_generic_args(&$p_arg.path), generics, &subtype, is_ref, is_mut, ptr_for_ref, true) { return false; @@ -2051,7 +2273,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } else { return false; } } else { return false; } } - if self.is_transparent_container(ident, is_ref) { return true; } + if self.is_transparent_container(ident, is_ref, args.iter().map(|a| *a)) { return true; } // Push the "end of type" Z write!(w, "Z").unwrap(); write!(mangled_type, "Z").unwrap(); @@ -2059,8 +2281,8 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { // Make sure the type is actually defined: self.check_create_container(String::from_utf8(mangled_type).unwrap(), ident, args, generics, is_ref) } - fn write_c_mangled_container_path(&mut self, w: &mut W, args: Vec<&syn::Type>, generics: Option<&GenericTypes>, ident: &str, is_ref: bool, is_mut: bool, ptr_for_ref: bool) -> bool { - if !self.is_transparent_container(ident, is_ref) { + fn write_c_mangled_container_path(&self, w: &mut W, args: Vec<&syn::Type>, generics: Option<&GenericTypes>, ident: &str, is_ref: bool, is_mut: bool, ptr_for_ref: bool) -> bool { + if !self.is_transparent_container(ident, is_ref, args.iter().map(|a| *a)) { write!(w, "{}::", Self::generated_container_path()).unwrap(); } self.write_c_mangled_container_path_intern(w, args, generics, ident, is_ref, is_mut, ptr_for_ref, false) @@ -2102,14 +2324,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { false } } - fn write_c_type_intern(&mut self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, is_ref: bool, is_mut: bool, ptr_for_ref: bool) -> bool { + fn write_c_type_intern(&self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, is_ref: bool, is_mut: bool, ptr_for_ref: bool) -> bool { match t { syn::Type::Path(p) => { if p.qself.is_some() { return false; } if let Some(full_path) = self.maybe_resolve_path(&p.path, generics) { - if self.is_known_container(&full_path, is_ref) || self.is_transparent_container(&full_path, is_ref) { + if self.is_known_container(&full_path, is_ref) || self.is_path_transparent_container(&p.path, generics, is_ref) { return self.write_c_mangled_container_path(w, Self::path_to_generic_args(&p.path), generics, &full_path, is_ref, is_mut, ptr_for_ref); } if let Some(aliased_type) = self.crate_types.type_aliases.get(&full_path).cloned() { @@ -2170,15 +2392,10 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { self.check_create_container(mangled_container, "Vec", vec![&*r.elem], generics, false) } else { false } } else if let syn::Type::Tuple(_) = &*s.elem { - let mut args = syn::punctuated::Punctuated::new(); + let mut args = syn::punctuated::Punctuated::<_, syn::token::Comma>::new(); args.push(syn::GenericArgument::Type((*s.elem).clone())); let mut segments = syn::punctuated::Punctuated::new(); - segments.push(syn::PathSegment { - ident: syn::Ident::new("Vec", Span::call_site()), - arguments: syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - colon2_token: None, lt_token: syn::Token![<](Span::call_site()), args, gt_token: syn::Token![>](Span::call_site()), - }) - }); + segments.push(parse_quote!(Vec<#args>)); self.write_c_type_intern(w, &syn::Type::Path(syn::TypePath { qself: None, path: syn::Path { leading_colon: None, segments } }), generics, false, is_mut, ptr_for_ref) } else { false } }, @@ -2193,14 +2410,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { _ => false, } } - pub fn write_c_type(&mut self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, ptr_for_ref: bool) { + pub fn write_c_type(&self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, ptr_for_ref: bool) { assert!(self.write_c_type_intern(w, t, generics, false, false, ptr_for_ref)); } - pub fn understood_c_path(&mut self, p: &syn::Path) -> bool { + pub fn understood_c_path(&self, p: &syn::Path) -> bool { if p.leading_colon.is_some() { return false; } self.write_c_path_intern(&mut std::io::sink(), p, None, false, false, false) } - pub fn understood_c_type(&mut self, t: &syn::Type, generics: Option<&GenericTypes>) -> bool { + pub fn understood_c_type(&self, t: &syn::Type, generics: Option<&GenericTypes>) -> bool { self.write_c_type_intern(&mut std::io::sink(), t, generics, false, false, false) } }