blob: 61d7d6dc5e97afe0be13ecbd96117819b333006d [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;
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070020mod type_helpers;
Brian Silverman4e662aa2022-05-11 23:10:19 -070021mod utilities;
22
23use analysis::fun::FnAnalyzer;
24use autocxx_parser::IncludeCppConfig;
25pub(crate) use codegen_cpp::CppCodeGenerator;
26pub(crate) use convert_error::ConvertError;
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070027use convert_error::ConvertErrorFromCpp;
Brian Silverman4e662aa2022-05-11 23:10:19 -070028use itertools::Itertools;
29use syn::{Item, ItemMod};
30
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070031use crate::{CodegenOptions, CppFilePair, UnsafePolicy};
Brian Silverman4e662aa2022-05-11 23:10:19 -070032
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,
Brian Silverman4e662aa2022-05-11 23:10:19 -070040 gc::filter_apis_by_following_edges_from_allowlist,
41 pod::analyze_pod_apis,
42 remove_ignored::filter_apis_by_ignored_dependents,
43 replace_hopeless_typedef_targets,
44 tdef::convert_typedef_targets,
45 },
46 api::AnalysisPhase,
47 apivec::ApiVec,
48 codegen_rs::RsCodeGenerator,
49 parse::ParseBindgen,
50};
51
52const LOG_APIS: bool = true;
53
54/// Converts the bindings generated by bindgen into a form suitable
55/// for use with `cxx`.
56/// In fact, most of the actual operation happens within an
57/// individual `BridgeConversion`.
58///
59/// # Flexibility in handling bindgen output
60///
61/// autocxx is inevitably tied to the details of the bindgen output;
62/// e.g. the creation of a 'root' mod when namespaces are enabled.
63/// At the moment this crate takes the view that it's OK to panic
64/// if the bindgen output is not as expected. It may be in future that
65/// we need to be a bit more graceful, but for now, that's OK.
66pub(crate) struct BridgeConverter<'a> {
67 include_list: &'a [String],
68 config: &'a IncludeCppConfig,
69}
70
71/// C++ and Rust code generation output.
72pub(crate) struct CodegenResults {
73 pub(crate) rs: Vec<Item>,
74 pub(crate) cpp: Option<CppFilePair>,
75 pub(crate) cxxgen_header_name: String,
76}
77
78impl<'a> BridgeConverter<'a> {
79 pub fn new(include_list: &'a [String], config: &'a IncludeCppConfig) -> Self {
80 Self {
81 include_list,
82 config,
83 }
84 }
85
86 fn dump_apis<T: AnalysisPhase>(label: &str, apis: &ApiVec<T>) {
87 if LOG_APIS {
88 log::info!(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070089 "##### APIs after {}:\n{}",
Brian Silverman4e662aa2022-05-11 23:10:19 -070090 label,
91 apis.iter()
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070092 .map(|api| { format!(" {api:?}") })
Brian Silverman4e662aa2022-05-11 23:10:19 -070093 .sorted()
94 .join("\n")
95 )
96 }
97 }
98
99 /// Convert a TokenStream of bindgen-generated bindings to a form
100 /// suitable for cxx.
101 ///
102 /// This is really the heart of autocxx. It parses the output of `bindgen`
103 /// (although really by "parse" we mean to interpret the structures already built
104 /// up by the `syn` crate).
105 pub(crate) fn convert(
106 &self,
107 mut bindgen_mod: ItemMod,
108 unsafe_policy: UnsafePolicy,
109 inclusions: String,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700110 codegen_options: &CodegenOptions,
111 source_file_contents: &str,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700112 ) -> Result<CodegenResults, ConvertError> {
113 match &mut bindgen_mod.content {
114 None => Err(ConvertError::NoContent),
115 Some((_, items)) => {
116 // Parse the bindgen mod.
117 let items_to_process = items.drain(..).collect();
118 let parser = ParseBindgen::new(self.config);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700119 let apis = parser.parse_items(items_to_process, source_file_contents)?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700120 Self::dump_apis("parsing", &apis);
121 // Inside parse_results, we now have a list of APIs.
122 // We now enter various analysis phases.
123 // Next, convert any typedefs.
124 // "Convert" means replacing bindgen-style type targets
125 // (e.g. root::std::unique_ptr) with cxx-style targets (e.g. UniquePtr).
126 let apis = convert_typedef_targets(self.config, apis);
127 Self::dump_apis("typedefs", &apis);
128 // Now analyze which of them can be POD (i.e. trivial, movable, pass-by-value
129 // versus which need to be opaque).
130 // Specifically, let's confirm that the items requested by the user to be
131 // POD really are POD, and duly mark any dependent types.
132 // This returns a new list of `Api`s, which will be parameterized with
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700133 // the analysis results.
134 let analyzed_apis =
135 analyze_pod_apis(apis, self.config).map_err(ConvertError::Cpp)?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700136 Self::dump_apis("pod analysis", &analyzed_apis);
137 let analyzed_apis = replace_hopeless_typedef_targets(self.config, analyzed_apis);
138 let analyzed_apis = add_casts(analyzed_apis);
139 let analyzed_apis = create_alloc_and_frees(analyzed_apis);
140 // Next, figure out how we materialize different functions.
141 // Some will be simple entries in the cxx::bridge module; others will
142 // require C++ wrapper functions. This is probably the most complex
143 // part of `autocxx`. Again, this returns a new set of `Api`s, but
144 // parameterized by a richer set of metadata.
145 Self::dump_apis("adding casts", &analyzed_apis);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700146 let analyzed_apis = FnAnalyzer::analyze_functions(
147 analyzed_apis,
148 &unsafe_policy,
149 self.config,
150 codegen_options.force_wrapper_gen,
151 );
Brian Silverman4e662aa2022-05-11 23:10:19 -0700152 // If any of those functions turned out to be pure virtual, don't attempt
153 // to generate UniquePtr implementations for the type, since it can't
154 // be instantiated.
155 Self::dump_apis("analyze fns", &analyzed_apis);
156 let analyzed_apis = mark_types_abstract(analyzed_apis);
157 Self::dump_apis("marking abstract", &analyzed_apis);
158 // Annotate structs with a note of any copy/move constructors which
159 // we may want to retain to avoid garbage collecting them later.
160 let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700161 Self::dump_apis("adding constructor deps", &analyzed_apis);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700162 let analyzed_apis = discard_ignored_functions(analyzed_apis);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700163 Self::dump_apis("ignoring ignorable fns", &analyzed_apis);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700164 // Remove any APIs whose names are not compatible with cxx.
165 let analyzed_apis = check_names(analyzed_apis);
166 // During parsing or subsequent processing we might have encountered
167 // items which we couldn't process due to as-yet-unsupported features.
168 // There might be other items depending on such things. Let's remove them
169 // too.
170 let analyzed_apis = filter_apis_by_ignored_dependents(analyzed_apis);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700171 Self::dump_apis("removing ignored dependents", &analyzed_apis);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700172
173 // We now garbage collect the ones we don't need...
174 let mut analyzed_apis =
175 filter_apis_by_following_edges_from_allowlist(analyzed_apis, self.config);
176 // Determine what variably-sized C types (e.g. int) we need to include
177 analysis::ctypes::append_ctype_information(&mut analyzed_apis);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700178 Self::dump_apis("GC", &analyzed_apis);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700179 // And finally pass them to the code gen phases, which outputs
180 // code suitable for cxx to consume.
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700181 let cxxgen_header_name = codegen_options
182 .cpp_codegen_options
183 .cxxgen_header_namer
184 .name_header();
Brian Silverman4e662aa2022-05-11 23:10:19 -0700185 let cpp = CppCodeGenerator::generate_cpp_code(
186 inclusions,
187 &analyzed_apis,
188 self.config,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700189 &codegen_options.cpp_codegen_options,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700190 &cxxgen_header_name,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700191 )
192 .map_err(ConvertError::Cpp)?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700193 let rs = RsCodeGenerator::generate_rs_code(
194 analyzed_apis,
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700195 &unsafe_policy,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700196 self.include_list,
197 bindgen_mod,
198 self.config,
199 cpp.as_ref().map(|file_pair| file_pair.header_name.clone()),
200 );
201 Ok(CodegenResults {
202 rs,
203 cpp,
204 cxxgen_header_name,
205 })
206 }
207 }
208 }
209}