blob: 3043dcf09be23fb6f97c7822d87e75166566504c [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9mod analysis;
10mod api;
11mod apivec;
12mod codegen_cpp;
13mod codegen_rs;
14#[cfg(test)]
15mod conversion_tests;
16mod convert_error;
17mod doc_attr;
18mod error_reporter;
19mod parse;
20mod utilities;
21
22use analysis::fun::FnAnalyzer;
23use autocxx_parser::IncludeCppConfig;
24pub(crate) use codegen_cpp::CppCodeGenerator;
25pub(crate) use convert_error::ConvertError;
26use itertools::Itertools;
27use syn::{Item, ItemMod};
28
29use crate::{
30 conversion::analysis::deps::HasDependencies, CppCodegenOptions, CppFilePair, UnsafePolicy,
31};
32
33use self::{
34 analysis::{
35 abstract_types::{discard_ignored_functions, mark_types_abstract},
36 allocators::create_alloc_and_frees,
37 casts::add_casts,
38 check_names,
39 constructor_deps::decorate_types_with_constructor_deps,
40 fun::FnPhase,
41 gc::filter_apis_by_following_edges_from_allowlist,
42 pod::analyze_pod_apis,
43 remove_ignored::filter_apis_by_ignored_dependents,
44 replace_hopeless_typedef_targets,
45 tdef::convert_typedef_targets,
46 },
47 api::AnalysisPhase,
48 apivec::ApiVec,
49 codegen_rs::RsCodeGenerator,
50 parse::ParseBindgen,
51};
52
53const LOG_APIS: bool = true;
54
55/// Converts the bindings generated by bindgen into a form suitable
56/// for use with `cxx`.
57/// In fact, most of the actual operation happens within an
58/// individual `BridgeConversion`.
59///
60/// # Flexibility in handling bindgen output
61///
62/// autocxx is inevitably tied to the details of the bindgen output;
63/// e.g. the creation of a 'root' mod when namespaces are enabled.
64/// At the moment this crate takes the view that it's OK to panic
65/// if the bindgen output is not as expected. It may be in future that
66/// we need to be a bit more graceful, but for now, that's OK.
67pub(crate) struct BridgeConverter<'a> {
68 include_list: &'a [String],
69 config: &'a IncludeCppConfig,
70}
71
72/// C++ and Rust code generation output.
73pub(crate) struct CodegenResults {
74 pub(crate) rs: Vec<Item>,
75 pub(crate) cpp: Option<CppFilePair>,
76 pub(crate) cxxgen_header_name: String,
77}
78
79impl<'a> BridgeConverter<'a> {
80 pub fn new(include_list: &'a [String], config: &'a IncludeCppConfig) -> Self {
81 Self {
82 include_list,
83 config,
84 }
85 }
86
87 fn dump_apis<T: AnalysisPhase>(label: &str, apis: &ApiVec<T>) {
88 if LOG_APIS {
89 log::info!(
90 "APIs after {}:\n{}",
91 label,
92 apis.iter()
93 .map(|api| { format!(" {:?}", api) })
94 .sorted()
95 .join("\n")
96 )
97 }
98 }
99
100 fn dump_apis_with_deps(label: &str, apis: &ApiVec<FnPhase>) {
101 if LOG_APIS {
102 log::info!(
103 "APIs after {}:\n{}",
104 label,
105 apis.iter()
106 .map(|api| { format!(" {:?}, deps={}", api, api.format_deps()) })
107 .sorted()
108 .join("\n")
109 )
110 }
111 }
112
113 /// Convert a TokenStream of bindgen-generated bindings to a form
114 /// suitable for cxx.
115 ///
116 /// This is really the heart of autocxx. It parses the output of `bindgen`
117 /// (although really by "parse" we mean to interpret the structures already built
118 /// up by the `syn` crate).
119 pub(crate) fn convert(
120 &self,
121 mut bindgen_mod: ItemMod,
122 unsafe_policy: UnsafePolicy,
123 inclusions: String,
124 cpp_codegen_options: &CppCodegenOptions,
125 ) -> Result<CodegenResults, ConvertError> {
126 match &mut bindgen_mod.content {
127 None => Err(ConvertError::NoContent),
128 Some((_, items)) => {
129 // Parse the bindgen mod.
130 let items_to_process = items.drain(..).collect();
131 let parser = ParseBindgen::new(self.config);
132 let apis = parser.parse_items(items_to_process)?;
133 Self::dump_apis("parsing", &apis);
134 // Inside parse_results, we now have a list of APIs.
135 // We now enter various analysis phases.
136 // Next, convert any typedefs.
137 // "Convert" means replacing bindgen-style type targets
138 // (e.g. root::std::unique_ptr) with cxx-style targets (e.g. UniquePtr).
139 let apis = convert_typedef_targets(self.config, apis);
140 Self::dump_apis("typedefs", &apis);
141 // Now analyze which of them can be POD (i.e. trivial, movable, pass-by-value
142 // versus which need to be opaque).
143 // Specifically, let's confirm that the items requested by the user to be
144 // POD really are POD, and duly mark any dependent types.
145 // This returns a new list of `Api`s, which will be parameterized with
146 // the analysis results. It also returns an object which can be used
147 // by subsequent phases to work out which objects are POD.
148 let analyzed_apis = analyze_pod_apis(apis, self.config)?;
149 Self::dump_apis("pod analysis", &analyzed_apis);
150 let analyzed_apis = replace_hopeless_typedef_targets(self.config, analyzed_apis);
151 let analyzed_apis = add_casts(analyzed_apis);
152 let analyzed_apis = create_alloc_and_frees(analyzed_apis);
153 // Next, figure out how we materialize different functions.
154 // Some will be simple entries in the cxx::bridge module; others will
155 // require C++ wrapper functions. This is probably the most complex
156 // part of `autocxx`. Again, this returns a new set of `Api`s, but
157 // parameterized by a richer set of metadata.
158 Self::dump_apis("adding casts", &analyzed_apis);
159 let analyzed_apis =
160 FnAnalyzer::analyze_functions(analyzed_apis, unsafe_policy, self.config);
161 // If any of those functions turned out to be pure virtual, don't attempt
162 // to generate UniquePtr implementations for the type, since it can't
163 // be instantiated.
164 Self::dump_apis("analyze fns", &analyzed_apis);
165 let analyzed_apis = mark_types_abstract(analyzed_apis);
166 Self::dump_apis("marking abstract", &analyzed_apis);
167 // Annotate structs with a note of any copy/move constructors which
168 // we may want to retain to avoid garbage collecting them later.
169 let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis);
170 Self::dump_apis_with_deps("adding constructor deps", &analyzed_apis);
171 let analyzed_apis = discard_ignored_functions(analyzed_apis);
172 Self::dump_apis_with_deps("ignoring ignorable fns", &analyzed_apis);
173 // Remove any APIs whose names are not compatible with cxx.
174 let analyzed_apis = check_names(analyzed_apis);
175 // During parsing or subsequent processing we might have encountered
176 // items which we couldn't process due to as-yet-unsupported features.
177 // There might be other items depending on such things. Let's remove them
178 // too.
179 let analyzed_apis = filter_apis_by_ignored_dependents(analyzed_apis);
180 Self::dump_apis_with_deps("removing ignored dependents", &analyzed_apis);
181
182 // We now garbage collect the ones we don't need...
183 let mut analyzed_apis =
184 filter_apis_by_following_edges_from_allowlist(analyzed_apis, self.config);
185 // Determine what variably-sized C types (e.g. int) we need to include
186 analysis::ctypes::append_ctype_information(&mut analyzed_apis);
187 Self::dump_apis_with_deps("GC", &analyzed_apis);
188 // And finally pass them to the code gen phases, which outputs
189 // code suitable for cxx to consume.
190 let cxxgen_header_name = cpp_codegen_options.cxxgen_header_namer.name_header();
191 let cpp = CppCodeGenerator::generate_cpp_code(
192 inclusions,
193 &analyzed_apis,
194 self.config,
195 cpp_codegen_options,
196 &cxxgen_header_name,
197 )?;
198 let rs = RsCodeGenerator::generate_rs_code(
199 analyzed_apis,
200 self.include_list,
201 bindgen_mod,
202 self.config,
203 cpp.as_ref().map(|file_pair| file_pair.header_name.clone()),
204 );
205 Ok(CodegenResults {
206 rs,
207 cpp,
208 cxxgen_header_name,
209 })
210 }
211 }
212 }
213}