cpp_vwrap_gen/
lib.rs

1use std::io::Seek;
2use std::io::Read;
3use std::io::Write;
4use std::convert::TryInto;
5
6fn gen_method(mut outfile: &std::fs::File, mut outfile_hdr: &std::fs::File, class: &clang::Entity, method: &clang::Entity) -> std::io::Result<()> {
7    let mut outbuf = Vec::new();
8    let mut method_name = method.get_name().unwrap();
9
10    if let clang::EntityKind::Destructor = method.get_kind() {
11        method_name = "destructor".to_string();
12    };
13
14    if let clang::EntityKind::Constructor = method.get_kind() {
15        method_name = "constructor".to_string();
16    };
17
18    // write sig to outbuf
19    write!(&mut outbuf, "{} vwrap_{}_{}({} *thiz", method.get_result_type().unwrap().get_display_name(),
20        class.get_type().unwrap().get_display_name(),
21        method_name, class.get_type().unwrap().get_display_name())?;
22    if let Some(arguments) = method.get_arguments() {
23        for argument in arguments {
24            write!(&mut outbuf, ", ")?;
25
26            let range = argument.get_range().unwrap();
27            let loc_start = range.get_start().get_spelling_location();
28            let loc_end = range.get_end().get_spelling_location();
29            assert!(loc_start.file == loc_end.file);
30            assert!(loc_start.offset < loc_end.offset);
31            let filepath = loc_start.file.unwrap().get_path();
32
33            let mut file = std::fs::File::open(filepath).unwrap();
34            file.seek(std::io::SeekFrom::Start(loc_start.offset.into())).unwrap();
35            let mut buf = vec![0; (loc_end.offset - loc_start.offset).try_into().unwrap()];
36            file.read_exact(&mut buf)?;
37            outbuf.write_all(&buf)?;
38        }
39    }
40    write!(&mut outbuf, ")")?;
41
42    // write to header
43    outfile_hdr.write_all(&outbuf)?;
44    writeln!(&mut outfile_hdr, ";")?;
45
46    // write to sourcefile
47    outfile.write_all(&outbuf)?;
48    writeln!(&mut outfile, " {{")?;
49
50    write!(&mut outfile, "    ")?;
51    if method.get_result_type().unwrap().get_kind() != clang::TypeKind::Void {
52        write!(&mut outfile, "return ")?;
53    }
54
55    if let clang::EntityKind::Constructor = method.get_kind() {
56        write!(&mut outfile, "new(thiz) ")?;
57    }
58    else {
59        write!(&mut outfile, "thiz->")?;
60    }
61    write!(&mut outfile, "{}(", method.get_name().unwrap())?;
62    if let Some(arguments) = method.get_arguments() {
63        let mut first = true;
64        for argument in arguments {
65            if first {
66                first = false;
67            }
68            else {
69                write!(&mut outfile, ", ")?;
70            }
71
72            write!(&mut outfile, "{}", argument.get_display_name().unwrap())?;
73        }
74    }
75    writeln!(&mut outfile, ");")?;
76
77    writeln!(&mut outfile, "}}")?;
78
79    Ok(())
80}
81
82pub fn generate<F: Into<std::path::PathBuf>, S: AsRef<str>>(outpath_src: F, outpath_hdr: F, inpath: F, arguments: &[S]) -> std::io::Result<()> {
83    let clang = clang::Clang::new().unwrap();
84    let index = clang::Index::new(&clang, false, false);
85
86    let inpath2 = inpath.into();
87    let outpath_hdr2 = outpath_hdr.into();
88    let mut parser = index.parser(&inpath2);
89    let mut clang_args:Vec<std::string::String> = Vec::new();
90
91    if let Some(clang) = clang_sys::support::Clang::find(None, &[]) {
92        if let Some(paths) = clang.cpp_search_paths {
93            for path in paths.into_iter() {
94                clang_args.push("-isystem".to_string());
95                clang_args.push(path.to_str().unwrap().to_string());
96            }
97        }
98    }
99
100    for arg in arguments {
101        clang_args.push(arg.as_ref().to_string());
102    }
103
104    parser.arguments(&clang_args);
105
106    let tu = parser.parse().unwrap();
107    let mut has_diag = false;
108    for diag in &tu.get_diagnostics() {
109        eprintln!("{}", diag.formatter().format());
110        has_diag = true;
111    }
112    if has_diag {
113        std::process::exit(1);
114    }
115
116    let mut outfile = std::fs::File::create(outpath_src.into())?;
117    let mut outfile_hdr = std::fs::File::create(&outpath_hdr2)?;
118
119    writeln!(&mut outfile_hdr, "#pragma once")?;
120    writeln!(&mut outfile_hdr, "#include \"{}\"", inpath2.display())?;
121
122    writeln!(&mut outfile, "#include \"{}\"", outpath_hdr2.display())?;
123    writeln!(&mut outfile, "#include <new>")?;
124
125    // create list of all classes we found
126    let mut classes = std::collections::HashMap::new();
127    for class in tu.get_entity().get_children() {
128        if class.get_kind() != clang::EntityKind::ClassDecl {
129            continue;
130        }
131
132        let type_ =  class.get_type().unwrap();
133        classes.insert(type_.get_display_name(), class);
134    }
135
136    for class in classes.values() {
137        let type_ =  class.get_type().unwrap();
138        let mut base_classes = Vec::new();
139        let mut changed = true;
140
141        // create list of all classes this inherits from
142        base_classes.push(type_.get_display_name());
143        while changed {
144            changed = false;
145
146            for entity in class.get_children() {
147                if entity.get_kind() != clang::EntityKind::BaseSpecifier {
148                    continue;
149                }
150
151                let display_name = entity.get_type().unwrap().get_display_name();
152                if !base_classes.contains(&display_name) {
153                    base_classes.push(display_name);
154                    changed = true;
155                }
156            }
157        }
158
159        for base_class_name in &base_classes {
160            let base_class = classes.get(base_class_name).unwrap();
161
162            for method in &base_class.get_children() {
163                match method.get_kind() {
164                    clang::EntityKind::Method => {
165                        // these are the types we currently can't access properly using bindgen
166                        // we generate wrappers for all inherited functions because we can't cast to the
167                        // base type without generating conversion wrappers.
168                        if class == base_class && !method.is_virtual_method() && !method.is_inline_function() {
169                            continue;
170                        }
171                        // by ignoring overriding methods we ensure we generate one wrapper only,
172                        // by generting it for the original only.
173                        if method.get_overridden_methods().is_some() {
174                            continue;
175                        }
176                        // we can't access non-public methods from a C wrapper.
177                        // XXX: this will go wrong with overriding methods changing visibility
178                        if method.get_accessibility().unwrap() != clang::Accessibility::Public {
179                            continue;
180                        }
181                    },
182                    // generate wrappers for public constructors/destructors of the current class
183                    clang::EntityKind::Destructor | clang::EntityKind::Constructor => {
184                        if class != base_class {
185                            continue;
186                        }
187
188                        if class.is_abstract_record() {
189                            continue;
190                        }
191
192                        if method.get_accessibility().unwrap() != clang::Accessibility::Public {
193                            continue;
194                        }
195                    },
196                    _ => continue
197                }
198
199                gen_method(&outfile, &outfile_hdr, &class, &method)?;
200            }
201        }
202        writeln!(&mut outfile)?;
203    }
204    Ok(())
205}