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,
+                })
+            }
+        }
+    }
+}