From 784b8fce15fbdc0ecd38bac6000a0109be63ddbc Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 17 Aug 2017 23:21:22 +0200 Subject: [PATCH 1/3] Use a graph the represend the dependencies between exported items --- Cargo.lock | 23 ++++ Cargo.toml | 1 + compile-tests/cycle.rs | 13 ++ src/bindgen/bindings.rs | 73 +---------- src/bindgen/builder.rs | 54 +++----- src/bindgen/cdecl.rs | 5 +- src/bindgen/dependencies.rs | 171 ++++++++++++++++++++---- src/bindgen/ir/function.rs | 48 +++---- src/bindgen/ir/item.rs | 160 ++++++++++++++++++++--- src/bindgen/ir/opaque.rs | 18 +-- src/bindgen/ir/path.rs | 5 + src/bindgen/ir/specialization.rs | 184 ++++++++++---------------- src/bindgen/ir/structure.rs | 70 ++++------ src/bindgen/ir/ty.rs | 214 ++++++++++++++++--------------- src/bindgen/ir/typedef.rs | 88 ++++++++++--- src/bindgen/library.rs | 152 ++-------------------- src/bindgen/mangle.rs | 3 +- src/bindgen/mod.rs | 1 - src/bindgen/monomorph.rs | 118 ----------------- src/bindgen/writer.rs | 17 --- src/lib.rs | 1 + src/main.rs | 1 + 22 files changed, 658 insertions(+), 762 deletions(-) create mode 100644 compile-tests/cycle.rs delete mode 100644 src/bindgen/monomorph.rs diff --git a/Cargo.lock b/Cargo.lock index 8fa957663..e359592a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,6 +4,7 @@ version = "0.1.23" dependencies = [ "clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "petgraph 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -52,6 +53,11 @@ name = "dtoa" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fixedbitset" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "itoa" version = "0.3.1" @@ -81,6 +87,20 @@ name = "num-traits" version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ordermap" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "petgraph" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixedbitset 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ordermap 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.3.15" @@ -213,11 +233,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" "checksum clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f" "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" +"checksum fixedbitset 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b0cb3d75726fa0c5ed3dce5dfcf0796affa2a60b33967f45012d86fb95a886f2" "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "e7eb6b826bfc1fdea7935d46556250d1799b7fe2d9f7951071f4291710665e3e" "checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad" "checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" +"checksum ordermap 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c036a53e6bb62d7eee2edf7e087df56fd84c7bbae6a0bd93c2b9f54bddf62e03" +"checksum petgraph 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "14c6ae5ccb73b438781abc93d35615019b1ad6e24b44116377fb819cfd7587de" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" "checksum serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)" = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" diff --git a/Cargo.toml b/Cargo.toml index 4dd6f6088..d7211c34c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ serde_derive = "0.9" serde_json = "0.9" tempdir = "0.3" toml = "0.3" +petgraph = "0.4" [dependencies.syn] version = "0.11" diff --git a/compile-tests/cycle.rs b/compile-tests/cycle.rs new file mode 100644 index 000000000..ef5ac9c69 --- /dev/null +++ b/compile-tests/cycle.rs @@ -0,0 +1,13 @@ +#[repr(C)] +pub struct Foo { + a: i32, + b: *const Bar, +} + +#[repr(C)] +pub struct Bar { + a: *mut Foo, +} + +#[no_mangle] +pub extern "C" fn foo(f: Foo) {} diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index ff5a1fabd..453212ba4 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -8,27 +8,20 @@ use std::path; use std::fs; use bindgen::config::{Config, Language}; -use bindgen::ir::{Item, Function}; -use bindgen::monomorph::TemplateSpecialization; -use bindgen::writer::{ListType, Source, SourceWriter}; +use bindgen::ir::Item; +use bindgen::writer::{Source, SourceWriter}; pub struct Bindings { config: Config, items: Vec, - functions: Vec, - template_specializations: Vec, } impl Bindings { pub fn new(config: Config, - items: Vec, - functions: Vec, - template_specializations: Vec) -> Bindings { + items: Vec) -> Bindings { Bindings { - config: config, - items: items, - functions: functions, - template_specializations: template_specializations, + config, + items, } } @@ -83,25 +76,10 @@ impl Bindings { if self.config.language == Language::Cxx { out.new_line_if_not_start(); - out.write("extern \"C\" {"); - out.new_line(); self.open_namespaces(&mut out); } - for item in &self.items { - out.new_line_if_not_start(); - match item { - &Item::Enum(ref x) => x.write(&self.config, &mut out), - &Item::Struct(ref x) => x.write(&self.config, &mut out), - &Item::OpaqueItem(ref x) => x.write(&self.config, &mut out), - &Item::Typedef(ref x) => x.write(&self.config, &mut out), - &Item::Specialization(_) => { - unreachable!("should not encounter a specialization in a generated library") - } - } - out.new_line(); - } if let Some(ref f) = self.config.autogen_warning { out.new_line_if_not_start(); @@ -109,51 +87,14 @@ impl Bindings { out.new_line(); } - for function in &self.functions { + for item in &self.items { out.new_line_if_not_start(); - function.write(&self.config, &mut out); + item.write(&self.config, &mut out); out.new_line(); } if self.config.language == Language::Cxx { self.close_namespaces(&mut out); - - out.new_line_if_not_start(); - out.write("} // extern \"C\""); - out.new_line(); - } - - if self.config.structure.generic_template_specialization && - self.config.language == Language::Cxx { - self.open_namespaces(&mut out); - for template in &self.template_specializations { - out.new_line_if_not_start(); - out.write("template<"); - for (i, param) in template.generic.generic_params.iter().enumerate() { - if i != 0 { - out.write(", ") - } - out.write("typename "); - out.write(param); - } - out.write(">"); - out.new_line(); - out.write(&format!("struct {};", template.generic.name)); - out.new_line(); - - for &(ref monomorph_path, ref generic_values) in &template.monomorphs { - out.new_line(); - out.write("template<>"); - out.new_line(); - out.write(&format!("struct {}<", template.generic.name)); - out.write_horizontal_source_list(generic_values, ListType::Join(", ")); - out.write(&format!("> : public {}", monomorph_path)); - out.open_brace(); - out.close_brace(true); - out.new_line(); - } - } - self.close_namespaces(&mut out); } if let Some(ref f) = self.config.autogen_warning { diff --git a/src/bindgen/builder.rs b/src/bindgen/builder.rs index 077d22098..d07bc3ca8 100644 --- a/src/bindgen/builder.rs +++ b/src/bindgen/builder.rs @@ -11,7 +11,7 @@ use syn; use bindgen::cargo::Cargo; use bindgen::config::Config; use bindgen::ir::{AnnotationSet, Cfg, Documentation, Enum, Function}; -use bindgen::ir::{OpaqueItem, Specialization, Struct, Typedef}; +use bindgen::ir::{OpaqueItem, Struct, Typedef}; use bindgen::library::Library; use bindgen::rust_lib; use bindgen::utilities::{SynAbiHelpers, SynItemHelpers}; @@ -25,7 +25,6 @@ pub struct LibraryBuilder { structs: BTreeMap, opaque_items: BTreeMap, typedefs: BTreeMap, - specializations: BTreeMap, functions: Vec, } @@ -39,7 +38,6 @@ impl LibraryBuilder { structs: BTreeMap::new(), opaque_items: BTreeMap::new(), typedefs: BTreeMap::new(), - specializations: BTreeMap::new(), functions: Vec::new(), } } @@ -115,14 +113,11 @@ impl LibraryBuilder { })?; } - self.functions.sort_by(|x, y| x.name.cmp(&y.name)); - Ok(Library::new(self.config, self.enums, self.structs, self.opaque_items, self.typedefs, - self.specializations, self.functions)) } @@ -333,42 +328,23 @@ impl LibraryBuilder { generics: &syn::Generics) { let alias_name = item.ident.to_string(); - let fail1 = if generics.lifetimes.is_empty() && - generics.ty_params.is_empty() + match Typedef::load(alias_name.clone(), + &item.attrs, + generics, + ty, + mod_cfg) { - match Typedef::load(alias_name.clone(), - ty, - &item.attrs, - mod_cfg) - { - Ok(typedef) => { - info!("take {}::{}", crate_name, &item.ident); - self.typedefs.insert(alias_name, typedef); - return; - } - Err(msg) => msg, - } - } else { - format!("cannot have generics in typedef") - }; - - let fail2 = match Specialization::load(alias_name.clone(), - generics, - ty, - &item.attrs, - mod_cfg) { - Ok(spec) => { + Ok(typedef) => { info!("take {}::{}", crate_name, &item.ident); - self.specializations.insert(alias_name, spec); + self.typedefs.insert(alias_name, typedef); return; } - Err(msg) => msg, - }; - - info!("skip {}::{} - ({} and {})", - crate_name, - &item.ident, - fail1, - fail2); + Err(msg) => { + info!("skip {}::{} - ({})", + crate_name, + &item.ident, + msg); + }, + } } } diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs index 50b46fef9..39e7f3c3a 100644 --- a/src/bindgen/cdecl.rs +++ b/src/bindgen/cdecl.rs @@ -62,9 +62,6 @@ impl CDecl { fn build_type(&mut self, t: &Type, is_const: bool) { match t { &Type::Path(ref path) => { - // We should be assured that there are no generics by instantiating - // monomorphs and mangling paths. - assert!(path.generics.len() == 0); if is_const { assert!(self.type_qualifers.len() == 0); @@ -72,7 +69,7 @@ impl CDecl { } assert!(self.type_name.len() == 0); - self.type_name = path.name.clone(); + self.type_name = path.mangle(); } &Type::Primitive(ref p) => { if is_const { diff --git a/src/bindgen/dependencies.rs b/src/bindgen/dependencies.rs index ec8dca986..170e2ff1f 100644 --- a/src/bindgen/dependencies.rs +++ b/src/bindgen/dependencies.rs @@ -1,43 +1,166 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use std::fmt::{self, Display}; +use std::collections::HashMap; -use std::collections::HashSet; -use std::cmp::Ordering; +use petgraph::{Graph, Direction}; +use petgraph::graph::{NodeIndex, EdgeIndex}; -use bindgen::ir::{Item, Path}; +use bindgen::ir::{Function, OpaqueItem, Item}; +use bindgen::library::Library; +use bindgen::config::Config; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum DependencyKind { + Ptr, + Normal, +} + +impl Display for DependencyKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + DependencyKind::Ptr => write!(f, "Ptr"), + DependencyKind::Normal => write!(f, "Normal"), + } + } +} /// A dependency list is used for gathering what order to output the types. -pub struct Dependencies { - pub order: Vec, - pub items: HashSet, +pub struct DependencyList { + graph: Graph, + lookup: HashMap, } -impl Dependencies { - pub fn new() -> Dependencies { - Dependencies { - order: Vec::new(), - items: HashSet::new(), +impl DependencyList { + pub fn new(functions: &[Function], library: &Library, config: &Config) -> Self { + let mut d = DependencyList { + graph: Graph::new(), + lookup: HashMap::new(), + }; + for f in functions { + d.add_dep(Item::Function(f.clone()), library, config); + } + d + } + + fn add_dep(&mut self, mut item: Item, library: &Library, config: &Config) { + if !self.lookup.contains_key(&item) { + item.apply_transformation(config); + let idx = self.graph.add_node(item.clone()); + self.lookup.insert(item.clone(), idx); + let deps = item.get_deps(library); + for &(ref d, _) in &deps { + self.add_dep(d.clone(), library, config); + } + for (d, k) in deps { + if let Some(to_id) = self.lookup.get(&d) { + match d { + Item::Specialization(ref s) if !s.generic_values.is_empty() => { + self.graph.add_edge(*to_id, idx, k); + } + _ => { + self.graph.add_edge(idx, *to_id, k); + } + } + } else { + println!("Did not found {:?}", d); + panic!(); + } + } } } - pub fn sort(&mut self) { - // Sort enums and opaque structs into their own layers because they don't - // depend on each other or anything else. - let ordering = |a: &Item, b: &Item| { - match (a, b) { - (&Item::Enum(ref x), &Item::Enum(ref y)) => x.name.cmp(&y.name), - (&Item::Enum(_), _) => Ordering::Less, - (_, &Item::Enum(_)) => Ordering::Greater, - (&Item::OpaqueItem(ref x), &Item::OpaqueItem(ref y)) => x.name.cmp(&y.name), - (&Item::OpaqueItem(_), _) => Ordering::Less, - (_, &Item::OpaqueItem(_)) => Ordering::Greater, + // It's there for debugging + #[allow(dead_code)] + pub fn print(&self) { + use petgraph::dot::Dot; + println!("{}", Dot::new(&self.graph)); + } + + fn generate_opaque_item( + &self, + id: NodeIndex, + o: OpaqueItem, + ret: &mut Vec, + ) -> Option> { + use petgraph::visit::EdgeRef; + // It is possible to have multiple edges with different + // dependencies between nodes, so we need to group the edges by + // theire source + let mut edges = HashMap::new(); + for e in self.graph.edges_directed(id, Direction::Incoming) { + edges.entry(e.source()).or_insert_with(Vec::new).push(e); + } + // We would only remove edges with a ptr dependency between nodes + // by injecting a opaque wrapper + let edges = edges + .values() + .filter(|edges| { + edges.iter().all(|e| e.weight() == &DependencyKind::Ptr) + }) + .flat_map(|edges| edges.iter().map(|e| e.id())) + .collect::>(); + // If there is node ptr dependency we are done here + if edges.is_empty() { + None + } else { + ret.push(Item::OpaqueItem(o)); + Some(edges) + } + } - _ => Ordering::Equal, + fn remove_cycle(&mut self, id: NodeIndex, ret: &mut Vec) { + let edges = { + let node = self.graph.node_weight(id).expect("Got id from graph above"); + match *node { + Item::Struct(ref s) => self.generate_opaque_item(id, s.as_opaque(), ret), + _ => return, } }; + if let Some(edges) = edges { + for e in edges { + self.graph.remove_edge(e); + } + } + } - self.order.sort_by(ordering); + pub fn calculate_order(mut self) -> Vec { + let mut ret = Vec::new(); + let mut cycle_counter = 0; + while self.graph.node_count() > 0 { + // find structs without any dependency + let externals = self.graph + .externals(Direction::Outgoing) + .collect::>(); + if externals.is_empty() { + if cycle_counter >= self.graph.node_count() { + self.print(); + panic!("Could not remove cycle"); + } + // there is a cyclic graph left, so we add a struct as opaque + // item and remove some edge from the dependceny graph + let id = self.graph + .node_indices() + .skip(cycle_counter) + .next() + .expect("Graph is not empty"); + self.remove_cycle(id, &mut ret); + cycle_counter += 1; + } else { + cycle_counter = 0; + // Iterate over all nodes without dependency + // 1. Remove them from the graph + // 2. Push them to the orderd struct list + for idx in externals { + if let Some(mut s) = self.graph.remove_node(idx) { + s.mangle_paths(); + ret.push(s); + } + } + } + } + ret } } diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs index 966015f16..ec4be0ec6 100644 --- a/src/bindgen/ir/function.rs +++ b/src/bindgen/ir/function.rs @@ -8,13 +8,13 @@ use syn; use bindgen::cdecl; use bindgen::config::{Config, Layout}; -use bindgen::dependencies::Dependencies; -use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, SynFnRetTyHelpers, Type}; +use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, SynFnRetTyHelpers, Type, Item}; use bindgen::library::Library; -use bindgen::monomorph::Monomorphs; use bindgen::rename::{IdentifierType, RenameRule}; use bindgen::utilities::{find_first_some, IterHelpers}; use bindgen::writer::{Source, SourceWriter}; +use bindgen::dependencies::DependencyKind; +use bindgen::config::Language; #[derive(Debug, Clone)] pub struct Function { @@ -49,27 +49,6 @@ impl Function { }) } - pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { - self.ret.add_dependencies(library, out); - for &(_, ref ty) in &self.args { - ty.add_dependencies(library, out); - } - } - - pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { - self.ret.add_monomorphs(library, out); - for &(_, ref ty) in &self.args { - ty.add_monomorphs(library, out); - } - } - - pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { - self.ret.mangle_paths(monomorphs); - for &mut (_, ref mut ty) in &mut self.args { - ty.mangle_paths(monomorphs); - } - } - pub fn rename_args(&mut self, config: &Config) { let rules = [self.annotations.parse_atom::("rename-all"), config.function.rename_args]; @@ -82,6 +61,21 @@ impl Function { .collect() } } + + pub fn mangle_paths(&mut self) { + self.ret.mangle_paths(); + for &mut (_, ref mut ty) in &mut self.args { + ty.mangle_paths(); + } + } + + pub fn get_deps(&self, library: &Library) -> Vec<(Item, DependencyKind)> { + let mut ret = self.ret.get_items(library, DependencyKind::Normal); + for &(_, ref arg) in &self.args { + ret.extend_from_slice(&arg.get_items(library, DependencyKind::Normal)); + } + ret + } } impl Source for Function { @@ -97,6 +91,9 @@ impl Source for Function { if func.extern_decl { out.write("extern "); } else { + if config.language == Language::Cxx { + out.write("extern \"C\" "); + } if let Some(ref prefix) = prefix { out.write(prefix); out.write(" "); @@ -125,6 +122,9 @@ impl Source for Function { if func.extern_decl { out.write("extern "); } else { + if config.language == Language::Cxx { + out.write("extern \"C\" "); + } if let Some(ref prefix) = prefix { out.write(prefix); out.new_line(); diff --git a/src/bindgen/ir/item.rs b/src/bindgen/ir/item.rs index ccbd0b0af..668a3124b 100644 --- a/src/bindgen/ir/item.rs +++ b/src/bindgen/ir/item.rs @@ -2,33 +2,159 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use bindgen::dependencies::Dependencies; -use bindgen::ir::{Enum, OpaqueItem, Specialization, Struct, Typedef}; +use bindgen::ir::{Enum, OpaqueItem, Specialization, Struct, Typedef, Function}; +use bindgen::dependencies::DependencyKind; use bindgen::library::Library; +use bindgen::writer::{Source, SourceWriter}; +use bindgen::config::Config; +use std::hash::{Hash, Hasher}; +use std::fmt::{self, Display}; +use std::io::Write; -/// An item is any type of rust item besides a function +/// An item is any type of rust item #[derive(Debug, Clone)] pub enum Item { - OpaqueItem(OpaqueItem), - Struct(Struct), Enum(Enum), + Struct(Struct), + OpaqueItem(OpaqueItem), Typedef(Typedef), + Function(Function), Specialization(Specialization), } impl Item { - pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { - match self { - &Item::Struct(ref x) => { - x.add_dependencies(library, out); - }, - &Item::Typedef(ref x) => { - x.add_dependencies(library, out); - }, - &Item::Specialization(..) => { - unreachable!(); - }, - _ => { } + fn name(&self) -> &str { + match *self { + Item::Enum(ref e) => &e.name, + Item::Struct(ref s) => &s.name, + Item::OpaqueItem(ref o) => &o.name, + Item::Typedef(ref t) => &t.name, + Item::Function(ref f) => &f.name, + Item::Specialization(ref s) => &s.name, + } + } + + pub fn get_deps(&self, library: &Library) -> Vec<(Item, DependencyKind)> { + match *self { + Item::Enum(_) | Item::OpaqueItem(_) => Vec::new(), + Item::Specialization(ref s) => s.get_deps(library), + Item::Struct(ref s) => s.get_deps(library), + Item::Typedef(ref t) => t.get_deps(library), + Item::Function(ref f) => f.get_deps(library), + } + } + + pub fn mangle_paths(&mut self) { + match *self { + Item::Enum(_) | Item::OpaqueItem(_) | Item::Specialization(_) => {} + Item::Struct(ref mut s) => s.mangle_paths(), + Item::Typedef(ref mut t) => t.mangle_paths(), + Item::Function(ref mut f) => f.mangle_paths(), + + } + } + + pub fn apply_transformation(&mut self, config: &Config) { + match *self { + Item::Enum(ref mut e) => { + e.rename_values(config); + } + Item::Struct(ref mut s) => { + s.rename_fields(config); + } + Item::Function(ref mut f) => { + f.rename_args(config); + } + _ => {} + } + } +} + +impl Display for Item { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Item::Enum(ref e) => write!(f, "Enum {}", e.name), + Item::Struct(ref s) => write!(f, "Struct {}", s.name), + Item::OpaqueItem(ref o) => write!(f, "Opaque {}", o.name), + Item::Typedef(ref t) => write!(f, "Typedef {}", t.name), + Item::Function(ref c) => write!(f, "Function {}", c.name), + Item::Specialization(ref s) if !s.generic_values.is_empty() => { + write!(f, "Specialization {}<", s.name)?; + let mut first = true; + for g in &s.generic_values { + if first { + first = false; + } else { + write!(f, ", ")?; + } + write!(f, "{:?}", g.get_root_path())?; + } + write!(f, ">") + } + Item::Specialization(ref s) => { + write!(f, "Specialization {}<", s.name)?; + let mut first = true; + for g in &s.generic_params { + if first { + first = false; + } else { + write!(f, ", ")?; + } + write!(f, "{}", g)?; + } + write!(f, ">") + } + } + } +} + +impl Hash for Item { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + self.name().hash(state); + match *self { + Item::Enum(_) => "enum".hash(state), + Item::Struct(_) => "struct".hash(state), + Item::OpaqueItem(_) => "opaque".hash(state), + Item::Typedef(_) => "typedef".hash(state), + Item::Function(_) => "function".hash(state), + Item::Specialization(ref s) => { + "specialization".hash(state); + s.generic_values.hash(state); + } + } + } +} + +impl PartialEq for Item { + fn eq(&self, rhs: &Self) -> bool { + match (self, rhs) { + (&Item::Enum(ref e1), &Item::Enum(ref e2)) => e1.name == e2.name, + (&Item::Struct(ref s1), &Item::Struct(ref s2)) => s1.name == s2.name, + (&Item::OpaqueItem(ref o1), &Item::OpaqueItem(ref o2)) => o1.name == o2.name, + (&Item::Typedef(ref t1), &Item::Typedef(ref t2)) => t1.name == t2.name, + (&Item::Function(ref f1), &Item::Function(ref f2)) => f1.name == f2.name, + (&Item::Specialization(ref s1), &Item::Specialization(ref s2)) => { + s1.name == s2.name && s1.generic_values == s2.generic_values + } + _ => false, + } + } +} + +impl Eq for Item {} + +impl Source for Item { + fn write(&self, config: &Config, out: &mut SourceWriter) { + match *self { + Item::Enum(ref e) => e.write(config, out), + Item::Struct(ref s) => s.write(config, out), + Item::OpaqueItem(ref o) => o.write(config, out), + Item::Typedef(ref t) => t.write(config, out), + Item::Function(ref f) => f.write(config, out), + Item::Specialization(ref s) => s.write(config, out), } } } diff --git a/src/bindgen/ir/opaque.rs b/src/bindgen/ir/opaque.rs index 659cfe9ec..c6adbcbc3 100644 --- a/src/bindgen/ir/opaque.rs +++ b/src/bindgen/ir/opaque.rs @@ -7,9 +7,7 @@ use std::io::Write; use syn; use bindgen::config::{Config, Language}; -use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Path, Type}; -use bindgen::mangle; -use bindgen::monomorph::Monomorphs; +use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Path}; use bindgen::writer::{Source, SourceWriter}; #[derive(Debug, Clone)] @@ -38,20 +36,6 @@ impl OpaqueItem { documentation: Documentation::load(attrs), } } - - pub fn instantiate_monomorph(&self, generic_values: &Vec, out: &mut Monomorphs) { - assert!(self.generic_params.len() > 0); - - let monomorph = OpaqueItem { - name: mangle::mangle_path(&self.name, generic_values), - generic_params: vec![], - cfg: self.cfg.clone(), - annotations: self.annotations.clone(), - documentation: self.documentation.clone(), - }; - - out.insert_opaque(self, monomorph, generic_values.clone()); - } } impl Source for OpaqueItem { diff --git a/src/bindgen/ir/path.rs b/src/bindgen/ir/path.rs index d6bd6b8b4..e787454d1 100644 --- a/src/bindgen/ir/path.rs +++ b/src/bindgen/ir/path.rs @@ -6,6 +6,7 @@ use syn; use bindgen::ir::Type; use bindgen::utilities::IterHelpers; +use bindgen::mangle; pub type Path = String; @@ -51,4 +52,8 @@ impl GenericPath { Ok(GenericPath::new(name, generics)) } + + pub fn mangle(&self) -> Path { + mangle::mangle_path(&self.name, &self.generics) + } } diff --git a/src/bindgen/ir/specialization.rs b/src/bindgen/ir/specialization.rs index a80b05638..6c2c87f52 100644 --- a/src/bindgen/ir/specialization.rs +++ b/src/bindgen/ir/specialization.rs @@ -2,11 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use syn; - -use bindgen::ir::{AnnotationSet, Cfg, Documentation, Enum}; -use bindgen::ir::{GenericPath, Item, OpaqueItem, PrimitiveType, Struct, Typedef}; +use bindgen::ir::{AnnotationSet, Cfg, Documentation, Item, Type}; use bindgen::library::Library; +use bindgen::writer::{Source, SourceWriter}; +use bindgen::dependencies::DependencyKind; +use bindgen::mangle; +use bindgen::config::Config; +use std::io::Write; /// A type alias that generates a copy of its aliasee with a new name. If the type /// alias has generic values, it specializes its aliasee. This is useful for @@ -15,133 +17,83 @@ use bindgen::library::Library; pub struct Specialization { pub name: String, pub generic_params: Vec, - pub aliased: GenericPath, + pub generic_values: Vec, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, } impl Specialization { - pub fn load(name: String, - generics: &syn::Generics, - ty: &syn::Ty, - attrs: &Vec, - mod_cfg: &Option) -> Result - { - match ty { - &syn::Ty::Path(ref _q, ref p) => { - let generic_params = generics.ty_params.iter() - .map(|x| x.ident.to_string()) - .collect::>(); - - let path = GenericPath::load(p)?; - if PrimitiveType::maybe(&path.name).is_some() { - return Err(format!("can't specialize a primitive")); + pub fn get_deps(&self, library: &Library) -> Vec<(Item, DependencyKind)> { + if self.generic_values.is_empty() { + return Vec::new(); + } + if let Some(v) = library.get_item(&self.name) { + let mut ret = self.generic_values.iter() + .flat_map(|g| g.get_items(library, DependencyKind::Normal)) + .collect::>(); + match v { + Item::Struct(mut s) => { + s.name = mangle::mangle_path(&s.name, &self.generic_values); + ret.push((Item::Specialization(Specialization { + generic_values: Vec::new(), + ..self.clone() + }), DependencyKind::Normal)); + ret } - - Ok(Specialization { - name: name, - generic_params: generic_params, - aliased: path, - cfg: Cfg::append(mod_cfg, Cfg::load(attrs)), - annotations: AnnotationSet::load(attrs)?, - documentation: Documentation::load(attrs), - }) - } - _ => { - Err(format!("not a path")) + Item::Typedef(mut t) => { + t.name = mangle::mangle_path(&t.name, &self.generic_values); + ret.push((Item::Specialization(Specialization { + generic_values: Vec::new(), + ..self.clone() + }), DependencyKind::Normal)); + ret + } + e =>{ println!("{:?}", e); unimplemented!()} } + } else { + Vec::new() } } +} - pub fn specialize(&self, library: &Library) -> Result, String> { - match library.get_item(&self.aliased.name) { - Some(aliased) => { - match aliased { - Item::OpaqueItem(ref aliased) => { - if self.aliased.generics.len() != - aliased.generic_params.len() { - return Err(format!("incomplete specialization")); - } - - Ok(Some(Item::OpaqueItem(OpaqueItem { - name: self.name.clone(), - generic_params: self.generic_params.clone(), - cfg: self.cfg.clone(), - annotations: self.annotations.clone(), - documentation: self.documentation.clone(), - }))) - } - Item::Struct(ref aliased) => { - if self.aliased.generics.len() != - aliased.generic_params.len() { - return Err(format!("incomplete specialization")); - } - - let mappings = aliased.generic_params.iter() - .zip(self.aliased.generics.iter()) - .collect::>(); - - Ok(Some(Item::Struct(Struct { - name: self.name.clone(), - generic_params: self.generic_params.clone(), - fields: aliased.fields.iter() - .map(|x| (x.0.clone(), x.1.specialize(&mappings), x.2.clone())) - .collect(), - tuple_struct: aliased.tuple_struct, - cfg: self.cfg.clone(), - annotations: self.annotations.clone(), - documentation: self.documentation.clone(), - }))) - } - Item::Enum(ref aliased) => { - Ok(Some(Item::Enum(Enum { - name: self.name.clone(), - repr: aliased.repr.clone(), - values: aliased.values.clone(), - cfg: self.cfg.clone(), - annotations: self.annotations.clone(), - documentation: self.documentation.clone(), - }))) - } - Item::Typedef(ref aliased) => { - Ok(Some(Item::Typedef(Typedef { - name: self.name.clone(), - aliased: aliased.aliased.clone(), - cfg: self.cfg.clone(), - annotations: self.annotations.clone(), - documentation: self.documentation.clone(), - }))) - } - Item::Specialization(ref aliased) => { - if self.aliased.generics.len() != - aliased.generic_params.len() { - return Err(format!("incomplete specialization")); - } - - let mappings = aliased.generic_params.iter() - .zip(self.aliased.generics.iter()) - .collect::>(); - - let generics = aliased.aliased.generics.iter() - .map(|x| x.specialize(&mappings)) - .collect(); - - Specialization { - name: self.name.clone(), - generic_params: self.generic_params.clone(), - aliased: GenericPath::new(aliased.aliased.name.clone(), generics), - cfg: self.cfg.clone(), - annotations: self.annotations.clone(), - documentation: self.documentation.clone(), - }.specialize(library) - } +impl Source for Specialization { + fn write(&self, config: &Config, out: &mut SourceWriter) { + if !config.structure.generic_template_specialization { + return; + } + self.documentation.write(config, out); + if self.generic_values.is_empty() { + out.write("template<"); + let mut first = true; + for t in &self.generic_params { + if first { + first = false; + } else { + out.write(", "); } + out.write(&format!("typename {}", t)); } - None => { - Err(format!("couldn't find aliased type")) + out.write(">"); + out.new_line(); + out.write(&format!("struct {};", self.name)); + } else { + out.write("template<>"); + out.new_line(); + out.write(&format!("struct {}<", self.name)); + let mut first = true; + for t in &self.generic_values { + if first { + first = false; + } else { + out.write(", "); + } + t.write(config, out); } + out.write(&format!("> : public {}", mangle::mangle_path(&self.name, &self.generic_values))); + out.open_brace(); + out.close_brace(true); } } } diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index a3ef672f0..7ce56db9a 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -7,12 +7,11 @@ use std::io::Write; use syn; use bindgen::config::{Config, Language}; -use bindgen::dependencies::Dependencies; -use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Repr, Type}; +use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Type}; +use bindgen::ir::{Repr, Specialization, OpaqueItem, Item}; use bindgen::library::Library; -use bindgen::mangle; -use bindgen::monomorph::Monomorphs; use bindgen::rename::{IdentifierType, RenameRule}; +use bindgen::dependencies::DependencyKind; use bindgen::utilities::{find_first_some, IterHelpers}; use bindgen::writer::{ListType, Source, SourceWriter}; @@ -25,6 +24,7 @@ pub struct Struct { pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, + pub specialization: Option, } impl Struct { @@ -72,60 +72,34 @@ impl Struct { cfg: Cfg::append(mod_cfg, Cfg::load(attrs)), annotations: AnnotationSet::load(attrs)?, documentation: Documentation::load(attrs), + specialization: None, }) } - pub fn is_generic(&self) -> bool { - self.generic_params.len() > 0 - } - - pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { - for &(_, ref ty, _) in &self.fields { - ty.add_dependencies_ignoring_generics(&self.generic_params, library, out); + pub fn as_opaque(&self) -> OpaqueItem { + OpaqueItem { + name: self.name.clone(), + generic_params: self.generic_params.clone(), + annotations: self.annotations.clone(), + documentation: self.documentation.clone(), + cfg: self.cfg.clone(), } } - pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { - // Generic structs can instantiate monomorphs only once they've been - // instantiated. See `instantiate_monomorph` for more details. - if self.is_generic() { - return; + pub fn get_deps(&self, library: &Library) -> Vec<(Item, DependencyKind)> { + let mut ret = Vec::new(); + for f in &self.fields { + ret.extend_from_slice(&f.1.get_items(library, DependencyKind::Normal)); } - - for &(_, ref ty, _) in &self.fields { - ty.add_monomorphs(library, out); + if let Some(ref s) = self.specialization { + ret.push((Item::Specialization(s.clone()), DependencyKind::Normal)); } + ret } - pub fn instantiate_monomorph(&self, library: &Library, generic_values: &Vec, out: &mut Monomorphs) { - assert!(self.generic_params.len() > 0 && - self.generic_params.len() == generic_values.len()); - - let mappings = self.generic_params.iter() - .zip(generic_values.iter()) - .collect::>(); - - let monomorph = Struct { - name: mangle::mangle_path(&self.name, generic_values), - generic_params: vec![], - fields: self.fields.iter() - .map(|x| (x.0.clone(), x.1.specialize(&mappings), x.2.clone())) - .collect(), - tuple_struct: self.tuple_struct, - cfg: self.cfg.clone(), - annotations: self.annotations.clone(), - documentation: self.documentation.clone(), - }; - - // Instantiate any monomorphs for any generic paths we may have just created. - monomorph.add_monomorphs(library, out); - - out.insert_struct(self, monomorph, generic_values.clone()); - } - - pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { + pub fn mangle_paths(&mut self) { for &mut (_, ref mut ty, _) in &mut self.fields { - ty.mangle_paths(monomorphs); + ty.mangle_paths(); } } @@ -173,7 +147,7 @@ impl Source for Struct { if config.language == Language::C { out.write("typedef struct"); } else { - out.write(&format!("struct {}", self.name)); + out.write(&format!("extern \"C\" struct {}", self.name)); } out.open_brace(); diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index b8bc1611a..8f002f650 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -9,12 +9,12 @@ use syn; use bindgen::cdecl; use bindgen::config::Config; -use bindgen::dependencies::Dependencies; use bindgen::ir::{Documentation, GenericPath, Item, Path}; +use bindgen::dependencies::DependencyKind; use bindgen::library::Library; -use bindgen::monomorph::Monomorphs; use bindgen::utilities::IterHelpers; use bindgen::writer::{Source, SourceWriter}; +use bindgen::mangle; #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum PrimitiveType { @@ -303,125 +303,127 @@ impl Type { } } - pub fn add_dependencies_ignoring_generics(&self, generic_params: &Vec, library: &Library, out: &mut Dependencies) { + + pub fn mangle_paths(&mut self) { match self { - &Type::ConstPtr(ref ty) => { - ty.add_dependencies_ignoring_generics(generic_params, library, out); + &mut Type::ConstPtr(ref mut ty) => { + ty.mangle_paths(); } - &Type::Ptr(ref ty) => { - ty.add_dependencies_ignoring_generics(generic_params, library, out); + &mut Type::Ptr(ref mut ty) => { + ty.mangle_paths(); } - &Type::Path(ref path) => { - for generic_value in &path.generics { - generic_value.add_dependencies_ignoring_generics(generic_params, library, out); - } - if !generic_params.contains(&path.name) { - if let Some(item) = library.get_item(&path.name) { - if !out.items.contains(&path.name) { - out.items.insert(path.name.clone()); - - item.add_dependencies(library, out); - - out.order.push(item); - } - } else { - warn!("can't find {}", path.name); - } - } + &mut Type::Path(ref mut path) => { + path.name = path.mangle(); + path.generics = Vec::new(); } - &Type::Primitive(_) => { } - &Type::Array(ref ty, _) => { - ty.add_dependencies_ignoring_generics(generic_params, library, out); + &mut Type::Primitive(_) => { } + &mut Type::Array(ref mut ty, _) => { + ty.mangle_paths(); } - &Type::FuncPtr(ref ret, ref args) => { - ret.add_dependencies_ignoring_generics(generic_params, library, out); + &mut Type::FuncPtr(ref mut ret, ref mut args) => { + ret.mangle_paths(); for arg in args { - arg.add_dependencies_ignoring_generics(generic_params, library, out); + arg.mangle_paths(); } } } } - pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { - self.add_dependencies_ignoring_generics(&Vec::new(), library, out) - } - - pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { - match self { - &Type::ConstPtr(ref ty) => { - ty.add_monomorphs(library, out); - } - &Type::Ptr(ref ty) => { - ty.add_monomorphs(library, out); - } - &Type::Path(ref path) => { - if path.generics.len() == 0 || - out.contains(&path) { - return; - } - - let item = library.get_item(&path.name); - if let Some(item) = item { - match item { - Item::OpaqueItem(ref x) => { - x.instantiate_monomorph(&path.generics, out); - }, - Item::Struct(ref x) => { - x.instantiate_monomorph(library, &path.generics, out); - }, - Item::Enum(..) => { - warn!("cannot instantiate a generic enum") - }, - Item::Typedef(..) => { - warn!("cannot instantiate a generic typedef") - }, - Item::Specialization(..) => { - warn!("cannot instantiate a generic specialization") - }, - } - } - } - &Type::Primitive(_) => { } - &Type::Array(ref ty, _) => { - ty.add_monomorphs(library, out); - } - &Type::FuncPtr(ref ret, ref args) => { - ret.add_monomorphs(library, out); + pub fn get_items(&self, + library: &Library, + kind: DependencyKind) + -> Vec<(Item, DependencyKind)> { + match *self { + Type::ConstPtr(ref tpe) | + Type::Ptr(ref tpe) => tpe.get_items(library, DependencyKind::Ptr), + Type::Array(ref tpe, _) => tpe.get_items(library, DependencyKind::Normal), + Type::Primitive(..) => Vec::new(), + Type::FuncPtr(ref ret, ref args) => { + let mut ret = ret.get_items(library, DependencyKind::Normal); for arg in args { - arg.add_monomorphs(library, out); + ret.extend_from_slice(&arg.get_items(library, DependencyKind::Normal)); } + ret } - } - } - - pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { - match self { - &mut Type::ConstPtr(ref mut ty) => { - ty.mangle_paths(monomorphs); - } - &mut Type::Ptr(ref mut ty) => { - ty.mangle_paths(monomorphs); - } - &mut Type::Path(ref mut path) => { - if path.generics.len() == 0 { - return; - } - - if let Some(mangled) = monomorphs.mangle_path(path) { - path.name = mangled.clone(); - path.generics = Vec::new(); + Type::Path(ref path) => { + if let Some(value) = library.get_item(&path.name) { + match value { + Item::Struct(s) => { + if s.generic_params.is_empty() { + vec![(Item::Struct(s), kind)] + } else { + let mut ret = Vec::new(); + let mappings = s.generic_params + .iter() + .zip(path.generics.iter()) + .collect::>(); + + let specialized = super::Specialization { + name: s.name.clone(), + annotations: s.annotations.clone(), + generic_params: s.generic_params.clone(), + documentation: s.documentation.clone(), + generic_values: path.generics.clone(), + cfg: s.cfg.clone(), + }; + + let monomorph = super::Struct { + name: mangle::mangle_path(&s.name, &path.generics), + fields: s.fields + .iter() + .map(|&(ref name, ref tpe, ref doc)| (name.clone(), tpe.specialize(&mappings), doc.clone())) + .collect(), + generic_params: vec![], + specialization: Some(specialized), + ..s + }; + ret.push((Item::Struct(monomorph), kind)); + + ret + } + } + Item::Typedef(t) => { + if t.generic_params.is_empty() { + vec![(Item::Typedef(t), kind)] + } else { + let mappings = t.generic_params + .iter() + .zip(path.generics.iter()) + .collect::>(); + let specialized = super::Specialization { + name: t.name.clone(), + annotations: t.annotations.clone(), + generic_params: t.generic_params.clone(), + documentation: t.documentation.clone(), + generic_values: path.generics.clone(), + cfg: t.cfg.clone(), + }; + let monomorph = super::Typedef { + name: mangle::mangle_path(&t.name, &path.generics), + generic_params: vec![], + aliased: t.aliased.specialize(&mappings), + specialization: Some(specialized), + ..t + }; + vec![(Item::Typedef(monomorph), kind)] + } + } + Item::OpaqueItem(o) => { + if o.generic_params.is_empty() { + vec![(Item::OpaqueItem(o), kind)] + } else { + let monomorph = super::OpaqueItem { + name: mangle::mangle_path(&o.name, &path.generics), + generic_params: vec![], + ..o + }; + vec![(Item::OpaqueItem(monomorph), kind)] + } + } + i => vec![(i, kind)], + } } else { - warn!("cannot find a monomorph for {:?}", path); - } - } - &mut Type::Primitive(_) => { } - &mut Type::Array(ref mut ty, _) => { - ty.mangle_paths(monomorphs); - } - &mut Type::FuncPtr(ref mut ret, ref mut args) => { - ret.mangle_paths(monomorphs); - for arg in args { - arg.mangle_paths(monomorphs); + Vec::new() } } } diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs index 874fbd22e..59efa0a45 100644 --- a/src/bindgen/ir/typedef.rs +++ b/src/bindgen/ir/typedef.rs @@ -8,11 +8,11 @@ use std::io::Write; use syn; use bindgen::config::Config; -use bindgen::dependencies::Dependencies; -use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Path, Type}; +use bindgen::dependencies::DependencyKind; +use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Path, Type, Specialization, Item}; use bindgen::library::Library; -use bindgen::monomorph::Monomorphs; use bindgen::writer::{Source, SourceWriter}; +use bindgen::utilities::IterHelpers; /// A type alias that is represented as a C typedef #[derive(Debug, Clone)] @@ -21,22 +21,71 @@ pub struct Typedef { pub aliased: Type, pub cfg: Option, pub annotations: AnnotationSet, + pub generic_params: Vec, + pub generic_values: Vec, pub documentation: Documentation, + pub specialization: Option, } impl Typedef { pub fn load(name: String, - ty: &syn::Ty, attrs: &Vec, + generics: &syn::Generics, + ty: &syn::Ty, mod_cfg: &Option) -> Result { if let Some(x) = Type::load(ty)? { - Ok(Typedef { - name: name, - aliased: x, - cfg: Cfg::append(mod_cfg, Cfg::load(attrs)), - annotations: AnnotationSet::load(attrs)?, - documentation: Documentation::load(attrs), - }) + match ty { + &syn::Ty::Path(_, ref p) => { + let generic_params = generics + .ty_params + .iter() + .map(|x| x.ident.to_string()) + .collect::>(); + + + let generic_values = match p.segments[0].parameters { + syn::PathParameters::AngleBracketed(ref d) => { + if !d.lifetimes.is_empty() || + !d.bindings.is_empty() + { + return Err(format!("path generic parameter contains bindings, or lifetimes")); + } + + d.types.iter().try_skip_map(|x| Type::load(x))? + } + syn::PathParameters::Parenthesized(_) => { + return Err(format!("path contains parentheses")); + } + }; + Ok(Typedef { + name: name, + aliased: x, + generic_params, + generic_values, + specialization: None, + cfg: Cfg::append(mod_cfg, Cfg::load(attrs)), + annotations: AnnotationSet::load(attrs)?, + documentation: Documentation::load(attrs), + }) + } + _ if generics.ty_params.is_empty() && + generics.lifetimes.is_empty() => { + Ok(Typedef { + name: name, + aliased: x, + generic_params: Vec::new(), + generic_values: Vec::new(), + specialization: None, + cfg: Cfg::append(mod_cfg, Cfg::load(attrs)), + annotations: AnnotationSet::load(attrs)?, + documentation: Documentation::load(attrs), + }) + } + i => { + println!("{:?}", i); + unimplemented!() + } + } } else { Err(format!("cannot have a typedef of a zero sized type")) } @@ -62,16 +111,17 @@ impl Typedef { } } - pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { - self.aliased.add_dependencies(library, out); + pub fn mangle_paths(&mut self) { + self.aliased.mangle_paths(); } - pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { - self.aliased.add_monomorphs(library, out); - } - - pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { - self.aliased.mangle_paths(monomorphs); + pub fn get_deps(&self, library: &Library) -> Vec<(Item, DependencyKind)> { + assert!(self.generic_params.is_empty()); + let mut ret = self.aliased.get_items(library, DependencyKind::Normal); + if let Some(ref s) = self.specialization { + ret.push((Item::Specialization(s.clone()), DependencyKind::Normal)); + } + ret } } diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index adb78f394..cdf56ce07 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -4,14 +4,12 @@ use std::collections::BTreeMap; use std::collections::HashMap; -use std::mem; use bindgen::bindings::Bindings; -use bindgen::config::{Config, Language}; -use bindgen::dependencies::Dependencies; +use bindgen::config::Config; use bindgen::ir::{Enum, Function, Item, OpaqueItem}; -use bindgen::ir::{Path, Specialization, Struct, Typedef}; -use bindgen::monomorph::{Monomorphs, TemplateSpecialization}; +use bindgen::ir::{Path, Struct, Typedef}; +use bindgen::dependencies::DependencyList; #[derive(Debug, Clone)] pub struct Library { @@ -20,9 +18,7 @@ pub struct Library { structs: BTreeMap, opaque_items: BTreeMap, typedefs: BTreeMap, - specializations: BTreeMap, functions: Vec, - template_specializations: Vec, } impl Library { @@ -31,7 +27,6 @@ impl Library { structs: BTreeMap, opaque_items: BTreeMap, typedefs: BTreeMap, - specializations: BTreeMap, functions: Vec) -> Library { Library { config: config, @@ -39,41 +34,19 @@ impl Library { structs: structs, opaque_items: opaque_items, typedefs: typedefs, - specializations: specializations, functions: functions, - template_specializations: Vec::new(), } } pub fn generate(mut self) -> Result { self.transfer_annotations(); - self.rename_items(); - self.specialize_items(); - self.instantiate_monomorphs(); - let mut dependencies = Dependencies::new(); + let deps = DependencyList::new(&self.functions, &self, &self.config); + deps.print(); - for function in &self.functions { - function.add_dependencies(&self, &mut dependencies); - } - - if self.config.structure.generic_template_specialization && - self.config.language == Language::Cxx { - for template_specialization in &self.template_specializations { - template_specialization.add_dependencies(&self, &mut dependencies); - } - } - - dependencies.sort(); - - let items = dependencies.order; - let functions = mem::replace(&mut self.functions, Vec::new()); - let template_specializations = mem::replace(&mut self.template_specializations, Vec::new()); - - Ok(Bindings::new(self.config.clone(), - items, - functions, - template_specializations)) + // Gather only the items that we need for this + // `extern "c"` interface + Ok(Bindings::new(self.config, deps.calculate_order())) } pub fn get_item(&self, p: &Path) -> Option { @@ -89,23 +62,10 @@ impl Library { if let Some(x) = self.typedefs.get(p) { return Some(Item::Typedef(x.clone())); } - if let Some(x) = self.specializations.get(p) { - return Some(Item::Specialization(x.clone())); - } None } - fn insert_item(&mut self, item: Item) { - match item { - Item::OpaqueItem(x) => { self.opaque_items.insert(x.name.clone(), x); }, - Item::Struct(x) => { self.structs.insert(x.name.clone(), x); }, - Item::Enum(x) => { self.enums.insert(x.name.clone(), x); }, - Item::Typedef(x) => { self.typedefs.insert(x.name.clone(), x); }, - Item::Specialization(x) => { self.specializations.insert(x.name.clone(), x); }, - }; - } - fn transfer_annotations(&mut self) { let mut annotations = HashMap::new(); @@ -151,102 +111,6 @@ impl Library { x.annotations = annotations; continue; } - if let Some(x) = self.specializations.get_mut(&alias_path) { - if !x.annotations.is_empty() { - warn!("can't transfer annotations from typedef to alias ({}) that already has annotations.", - alias_path); - continue; - } - x.annotations = annotations; - continue; - } - } - } - - fn rename_items(&mut self) { - for item in self.structs.values_mut() { - item.rename_fields(&self.config); - } - - for item in self.enums.values_mut() { - item.rename_values(&self.config); - } - - for item in &mut self.functions { - item.rename_args(&self.config); - } - } - - fn specialize_items(&mut self) { - let mut specializations = Vec::new(); - - for specialization in self.specializations.values() { - match specialization.specialize(&self) { - Ok(Some(specialization)) => { - specializations.push(specialization); - } - Ok(None) => { } - Err(msg) => { - warn!("specializing {} failed - ({})", specialization.name.clone(), msg); - } - } } - - for specialization in specializations { - self.insert_item(specialization); - } - - self.specializations.clear(); - } - - fn instantiate_monomorphs(&mut self) { - assert!(self.specializations.len() == 0); - - let mut monomorphs = Monomorphs::new(); - - for x in self.structs.values() { - x.add_monomorphs(self, &mut monomorphs); - } - for x in self.typedefs.values() { - x.add_monomorphs(self, &mut monomorphs); - } - for x in &self.functions { - x.add_monomorphs(self, &mut monomorphs); - } - - for monomorph in monomorphs.drain_structs() { - self.structs.insert(monomorph.name.clone(), monomorph); - } - for monomorph in monomorphs.drain_opaques() { - self.opaque_items.insert(monomorph.name.clone(), monomorph); - } - - let opaque_items = mem::replace(&mut self.opaque_items, BTreeMap::new()); - for (path, item) in opaque_items { - if item.generic_params.len() != 0 { - continue; - } - self.opaque_items.insert(path, item); - } - - let structs = mem::replace(&mut self.structs, BTreeMap::new()); - for (path, item) in structs { - if item.generic_params.len() != 0 { - continue; - } - self.structs.insert(path, item); - } - - for x in self.structs.values_mut() { - x.mangle_paths(&monomorphs); - } - for x in self.typedefs.values_mut() { - x.mangle_paths(&monomorphs); - } - for x in &mut self.functions { - x.mangle_paths(&monomorphs); - } - - self.template_specializations = monomorphs.drain_template_specializations(); } } diff --git a/src/bindgen/mangle.rs b/src/bindgen/mangle.rs index 1eda9a7c3..b0b955752 100644 --- a/src/bindgen/mangle.rs +++ b/src/bindgen/mangle.rs @@ -9,11 +9,10 @@ pub fn mangle_path(name: &str, generic_values: &[Type]) -> String { } fn internal_mangle_path(name: &str, generic_values: &[Type], last_in_parent: bool) -> String { - assert!(!name.contains("_")); - if generic_values.is_empty() { return String::from(name); } + assert!(!name.contains("_")); let mut out = String::from(name); diff --git a/src/bindgen/mod.rs b/src/bindgen/mod.rs index 7d124490f..101bb2f8c 100644 --- a/src/bindgen/mod.rs +++ b/src/bindgen/mod.rs @@ -43,7 +43,6 @@ mod dependencies; mod ir; mod library; mod mangle; -mod monomorph; mod rename; mod rust_lib; mod utilities; diff --git a/src/bindgen/monomorph.rs b/src/bindgen/monomorph.rs deleted file mode 100644 index 5b2dea445..000000000 --- a/src/bindgen/monomorph.rs +++ /dev/null @@ -1,118 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use std::collections::HashMap; -use std::mem; - -use bindgen::dependencies::Dependencies; -use bindgen::ir::{GenericPath, OpaqueItem, Path, Struct, Type}; -use bindgen::library::Library; - -#[derive(Clone, Debug)] -pub struct TemplateSpecialization { - pub generic: Struct, - pub monomorphs: Vec<(Path, Vec)>, -} - -impl TemplateSpecialization { - fn new(generic: Struct) -> TemplateSpecialization { - TemplateSpecialization { - generic: generic, - monomorphs: Vec::new(), - } - } - - pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { - for &(_, ref generic_values) in &self.monomorphs { - for generic_value in generic_values { - generic_value.add_dependencies(library, out); - } - } - } -} - -#[derive(Clone, Debug)] -pub struct Monomorphs { - replacements: HashMap, - opaques: Vec, - structs: Vec, - templates: HashMap, -} - -impl Monomorphs { - pub fn new() -> Monomorphs { - Monomorphs { - replacements: HashMap::new(), - opaques: Vec::new(), - structs: Vec::new(), - templates: HashMap::new(), - } - } - - pub fn contains(&self, path: &GenericPath) -> bool { - self.replacements.contains_key(path) - } - - pub fn insert_struct(&mut self, - generic: &Struct, - monomorph: Struct, - parameters: Vec) { - // Add extra information for struct instantiations so we can use template - // specialization to make using the type more ergonomic. - self.templates.entry(generic.name.clone()) - .or_insert_with(|| TemplateSpecialization::new(generic.clone())) - .monomorphs.push((monomorph.name.clone(), parameters.clone())); - - let replacement_path = GenericPath::new(generic.name.clone(), parameters); - - debug_assert!(generic.generic_params.len() > 0); - debug_assert!(!self.contains(&replacement_path)); - - self.replacements.insert(replacement_path, monomorph.name.clone()); - self.structs.push(monomorph); - } - - pub fn insert_opaque(&mut self, - generic: &OpaqueItem, - monomorph: OpaqueItem, - parameters: Vec) { - let replacement_path = GenericPath::new(generic.name.clone(), parameters); - - debug_assert!(generic.generic_params.len() > 0); - debug_assert!(!self.contains(&replacement_path)); - - self.replacements.insert(replacement_path, monomorph.name.clone()); - self.opaques.push(monomorph); - } - - pub fn mangle_path(&self, path: &GenericPath) -> Option<&Path> { - self.replacements.get(path) - } - - pub fn drain_opaques(&mut self) -> Vec { - mem::replace(&mut self.opaques, Vec::new()) - } - - pub fn drain_structs(&mut self) -> Vec { - mem::replace(&mut self.structs, Vec::new()) - } - - pub fn drain_template_specializations(&mut self) -> Vec { - let mut not_mangled = mem::replace(&mut self.templates, HashMap::new()); - let mut mangled = Vec::new(); - - // The generic type arguments in `templates` need to be mangled - for (_, mut template) in not_mangled.drain() { - for &mut (_, ref mut generic_values) in &mut template.monomorphs { - for generic_value in generic_values { - generic_value.mangle_paths(&self); - } - } - - mangled.push(template); - } - - mangled - } -} diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs index 8b3ff17d5..1acdf2c05 100644 --- a/src/bindgen/writer.rs +++ b/src/bindgen/writer.rs @@ -154,23 +154,6 @@ impl<'a, F: Write> SourceWriter<'a, F> { self.max_line_length = cmp::max(self.max_line_length, self.line_length); } - pub fn write_horizontal_source_list<'b, S: Source>(&mut self, items: &Vec, list_type: ListType<'b>) { - for (i, ref item) in items.iter().enumerate() { - item.write(self.config, self); - - match list_type { - ListType::Join(text) => { - if i != items.len() - 1 { - self.write(&text); - } - } - ListType::Cap(text) => { - self.write(&text); - } - } - } - } - pub fn write_vertical_list<'b>(&mut self, items: &Vec, list_type: ListType<'b>) { let align_length = self.line_length_for_align(); self.push_set_spaces(align_length); diff --git a/src/lib.rs b/src/lib.rs index 8367add42..71d5b0a5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ extern crate serde_derive; extern crate serde_json; extern crate syn; extern crate toml; +extern crate petgraph; mod bindgen; diff --git a/src/main.rs b/src/main.rs index 4f2382a1f..7f5442a42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ extern crate serde_derive; extern crate serde_json; extern crate syn; extern crate toml; +extern crate petgraph; use clap::{Arg, ArgMatches, App}; From f7a9aea17d90318a779f98a3826be5a170f2de42 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 18 Aug 2017 10:30:09 +0200 Subject: [PATCH 2/3] Fix possible bug petgraph::Graphs EdgeIndices are not stable over removing (see https://docs.rs/petgraph/0.4.5/petgraph/graph/struct.Graph.html#method.remove_edge), so we should not remove edges by a list of id's. --- compile-tests/cycle.rs | 1 + src/bindgen/dependencies.rs | 37 ++++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/compile-tests/cycle.rs b/compile-tests/cycle.rs index ef5ac9c69..a70884bb3 100644 --- a/compile-tests/cycle.rs +++ b/compile-tests/cycle.rs @@ -7,6 +7,7 @@ pub struct Foo { #[repr(C)] pub struct Bar { a: *mut Foo, + b: Foo, } #[no_mangle] diff --git a/src/bindgen/dependencies.rs b/src/bindgen/dependencies.rs index 170e2ff1f..c031d8431 100644 --- a/src/bindgen/dependencies.rs +++ b/src/bindgen/dependencies.rs @@ -5,7 +5,8 @@ use std::fmt::{self, Display}; use std::collections::HashMap; use petgraph::{Graph, Direction}; -use petgraph::graph::{NodeIndex, EdgeIndex}; +use petgraph::graph::NodeIndex; +use petgraph::visit::EdgeRef; use bindgen::ir::{Function, OpaqueItem, Item}; use bindgen::library::Library; @@ -84,8 +85,7 @@ impl DependencyList { id: NodeIndex, o: OpaqueItem, ret: &mut Vec, - ) -> Option> { - use petgraph::visit::EdgeRef; + ) -> Option { // It is possible to have multiple edges with different // dependencies between nodes, so we need to group the edges by // theire source @@ -100,28 +100,47 @@ impl DependencyList { .filter(|edges| { edges.iter().all(|e| e.weight() == &DependencyKind::Ptr) }) - .flat_map(|edges| edges.iter().map(|e| e.id())) .collect::>(); // If there is node ptr dependency we are done here if edges.is_empty() { None } else { ret.push(Item::OpaqueItem(o)); - Some(edges) + Some(id) } } fn remove_cycle(&mut self, id: NodeIndex, ret: &mut Vec) { - let edges = { + let nid = { let node = self.graph.node_weight(id).expect("Got id from graph above"); match *node { Item::Struct(ref s) => self.generate_opaque_item(id, s.as_opaque(), ret), _ => return, } }; - if let Some(edges) = edges { - for e in edges { - self.graph.remove_edge(e); + if let Some(nid) = nid { + // We could not simply remove all edges in a given list here + // because the edge indices may change on removal + // Because of the borrow checker we could also not use + // a while let loop hereā€¦ + let mut skip_counter = 0; + loop { + let id = if let Some(e) = self.graph + .edges_directed(nid, Direction::Incoming) + .skip(skip_counter) + .next() + { + if *e.weight() == DependencyKind::Ptr { + e.id() + } else { + // Ignore edges with DependencyKind::Normal + skip_counter += 1; + continue; + } + } else { + break; + }; + self.graph.remove_edge(id); } } } From 28318203569e83e30f43ae26a807becc8c6fa88a Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Mon, 25 Sep 2017 20:44:45 +0200 Subject: [PATCH 3/3] Add a mechanism to reproduce the old output order --- src/bindgen/dependencies.rs | 56 ++++++++++++++++++++++++++----------- src/bindgen/ir/item.rs | 24 ++++++++++++++++ src/bindgen/library.rs | 1 - 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/bindgen/dependencies.rs b/src/bindgen/dependencies.rs index c031d8431..1db7d6e5f 100644 --- a/src/bindgen/dependencies.rs +++ b/src/bindgen/dependencies.rs @@ -8,10 +8,17 @@ use petgraph::{Graph, Direction}; use petgraph::graph::NodeIndex; use petgraph::visit::EdgeRef; -use bindgen::ir::{Function, OpaqueItem, Item}; +use bindgen::ir::{Function, OpaqueItem, Item, ItemKind}; use bindgen::library::Library; use bindgen::config::Config; +const ITEM_ORDER: [ItemKind; 6] = [ItemKind::Enum, + ItemKind::OpaqueItem, + ItemKind::Struct, + ItemKind::Typedef, + ItemKind::Function, + ItemKind::Specialization]; + #[derive(Debug, Clone, Copy, PartialEq)] pub enum DependencyKind { Ptr, @@ -149,11 +156,37 @@ impl DependencyList { let mut ret = Vec::new(); let mut cycle_counter = 0; while self.graph.node_count() > 0 { - // find structs without any dependency - let externals = self.graph - .externals(Direction::Outgoing) - .collect::>(); - if externals.is_empty() { + let mut all_empty = true; + let mut pos = 0; + while pos < ITEM_ORDER.len() { + let current_item_kind = ITEM_ORDER[pos]; + // find structs without any dependency + let externals = self.graph + .externals(Direction::Outgoing) + .filter(|idx| { + self.graph.node_weight(*idx) + .expect("Node is there because we got the id from the graph above") + == current_item_kind + }) + .collect::>(); + if externals.is_empty() { + pos += 1; + } else { + pos = 0; + all_empty = false; + cycle_counter = 0; + // Iterate over all nodes without dependency + // 1. Remove them from the graph + // 2. Push them to the orderd struct list + for idx in externals { + if let Some(mut s) = self.graph.remove_node(idx) { + s.mangle_paths(); + ret.push(s); + } + } + } + } + if all_empty { if cycle_counter >= self.graph.node_count() { self.print(); panic!("Could not remove cycle"); @@ -167,17 +200,6 @@ impl DependencyList { .expect("Graph is not empty"); self.remove_cycle(id, &mut ret); cycle_counter += 1; - } else { - cycle_counter = 0; - // Iterate over all nodes without dependency - // 1. Remove them from the graph - // 2. Push them to the orderd struct list - for idx in externals { - if let Some(mut s) = self.graph.remove_node(idx) { - s.mangle_paths(); - ret.push(s); - } - } } } ret diff --git a/src/bindgen/ir/item.rs b/src/bindgen/ir/item.rs index 668a3124b..519f194f8 100644 --- a/src/bindgen/ir/item.rs +++ b/src/bindgen/ir/item.rs @@ -11,6 +11,16 @@ use std::hash::{Hash, Hasher}; use std::fmt::{self, Display}; use std::io::Write; +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ItemKind { + Enum, + Struct, + OpaqueItem, + Typedef, + Function, + Specialization +} + /// An item is any type of rust item #[derive(Debug, Clone)] pub enum Item { @@ -70,6 +80,20 @@ impl Item { } } +impl<'a> PartialEq for &'a Item { + fn eq(&self, rhs: &ItemKind) -> bool { + match (*self, *rhs) { + (&Item::Enum(_), ItemKind::Enum) | + (&Item::Specialization(_), ItemKind::Specialization) | + (&Item::Struct(_), ItemKind::Struct) | + (&Item::Typedef(_), ItemKind::Typedef) | + (&Item::OpaqueItem(_), ItemKind::OpaqueItem) | + (&Item::Function(_), ItemKind::Function) => true, + _ => false + } + } +} + impl Display for Item { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index cdf56ce07..bec24b31b 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -42,7 +42,6 @@ impl Library { self.transfer_annotations(); let deps = DependencyList::new(&self.functions, &self, &self.config); - deps.print(); // Gather only the items that we need for this // `extern "c"` interface