Squashed 'third_party/autocxx/' content from commit 629e8fa53
git-subtree-dir: third_party/autocxx
git-subtree-split: 629e8fa531a633164c0b52e2a3cab536d4cd0849
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I62a03b0049f49adf029e0204639cdb5468dde1a1
diff --git a/engine/src/conversion/mod.rs b/engine/src/conversion/mod.rs
new file mode 100644
index 0000000..3043dcf
--- /dev/null
+++ b/engine/src/conversion/mod.rs
@@ -0,0 +1,213 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod analysis;
+mod api;
+mod apivec;
+mod codegen_cpp;
+mod codegen_rs;
+#[cfg(test)]
+mod conversion_tests;
+mod convert_error;
+mod doc_attr;
+mod error_reporter;
+mod parse;
+mod utilities;
+
+use analysis::fun::FnAnalyzer;
+use autocxx_parser::IncludeCppConfig;
+pub(crate) use codegen_cpp::CppCodeGenerator;
+pub(crate) use convert_error::ConvertError;
+use itertools::Itertools;
+use syn::{Item, ItemMod};
+
+use crate::{
+ conversion::analysis::deps::HasDependencies, CppCodegenOptions, CppFilePair, UnsafePolicy,
+};
+
+use self::{
+ analysis::{
+ abstract_types::{discard_ignored_functions, mark_types_abstract},
+ allocators::create_alloc_and_frees,
+ casts::add_casts,
+ check_names,
+ constructor_deps::decorate_types_with_constructor_deps,
+ fun::FnPhase,
+ gc::filter_apis_by_following_edges_from_allowlist,
+ pod::analyze_pod_apis,
+ remove_ignored::filter_apis_by_ignored_dependents,
+ replace_hopeless_typedef_targets,
+ tdef::convert_typedef_targets,
+ },
+ api::AnalysisPhase,
+ apivec::ApiVec,
+ codegen_rs::RsCodeGenerator,
+ parse::ParseBindgen,
+};
+
+const LOG_APIS: bool = true;
+
+/// Converts the bindings generated by bindgen into a form suitable
+/// for use with `cxx`.
+/// In fact, most of the actual operation happens within an
+/// individual `BridgeConversion`.
+///
+/// # Flexibility in handling bindgen output
+///
+/// autocxx is inevitably tied to the details of the bindgen output;
+/// e.g. the creation of a 'root' mod when namespaces are enabled.
+/// At the moment this crate takes the view that it's OK to panic
+/// if the bindgen output is not as expected. It may be in future that
+/// we need to be a bit more graceful, but for now, that's OK.
+pub(crate) struct BridgeConverter<'a> {
+ include_list: &'a [String],
+ config: &'a IncludeCppConfig,
+}
+
+/// C++ and Rust code generation output.
+pub(crate) struct CodegenResults {
+ pub(crate) rs: Vec<Item>,
+ pub(crate) cpp: Option<CppFilePair>,
+ pub(crate) cxxgen_header_name: String,
+}
+
+impl<'a> BridgeConverter<'a> {
+ pub fn new(include_list: &'a [String], config: &'a IncludeCppConfig) -> Self {
+ Self {
+ include_list,
+ config,
+ }
+ }
+
+ fn dump_apis<T: AnalysisPhase>(label: &str, apis: &ApiVec<T>) {
+ if LOG_APIS {
+ log::info!(
+ "APIs after {}:\n{}",
+ label,
+ apis.iter()
+ .map(|api| { format!(" {:?}", api) })
+ .sorted()
+ .join("\n")
+ )
+ }
+ }
+
+ fn dump_apis_with_deps(label: &str, apis: &ApiVec<FnPhase>) {
+ if LOG_APIS {
+ log::info!(
+ "APIs after {}:\n{}",
+ label,
+ apis.iter()
+ .map(|api| { format!(" {:?}, deps={}", api, api.format_deps()) })
+ .sorted()
+ .join("\n")
+ )
+ }
+ }
+
+ /// Convert a TokenStream of bindgen-generated bindings to a form
+ /// suitable for cxx.
+ ///
+ /// This is really the heart of autocxx. It parses the output of `bindgen`
+ /// (although really by "parse" we mean to interpret the structures already built
+ /// up by the `syn` crate).
+ pub(crate) fn convert(
+ &self,
+ mut bindgen_mod: ItemMod,
+ unsafe_policy: UnsafePolicy,
+ inclusions: String,
+ cpp_codegen_options: &CppCodegenOptions,
+ ) -> Result<CodegenResults, ConvertError> {
+ match &mut bindgen_mod.content {
+ None => Err(ConvertError::NoContent),
+ Some((_, items)) => {
+ // Parse the bindgen mod.
+ let items_to_process = items.drain(..).collect();
+ let parser = ParseBindgen::new(self.config);
+ let apis = parser.parse_items(items_to_process)?;
+ Self::dump_apis("parsing", &apis);
+ // Inside parse_results, we now have a list of APIs.
+ // We now enter various analysis phases.
+ // Next, convert any typedefs.
+ // "Convert" means replacing bindgen-style type targets
+ // (e.g. root::std::unique_ptr) with cxx-style targets (e.g. UniquePtr).
+ let apis = convert_typedef_targets(self.config, apis);
+ Self::dump_apis("typedefs", &apis);
+ // Now analyze which of them can be POD (i.e. trivial, movable, pass-by-value
+ // versus which need to be opaque).
+ // Specifically, let's confirm that the items requested by the user to be
+ // POD really are POD, and duly mark any dependent types.
+ // This returns a new list of `Api`s, which will be parameterized with
+ // the analysis results. It also returns an object which can be used
+ // by subsequent phases to work out which objects are POD.
+ let analyzed_apis = analyze_pod_apis(apis, self.config)?;
+ Self::dump_apis("pod analysis", &analyzed_apis);
+ let analyzed_apis = replace_hopeless_typedef_targets(self.config, analyzed_apis);
+ let analyzed_apis = add_casts(analyzed_apis);
+ let analyzed_apis = create_alloc_and_frees(analyzed_apis);
+ // Next, figure out how we materialize different functions.
+ // Some will be simple entries in the cxx::bridge module; others will
+ // require C++ wrapper functions. This is probably the most complex
+ // part of `autocxx`. Again, this returns a new set of `Api`s, but
+ // parameterized by a richer set of metadata.
+ Self::dump_apis("adding casts", &analyzed_apis);
+ let analyzed_apis =
+ FnAnalyzer::analyze_functions(analyzed_apis, unsafe_policy, self.config);
+ // If any of those functions turned out to be pure virtual, don't attempt
+ // to generate UniquePtr implementations for the type, since it can't
+ // be instantiated.
+ Self::dump_apis("analyze fns", &analyzed_apis);
+ let analyzed_apis = mark_types_abstract(analyzed_apis);
+ Self::dump_apis("marking abstract", &analyzed_apis);
+ // Annotate structs with a note of any copy/move constructors which
+ // we may want to retain to avoid garbage collecting them later.
+ let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis);
+ Self::dump_apis_with_deps("adding constructor deps", &analyzed_apis);
+ let analyzed_apis = discard_ignored_functions(analyzed_apis);
+ Self::dump_apis_with_deps("ignoring ignorable fns", &analyzed_apis);
+ // Remove any APIs whose names are not compatible with cxx.
+ let analyzed_apis = check_names(analyzed_apis);
+ // During parsing or subsequent processing we might have encountered
+ // items which we couldn't process due to as-yet-unsupported features.
+ // There might be other items depending on such things. Let's remove them
+ // too.
+ let analyzed_apis = filter_apis_by_ignored_dependents(analyzed_apis);
+ Self::dump_apis_with_deps("removing ignored dependents", &analyzed_apis);
+
+ // We now garbage collect the ones we don't need...
+ let mut analyzed_apis =
+ filter_apis_by_following_edges_from_allowlist(analyzed_apis, self.config);
+ // Determine what variably-sized C types (e.g. int) we need to include
+ analysis::ctypes::append_ctype_information(&mut analyzed_apis);
+ Self::dump_apis_with_deps("GC", &analyzed_apis);
+ // And finally pass them to the code gen phases, which outputs
+ // code suitable for cxx to consume.
+ let cxxgen_header_name = cpp_codegen_options.cxxgen_header_namer.name_header();
+ let cpp = CppCodeGenerator::generate_cpp_code(
+ inclusions,
+ &analyzed_apis,
+ self.config,
+ cpp_codegen_options,
+ &cxxgen_header_name,
+ )?;
+ let rs = RsCodeGenerator::generate_rs_code(
+ analyzed_apis,
+ self.include_list,
+ bindgen_mod,
+ self.config,
+ cpp.as_ref().map(|file_pair| file_pair.header_name.clone()),
+ );
+ Ok(CodegenResults {
+ rs,
+ cpp,
+ cxxgen_header_name,
+ })
+ }
+ }
+ }
+}