Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 1 | // 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 | |
| 9 | mod analysis; |
| 10 | mod api; |
| 11 | mod apivec; |
| 12 | mod codegen_cpp; |
| 13 | mod codegen_rs; |
| 14 | #[cfg(test)] |
| 15 | mod conversion_tests; |
| 16 | mod convert_error; |
| 17 | mod doc_attr; |
| 18 | mod error_reporter; |
| 19 | mod parse; |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 20 | mod type_helpers; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 21 | mod utilities; |
| 22 | |
| 23 | use analysis::fun::FnAnalyzer; |
| 24 | use autocxx_parser::IncludeCppConfig; |
| 25 | pub(crate) use codegen_cpp::CppCodeGenerator; |
| 26 | pub(crate) use convert_error::ConvertError; |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 27 | use convert_error::ConvertErrorFromCpp; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 28 | use itertools::Itertools; |
| 29 | use syn::{Item, ItemMod}; |
| 30 | |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 31 | use crate::{CodegenOptions, CppFilePair, UnsafePolicy}; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 32 | |
| 33 | use 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 40 | 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 | |
| 52 | const 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. |
| 66 | pub(crate) struct BridgeConverter<'a> { |
| 67 | include_list: &'a [String], |
| 68 | config: &'a IncludeCppConfig, |
| 69 | } |
| 70 | |
| 71 | /// C++ and Rust code generation output. |
| 72 | pub(crate) struct CodegenResults { |
| 73 | pub(crate) rs: Vec<Item>, |
| 74 | pub(crate) cpp: Option<CppFilePair>, |
| 75 | pub(crate) cxxgen_header_name: String, |
| 76 | } |
| 77 | |
| 78 | impl<'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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 89 | "##### APIs after {}:\n{}", |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 90 | label, |
| 91 | apis.iter() |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 92 | .map(|api| { format!(" {api:?}") }) |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 93 | .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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 110 | codegen_options: &CodegenOptions, |
| 111 | source_file_contents: &str, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 112 | ) -> 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 119 | let apis = parser.parse_items(items_to_process, source_file_contents)?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 120 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 133 | // the analysis results. |
| 134 | let analyzed_apis = |
| 135 | analyze_pod_apis(apis, self.config).map_err(ConvertError::Cpp)?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 136 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 146 | let analyzed_apis = FnAnalyzer::analyze_functions( |
| 147 | analyzed_apis, |
| 148 | &unsafe_policy, |
| 149 | self.config, |
| 150 | codegen_options.force_wrapper_gen, |
| 151 | ); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 152 | // 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 161 | Self::dump_apis("adding constructor deps", &analyzed_apis); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 162 | let analyzed_apis = discard_ignored_functions(analyzed_apis); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 163 | Self::dump_apis("ignoring ignorable fns", &analyzed_apis); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 164 | // 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 171 | Self::dump_apis("removing ignored dependents", &analyzed_apis); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 172 | |
| 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 178 | Self::dump_apis("GC", &analyzed_apis); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 179 | // And finally pass them to the code gen phases, which outputs |
| 180 | // code suitable for cxx to consume. |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 181 | let cxxgen_header_name = codegen_options |
| 182 | .cpp_codegen_options |
| 183 | .cxxgen_header_namer |
| 184 | .name_header(); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 185 | let cpp = CppCodeGenerator::generate_cpp_code( |
| 186 | inclusions, |
| 187 | &analyzed_apis, |
| 188 | self.config, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 189 | &codegen_options.cpp_codegen_options, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 190 | &cxxgen_header_name, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 191 | ) |
| 192 | .map_err(ConvertError::Cpp)?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 193 | let rs = RsCodeGenerator::generate_rs_code( |
| 194 | analyzed_apis, |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 195 | &unsafe_policy, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 196 | 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 | } |