mod fun_codegen;
mod function_wrapper_rs;
mod impl_item_creator;
mod lifetime;
mod namespace_organizer;
mod non_pod_struct;
pub(crate) mod unqualify;
use std::collections::{HashMap, HashSet};
use autocxx_parser::{IncludeCppConfig, RustFun};
use itertools::Itertools;
use proc_macro2::{Span, TokenStream};
use syn::{
parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg, ForeignItem,
ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, TraitItem,
};
use crate::{
conversion::{
codegen_rs::{
non_pod_struct::{make_non_pod, new_non_pod_struct},
unqualify::{unqualify_params, unqualify_ret_type},
},
doc_attr::get_doc_attrs,
},
types::{make_ident, Namespace, QualifiedName},
};
use impl_item_creator::create_impl_items;
use self::{
fun_codegen::gen_function,
namespace_organizer::{HasNs, NamespaceEntries},
};
use super::{
analysis::{
fun::{FnPhase, PodAndDepAnalysis, ReceiverMutability},
pod::PodAnalysis,
},
api::{AnalysisPhase, Api, SubclassName, TypeKind, TypedefKind},
convert_error::ErrorContextType,
};
use super::{
api::{Layout, Provenance, RustSubclassFnDetails, SuperclassMethod, TraitImplSignature},
apivec::ApiVec,
codegen_cpp::type_to_cpp::{
namespaced_name_using_original_name_map, original_name_map_from_apis, CppNameMap,
},
};
use super::{convert_error::ErrorContext, ConvertError};
use quote::quote;
struct ImplBlockDetails {
item: ImplItem,
ty: Ident,
}
struct TraitImplBlockDetails {
item: TraitItem,
key: TraitImplSignature,
}
#[derive(Clone)]
enum Use {
UsedFromCxxBridge,
UsedFromCxxBridgeWithAlias(Ident),
UsedFromBindgen,
SpecificNameFromBindgen(Ident),
Custom(Box<Item>),
}
fn get_string_items() -> Vec<Item> {
[
Item::Trait(parse_quote! {
pub trait ToCppString {
fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString>;
}
}),
Item::Impl(parse_quote! {
impl ToCppString for &str {
fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
make_string(self)
}
}
}),
Item::Impl(parse_quote! {
impl ToCppString for String {
fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
make_string(&self)
}
}
}),
Item::Impl(parse_quote! {
impl ToCppString for &String {
fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
make_string(self)
}
}
}),
Item::Impl(parse_quote! {
impl ToCppString for cxx::UniquePtr<cxx::CxxString> {
fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
self
}
}
}),
]
.to_vec()
}
pub(crate) struct RsCodeGenerator<'a> {
include_list: &'a [String],
bindgen_mod: ItemMod,
original_name_map: CppNameMap,
config: &'a IncludeCppConfig,
header_name: Option<String>,
}
impl<'a> RsCodeGenerator<'a> {
pub(crate) fn generate_rs_code(
all_apis: ApiVec<FnPhase>,
include_list: &'a [String],
bindgen_mod: ItemMod,
config: &'a IncludeCppConfig,
header_name: Option<String>,
) -> Vec<Item> {
let c = Self {
include_list,
bindgen_mod,
original_name_map: original_name_map_from_apis(&all_apis),
config,
header_name,
};
c.rs_codegen(all_apis)
}
fn rs_codegen(mut self, all_apis: ApiVec<FnPhase>) -> Vec<Item> {
let methods_by_superclass = self.accumulate_superclass_methods(&all_apis);
let subclasses_with_a_single_trivial_constructor =
find_trivially_constructed_subclasses(&all_apis);
let non_pod_types = find_non_pod_types(&all_apis);
let (rs_codegen_results_and_namespaces, additional_cpp_needs): (Vec<_>, Vec<_>) = all_apis
.into_iter()
.map(|api| {
let more_cpp_needed = api.needs_cpp_codegen();
let name = api.name().clone();
let gen = self.generate_rs_for_api(
api,
&methods_by_superclass,
&subclasses_with_a_single_trivial_constructor,
&non_pod_types,
);
((name, gen), more_cpp_needed)
})
.unzip();
let mut use_statements =
Self::generate_final_use_statements(&rs_codegen_results_and_namespaces);
let bindgen_root_items =
self.generate_final_bindgen_mods(&rs_codegen_results_and_namespaces);
let (_, rs_codegen_results): (Vec<_>, Vec<_>) =
rs_codegen_results_and_namespaces.into_iter().unzip();
let (extern_c_mod_items, extern_rust_mod_items, all_items, bridge_items): (
Vec<_>,
Vec<_>,
Vec<_>,
Vec<_>,
) = rs_codegen_results
.into_iter()
.map(|api| {
(
api.extern_c_mod_items,
api.extern_rust_mod_items,
api.global_items,
api.bridge_items,
)
})
.multiunzip();
let mut bridge_items: Vec<Item> = bridge_items.into_iter().flatten().collect();
let mut extern_c_mod_items: Vec<ForeignItem> =
extern_c_mod_items.into_iter().flatten().collect();
let mut extern_rust_mod_items = extern_rust_mod_items.into_iter().flatten().collect();
let mut all_items: Vec<Item> = all_items.into_iter().flatten().collect();
let has_additional_cpp_needs = additional_cpp_needs.into_iter().any(std::convert::identity);
extern_c_mod_items.extend(self.build_include_foreign_items(has_additional_cpp_needs));
let mut extern_c_mod: ItemForeignMod = parse_quote!(
extern "C++" {}
);
extern_c_mod.items.append(&mut extern_c_mod_items);
bridge_items.push(Self::make_foreign_mod_unsafe(extern_c_mod));
let mut extern_rust_mod: ItemForeignMod = parse_quote!(
extern "Rust" {}
);
extern_rust_mod.items.append(&mut extern_rust_mod_items);
bridge_items.push(Item::ForeignMod(extern_rust_mod));
if !bindgen_root_items.is_empty() {
self.bindgen_mod.vis = parse_quote! {};
self.bindgen_mod.content.as_mut().unwrap().1 = vec![Item::Mod(parse_quote! {
pub(super) mod root {
#(#bindgen_root_items)*
}
})];
all_items.push(Item::Mod(self.bindgen_mod));
}
all_items.push(Item::Mod(parse_quote! {
#[cxx::bridge]
mod cxxbridge {
#(#bridge_items)*
}
}));
all_items.push(Item::Use(parse_quote! {
#[allow(unused_imports)]
use bindgen::root;
}));
all_items.append(&mut use_statements);
all_items
}
fn accumulate_superclass_methods(
&self,
apis: &ApiVec<FnPhase>,
) -> HashMap<QualifiedName, Vec<SuperclassMethod>> {
let mut results = HashMap::new();
results.extend(
self.config
.superclasses()
.map(|sc| (QualifiedName::new_from_cpp_name(sc), Vec::new())),
);
for api in apis.iter() {
if let Api::SubclassTraitItem { details, .. } = api {
let list = results.get_mut(&details.receiver);
if let Some(list) = list {
list.push(details.clone());
}
}
}
results
}
fn make_foreign_mod_unsafe(ifm: ItemForeignMod) -> Item {
Item::Verbatim(quote! {
unsafe #ifm
})
}
fn build_include_foreign_items(&self, has_additional_cpp_needs: bool) -> Vec<ForeignItem> {
let extra_inclusion = if has_additional_cpp_needs {
Some(self.header_name.clone().unwrap())
} else {
None
};
let chained = self.include_list.iter().chain(extra_inclusion.iter());
chained
.map(|inc| {
ForeignItem::Macro(parse_quote! {
include!(#inc);
})
})
.collect()
}
fn generate_final_use_statements(
input_items: &[(QualifiedName, RsCodegenResult)],
) -> Vec<Item> {
let mut output_items = Vec::new();
let ns_entries = NamespaceEntries::new(input_items);
Self::append_child_use_namespace(&ns_entries, &mut output_items);
output_items
}
fn append_child_use_namespace(
ns_entries: &NamespaceEntries<(QualifiedName, RsCodegenResult)>,
output_items: &mut Vec<Item>,
) {
for (name, codegen) in ns_entries.entries() {
output_items.extend(codegen.materializations.iter().map(|materialization| {
match materialization {
Use::UsedFromCxxBridgeWithAlias(alias) => {
Self::generate_cxx_use_stmt(name, Some(alias))
}
Use::UsedFromCxxBridge => Self::generate_cxx_use_stmt(name, None),
Use::UsedFromBindgen => Self::generate_bindgen_use_stmt(name),
Use::SpecificNameFromBindgen(id) => {
let name = QualifiedName::new(name.get_namespace(), id.clone());
Self::generate_bindgen_use_stmt(&name)
}
Use::Custom(item) => *item.clone(),
}
}));
}
for (child_name, child_ns_entries) in ns_entries.children() {
if child_ns_entries.is_empty() {
continue;
}
let child_id = make_ident(child_name);
let mut new_mod: ItemMod = parse_quote!(
pub mod #child_id {
}
);
Self::append_child_use_namespace(
child_ns_entries,
&mut new_mod.content.as_mut().unwrap().1,
);
output_items.push(Item::Mod(new_mod));
}
}
fn append_uses_for_ns(&mut self, items: &mut Vec<Item>, ns: &Namespace) {
let super_duper = std::iter::repeat(make_ident("super")); let supers = super_duper.clone().take(ns.depth() + 2);
items.push(Item::Use(parse_quote! {
#[allow(unused_imports)]
use self::
#(#supers)::*
::cxxbridge;
}));
if !self.config.exclude_utilities() {
let supers = super_duper.clone().take(ns.depth() + 2);
items.push(Item::Use(parse_quote! {
#[allow(unused_imports)]
use self::
#(#supers)::*
::ToCppString;
}));
}
let supers = super_duper.take(ns.depth() + 1);
items.push(Item::Use(parse_quote! {
#[allow(unused_imports)]
use self::
#(#supers)::*
::root;
}));
}
fn append_child_bindgen_namespace(
&mut self,
ns_entries: &NamespaceEntries<(QualifiedName, RsCodegenResult)>,
output_items: &mut Vec<Item>,
ns: &Namespace,
) {
let mut impl_entries_by_type: HashMap<_, Vec<_>> = HashMap::new();
let mut trait_impl_entries_by_trait_and_ty: HashMap<_, Vec<_>> = HashMap::new();
for item in ns_entries.entries() {
output_items.extend(item.1.bindgen_mod_items.iter().cloned());
if let Some(impl_entry) = &item.1.impl_entry {
impl_entries_by_type
.entry(impl_entry.ty.clone())
.or_default()
.push(&impl_entry.item);
}
if let Some(trait_impl_entry) = &item.1.trait_impl_entry {
trait_impl_entries_by_trait_and_ty
.entry(trait_impl_entry.key.clone())
.or_default()
.push(&trait_impl_entry.item);
}
}
for (ty, entries) in impl_entries_by_type.into_iter() {
output_items.push(Item::Impl(parse_quote! {
impl #ty {
#(#entries)*
}
}))
}
for (key, entries) in trait_impl_entries_by_trait_and_ty.into_iter() {
let unsafety = key.unsafety;
let ty = key.ty;
let trt = key.trait_signature;
output_items.push(Item::Impl(parse_quote! {
#unsafety impl #trt for #ty {
#(#entries)*
}
}))
}
for (child_name, child_ns_entries) in ns_entries.children() {
let new_ns = ns.push((*child_name).clone());
let child_id = make_ident(child_name);
let mut inner_output_items = Vec::new();
self.append_child_bindgen_namespace(child_ns_entries, &mut inner_output_items, &new_ns);
if !inner_output_items.is_empty() {
let mut new_mod: ItemMod = parse_quote!(
pub mod #child_id {
}
);
self.append_uses_for_ns(&mut inner_output_items, &new_ns);
new_mod.content.as_mut().unwrap().1 = inner_output_items;
output_items.push(Item::Mod(new_mod));
}
}
}
fn id_to_expr(id: &Ident) -> Expr {
parse_quote! { #id }
}
fn generate_final_bindgen_mods(
&mut self,
input_items: &[(QualifiedName, RsCodegenResult)],
) -> Vec<Item> {
let mut output_items = Vec::new();
let ns = Namespace::new();
let ns_entries = NamespaceEntries::new(input_items);
self.append_child_bindgen_namespace(&ns_entries, &mut output_items, &ns);
self.append_uses_for_ns(&mut output_items, &ns);
output_items
}
fn generate_rs_for_api(
&self,
api: Api<FnPhase>,
associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>,
subclasses_with_a_single_trivial_constructor: &HashSet<QualifiedName>,
non_pod_types: &HashSet<QualifiedName>,
) -> RsCodegenResult {
let name = api.name().clone();
let id = name.get_final_ident();
let cpp_call_name = api.effective_cpp_name().to_string();
match api {
Api::StringConstructor { .. } => {
let make_string_name = make_ident(self.config.get_makestring_name());
RsCodegenResult {
extern_c_mod_items: vec![ForeignItem::Fn(parse_quote!(
fn #make_string_name(str_: &str) -> UniquePtr<CxxString>;
))],
global_items: get_string_items(),
materializations: vec![Use::UsedFromCxxBridgeWithAlias(make_ident(
"make_string",
))],
..Default::default()
}
}
Api::Function { fun, analysis, .. } => gen_function(
name.get_namespace(),
*fun,
analysis,
cpp_call_name,
non_pod_types,
),
Api::Const { const_item, .. } => RsCodegenResult {
bindgen_mod_items: vec![Item::Const(const_item)],
materializations: vec![Use::UsedFromBindgen],
..Default::default()
},
Api::Typedef { analysis, .. } => RsCodegenResult {
bindgen_mod_items: vec![match analysis.kind {
TypedefKind::Type(type_item) => Item::Type(type_item),
TypedefKind::Use(use_item) => Item::Use(use_item),
}],
materializations: vec![Use::UsedFromBindgen],
..Default::default()
},
Api::Struct {
details, analysis, ..
} => {
let doc_attrs = get_doc_attrs(&details.item.attrs);
let layout = details.layout.clone();
self.generate_type(
&name,
id,
analysis.pod.kind,
analysis.constructors.move_constructor,
analysis.constructors.destructor,
|| Some((Item::Struct(details.item), doc_attrs)),
associated_methods,
layout,
)
}
Api::Enum { item, .. } => {
let doc_attrs = get_doc_attrs(&item.attrs);
self.generate_type(
&name,
id,
TypeKind::Pod,
true,
true,
|| Some((Item::Enum(item), doc_attrs)),
associated_methods,
None,
)
}
Api::ForwardDeclaration { .. } | Api::ConcreteType { .. } => self.generate_type(
&name,
id,
TypeKind::Abstract,
false, true, || None,
associated_methods,
None,
),
Api::CType { .. } => RsCodegenResult {
extern_c_mod_items: vec![ForeignItem::Verbatim(quote! {
type #id = autocxx::#id;
})],
..Default::default()
},
Api::RustType { path, .. } => RsCodegenResult {
global_items: vec![parse_quote! {
use super::#path;
}],
extern_rust_mod_items: vec![parse_quote! {
type #id;
}],
..Default::default()
},
Api::RustFn {
details:
RustFun {
path,
sig,
receiver: None,
..
},
..
} => RsCodegenResult {
global_items: vec![parse_quote! {
use super::#path;
}],
extern_rust_mod_items: vec![parse_quote! {
#sig;
}],
..Default::default()
},
Api::RustFn {
details:
RustFun {
sig,
receiver: Some(_),
..
},
..
} => RsCodegenResult {
extern_rust_mod_items: vec![parse_quote! {
#sig;
}],
..Default::default()
},
Api::RustSubclassFn {
details, subclass, ..
} => Self::generate_subclass_fn(id, *details, subclass),
Api::Subclass {
name, superclass, ..
} => {
let methods = associated_methods.get(&superclass);
let generate_peer_constructor =
subclasses_with_a_single_trivial_constructor.contains(&name.0.name);
self.generate_subclass(name, &superclass, methods, generate_peer_constructor)
}
Api::IgnoredItem {
err,
ctx: Some(ctx),
..
} => Self::generate_error_entry(err, ctx),
Api::IgnoredItem { .. } | Api::SubclassTraitItem { .. } => RsCodegenResult::default(),
}
}
fn generate_subclass(
&self,
sub: SubclassName,
superclass: &QualifiedName,
methods: Option<&Vec<SuperclassMethod>>,
generate_peer_constructor: bool,
) -> RsCodegenResult {
let super_name = superclass.get_final_item();
let super_path = superclass.to_type_path();
let super_cxxxbridge_id = superclass.get_final_ident();
let id = sub.id();
let holder = sub.holder();
let full_cpp = sub.cpp();
let cpp_path = full_cpp.to_type_path();
let cpp_id = full_cpp.get_final_ident();
let mut global_items = Vec::new();
global_items.push(parse_quote! {
pub use bindgen::root::#holder;
});
let relinquish_ownership_call = sub.cpp_remove_ownership();
let mut bindgen_mod_items = vec![
parse_quote! {
pub use cxxbridge::#cpp_id;
},
parse_quote! {
pub struct #holder(pub autocxx::subclass::CppSubclassRustPeerHolder<super::super::super::#id>);
},
parse_quote! {
impl autocxx::subclass::CppSubclassCppPeer for #cpp_id {
fn relinquish_ownership(&self) {
self.#relinquish_ownership_call();
}
}
},
];
let mut extern_c_mod_items = vec![
self.generate_cxxbridge_type(&full_cpp, false, Vec::new()),
parse_quote! {
fn #relinquish_ownership_call(self: &#cpp_id);
},
];
if let Some(methods) = methods {
let supers = SubclassName::get_supers_trait_name(superclass).to_type_path();
let methods_impls: Vec<ImplItem> = methods
.iter()
.filter(|m| !m.is_pure_virtual)
.map(|m| {
let cpp_super_method_name =
SubclassName::get_super_fn_name(&Namespace::new(), &m.name.to_string())
.get_final_ident();
let mut params = m.params.clone();
let ret = &m.ret_type.clone();
let (peer_fn, first_param) = match m.receiver_mutability {
ReceiverMutability::Const => ("peer", parse_quote!(&self)),
ReceiverMutability::Mutable => ("peer_mut", parse_quote!(&mut self)),
};
let peer_fn = make_ident(peer_fn);
*(params.iter_mut().next().unwrap()) = first_param;
let param_names = m.param_names.iter().skip(1);
let unsafe_token = m.requires_unsafe.wrapper_token();
parse_quote! {
#unsafe_token fn #cpp_super_method_name(#params) #ret {
use autocxx::subclass::CppSubclass;
self.#peer_fn().#cpp_super_method_name(#(#param_names),*)
}
}
})
.collect();
if !methods_impls.is_empty() {
bindgen_mod_items.push(parse_quote! {
#[allow(non_snake_case)]
impl #supers for super::super::super::#id {
#(#methods_impls)*
}
});
}
}
if generate_peer_constructor {
bindgen_mod_items.push(parse_quote! {
impl autocxx::subclass::CppPeerConstructor<#cpp_id> for super::super::super::#id {
fn make_peer(&mut self, peer_holder: autocxx::subclass::CppSubclassRustPeerHolder<Self>) -> cxx::UniquePtr<#cpp_path> {
#cpp_id :: make_unique(peer_holder)
}
}
})
};
let as_id = make_ident(format!("As_{}", super_name));
extern_c_mod_items.push(parse_quote! {
fn #as_id(self: &#cpp_id) -> &#super_cxxxbridge_id;
});
let as_mut_id = make_ident(format!("As_{}_mut", super_name));
extern_c_mod_items.push(parse_quote! {
fn #as_mut_id(self: Pin<&mut #cpp_id>) -> Pin<&mut #super_cxxxbridge_id>;
});
bindgen_mod_items.push(parse_quote! {
impl AsRef<#super_path> for super::super::super::#id {
fn as_ref(&self) -> &cxxbridge::#super_cxxxbridge_id {
use autocxx::subclass::CppSubclass;
self.peer().#as_id()
}
}
});
bindgen_mod_items.push(parse_quote! {
impl super::super::super::#id {
pub fn pin_mut(&mut self) -> ::std::pin::Pin<&mut cxxbridge::#super_cxxxbridge_id> {
use autocxx::subclass::CppSubclass;
self.peer_mut().#as_mut_id()
}
}
});
let remove_ownership = sub.remove_ownership();
global_items.push(parse_quote! {
#[allow(non_snake_case)]
pub fn #remove_ownership(me: Box<#holder>) -> Box<#holder> {
Box::new(#holder(me.0.relinquish_ownership()))
}
});
RsCodegenResult {
extern_c_mod_items,
bridge_items: create_impl_items(&cpp_id, false, true, self.config),
bindgen_mod_items,
materializations: vec![Use::Custom(Box::new(parse_quote! {
pub use cxxbridge::#cpp_id;
}))],
global_items,
extern_rust_mod_items: vec![
parse_quote! {
pub type #holder;
},
parse_quote! {
fn #remove_ownership(me: Box<#holder>) -> Box<#holder>;
},
],
..Default::default()
}
}
fn generate_subclass_fn(
api_name: Ident,
details: RustSubclassFnDetails,
subclass: SubclassName,
) -> RsCodegenResult {
let params = details.params;
let ret = details.ret;
let unsafe_token = details.requires_unsafe.wrapper_token();
let global_def = quote! { #unsafe_token fn #api_name(#params) #ret };
let params = unqualify_params(params);
let ret = unqualify_ret_type(ret);
let method_name = details.method_name;
let cxxbridge_decl: ForeignItemFn =
parse_quote! { #unsafe_token fn #api_name(#params) #ret; };
let args: Punctuated<Expr, Comma> =
Self::args_from_sig(&cxxbridge_decl.sig.inputs).collect();
let superclass_id = details.superclass.get_final_ident();
let methods_trait = SubclassName::get_methods_trait_name(&details.superclass);
let methods_trait = methods_trait.to_type_path();
let (deref_ty, deref_call, borrow, mut_token) = match details.receiver_mutability {
ReceiverMutability::Const => ("Deref", "deref", "try_borrow", None),
ReceiverMutability::Mutable => (
"DerefMut",
"deref_mut",
"try_borrow_mut",
Some(syn::token::Mut(Span::call_site())),
),
};
let deref_ty = make_ident(deref_ty);
let deref_call = make_ident(deref_call);
let borrow = make_ident(borrow);
let destroy_panic_msg = format!("Rust subclass API (method {} of subclass {} of superclass {}) called after subclass destroyed", method_name, subclass.0.name, superclass_id);
let reentrancy_panic_msg = format!("Rust subclass API (method {} of subclass {} of superclass {}) called whilst subclass already borrowed - likely a re-entrant call", method_name, subclass.0.name, superclass_id);
RsCodegenResult {
global_items: vec![parse_quote! {
#global_def {
let rc = me.0
.get()
.expect(#destroy_panic_msg);
let #mut_token b = rc
.as_ref()
.#borrow()
.expect(#reentrancy_panic_msg);
let r = std::ops::#deref_ty::#deref_call(& #mut_token b);
#methods_trait :: #method_name
(r,
#args)
}
}],
extern_rust_mod_items: vec![ForeignItem::Fn(cxxbridge_decl)],
..Default::default()
}
}
fn args_from_sig(params: &Punctuated<FnArg, Comma>) -> impl Iterator<Item = Expr> + '_ {
params.iter().skip(1).filter_map(|fnarg| match fnarg {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(fnarg) => match &*fnarg.pat {
syn::Pat::Ident(id) => Some(Self::id_to_expr(&id.ident)),
_ => None,
},
})
}
#[allow(clippy::too_many_arguments)] fn generate_type<F>(
&self,
name: &QualifiedName,
id: Ident,
type_kind: TypeKind,
movable: bool,
destroyable: bool,
item_creator: F,
associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>,
layout: Option<Layout>,
) -> RsCodegenResult
where
F: FnOnce() -> Option<(Item, Vec<Attribute>)>,
{
let mut bindgen_mod_items = Vec::new();
let mut materializations = vec![Use::UsedFromBindgen];
Self::add_superclass_stuff_to_type(
name,
&mut bindgen_mod_items,
&mut materializations,
associated_methods.get(name),
);
let orig_item = item_creator();
let doc_attrs = orig_item
.as_ref()
.map(|maybe_item| maybe_item.1.clone())
.unwrap_or_default();
match type_kind {
TypeKind::Pod | TypeKind::NonPod => {
let mut item = orig_item
.expect("Instantiable types must provide instance")
.0;
if matches!(type_kind, TypeKind::NonPod) {
if let Item::Struct(ref mut s) = item {
make_non_pod(s, layout);
} else {
item = Item::Struct(new_non_pod_struct(id.clone()));
}
}
bindgen_mod_items.push(item);
RsCodegenResult {
global_items: self.generate_extern_type_impl(type_kind, name),
bridge_items: create_impl_items(&id, movable, destroyable, self.config),
extern_c_mod_items: vec![self.generate_cxxbridge_type(name, true, doc_attrs)],
bindgen_mod_items,
materializations,
..Default::default()
}
}
TypeKind::Abstract => {
bindgen_mod_items.push(Item::Use(parse_quote! { pub use cxxbridge::#id; }));
RsCodegenResult {
extern_c_mod_items: vec![self.generate_cxxbridge_type(name, false, doc_attrs)],
bindgen_mod_items,
materializations,
..Default::default()
}
}
}
}
fn add_superclass_stuff_to_type(
name: &QualifiedName,
bindgen_mod_items: &mut Vec<Item>,
materializations: &mut Vec<Use>,
methods: Option<&Vec<SuperclassMethod>>,
) {
if let Some(methods) = methods {
let (supers, mains): (Vec<_>, Vec<_>) = methods
.iter()
.map(|method| {
let id = &method.name;
let super_id =
SubclassName::get_super_fn_name(&Namespace::new(), &id.to_string())
.get_final_ident();
let param_names: Punctuated<Expr, Comma> =
Self::args_from_sig(&method.params).collect();
let mut params = method.params.clone();
*(params.iter_mut().next().unwrap()) = match method.receiver_mutability {
ReceiverMutability::Const => parse_quote!(&self),
ReceiverMutability::Mutable => parse_quote!(&mut self),
};
let ret_type = &method.ret_type;
let unsafe_token = method.requires_unsafe.wrapper_token();
if method.is_pure_virtual {
(
None,
parse_quote!(
#unsafe_token fn #id(#params) #ret_type;
),
)
} else {
let a: Option<TraitItem> = Some(parse_quote!(
#unsafe_token fn #super_id(#params) #ret_type;
));
let b: TraitItem = parse_quote!(
#unsafe_token fn #id(#params) #ret_type {
self.#super_id(#param_names)
}
);
(a, b)
}
})
.unzip();
let supers: Vec<_> = supers.into_iter().flatten().collect();
let supers_name = SubclassName::get_supers_trait_name(name).get_final_ident();
let methods_name = SubclassName::get_methods_trait_name(name).get_final_ident();
if !supers.is_empty() {
bindgen_mod_items.push(parse_quote! {
#[allow(non_snake_case)]
pub trait #supers_name {
#(#supers)*
}
});
bindgen_mod_items.push(parse_quote! {
#[allow(non_snake_case)]
pub trait #methods_name : #supers_name {
#(#mains)*
}
});
materializations.push(Use::SpecificNameFromBindgen(supers_name));
} else {
bindgen_mod_items.push(parse_quote! {
#[allow(non_snake_case)]
pub trait #methods_name {
#(#mains)*
}
});
}
materializations.push(Use::SpecificNameFromBindgen(methods_name));
}
}
fn generate_error_entry(err: ConvertError, ctx: ErrorContext) -> RsCodegenResult {
let err = format!("autocxx bindings couldn't be generated: {}", err);
let (impl_entry, bindgen_mod_item, materialization) = match ctx.into_type() {
ErrorContextType::Item(id) => (
None,
Some(parse_quote! {
#[doc = #err]
pub struct #id;
}),
Some(Use::SpecificNameFromBindgen(id)),
),
ErrorContextType::SanitizedItem(id) => (
None,
None,
Some(Use::Custom(Box::new(parse_quote! {
#[doc = #err]
pub struct #id;
}))),
),
ErrorContextType::Method { self_ty, method } => (
Some(Box::new(ImplBlockDetails {
item: parse_quote! {
#[doc = #err]
fn #method(_uhoh: autocxx::BindingGenerationFailure) {
}
},
ty: self_ty,
})),
None,
None,
),
};
RsCodegenResult {
impl_entry,
bindgen_mod_items: bindgen_mod_item.into_iter().collect(),
materializations: materialization.into_iter().collect(),
..Default::default()
}
}
fn generate_cxx_use_stmt(name: &QualifiedName, alias: Option<&Ident>) -> Item {
let segs = Self::find_output_mod_root(name.get_namespace())
.chain(std::iter::once(make_ident("cxxbridge")))
.chain(std::iter::once(name.get_final_ident()));
Item::Use(match alias {
None => parse_quote! {
pub use #(#segs)::*;
},
Some(alias) => parse_quote! {
pub use #(#segs)::* as #alias;
},
})
}
fn generate_bindgen_use_stmt(name: &QualifiedName) -> Item {
let segs =
Self::find_output_mod_root(name.get_namespace()).chain(name.get_bindgen_path_idents());
Item::Use(parse_quote! {
pub use #(#segs)::*;
})
}
fn generate_extern_type_impl(&self, type_kind: TypeKind, tyname: &QualifiedName) -> Vec<Item> {
let tynamestring = namespaced_name_using_original_name_map(tyname, &self.original_name_map);
let fulltypath = tyname.get_bindgen_path_idents();
let kind_item = match type_kind {
TypeKind::Pod => "Trivial",
_ => "Opaque",
};
let kind_item = make_ident(kind_item);
vec![Item::Impl(parse_quote! {
unsafe impl cxx::ExternType for #(#fulltypath)::* {
type Id = cxx::type_id!(#tynamestring);
type Kind = cxx::kind::#kind_item;
}
})]
}
fn generate_cxxbridge_type(
&self,
name: &QualifiedName,
references_bindgen: bool,
doc_attrs: Vec<Attribute>,
) -> ForeignItem {
let ns = name.get_namespace();
let id = name.get_final_ident();
let mut ns_components: Vec<_> = ns.iter().cloned().collect();
let mut cxx_name = None;
if let Some(cpp_name) = self.original_name_map.get(name) {
let cpp_name = QualifiedName::new_from_cpp_name(cpp_name);
cxx_name = Some(cpp_name.get_final_item().to_string());
ns_components.extend(cpp_name.ns_segment_iter().cloned());
};
let mut for_extern_c_ts = if !ns_components.is_empty() {
let ns_string = ns_components.join("::");
quote! {
#[namespace = #ns_string]
}
} else {
TokenStream::new()
};
if let Some(n) = cxx_name {
for_extern_c_ts.extend(quote! {
#[cxx_name = #n]
});
}
for_extern_c_ts.extend(quote! {
#(#doc_attrs)*
});
if references_bindgen {
for_extern_c_ts.extend(quote! {
type #id = super::bindgen::root::
});
for_extern_c_ts.extend(ns.iter().map(make_ident).map(|id| {
quote! {
#id::
}
}));
for_extern_c_ts.extend(quote! {
#id;
});
} else {
for_extern_c_ts.extend(quote! {
type #id;
});
}
ForeignItem::Verbatim(for_extern_c_ts)
}
fn find_output_mod_root(ns: &Namespace) -> impl Iterator<Item = Ident> {
std::iter::repeat(make_ident("super")).take(ns.depth())
}
}
fn find_trivially_constructed_subclasses(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> {
let (simple_constructors, complex_constructors): (Vec<_>, Vec<_>) = apis
.iter()
.filter_map(|api| match api {
Api::Function { fun, .. } => match &fun.provenance {
Provenance::SynthesizedSubclassConstructor(details) => {
Some((&details.subclass.0.name, details.is_trivial))
}
_ => None,
},
_ => None,
})
.partition(|(_, trivial)| *trivial);
let simple_constructors: HashSet<_> =
simple_constructors.into_iter().map(|(qn, _)| qn).collect();
let complex_constructors: HashSet<_> =
complex_constructors.into_iter().map(|(qn, _)| qn).collect();
(&simple_constructors - &complex_constructors)
.into_iter()
.cloned()
.collect()
}
fn find_non_pod_types(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> {
apis.iter()
.filter_map(|api| match api {
Api::Struct {
name,
analysis:
PodAndDepAnalysis {
pod:
PodAnalysis {
kind: TypeKind::NonPod,
..
},
..
},
..
} => Some(name.name.clone()),
_ => None,
})
.collect()
}
impl HasNs for (QualifiedName, RsCodegenResult) {
fn get_namespace(&self) -> &Namespace {
self.0.get_namespace()
}
}
impl<T: AnalysisPhase> HasNs for Api<T> {
fn get_namespace(&self) -> &Namespace {
self.name().get_namespace()
}
}
#[derive(Default)]
struct RsCodegenResult {
extern_c_mod_items: Vec<ForeignItem>,
extern_rust_mod_items: Vec<ForeignItem>,
bridge_items: Vec<Item>,
global_items: Vec<Item>,
bindgen_mod_items: Vec<Item>,
impl_entry: Option<Box<ImplBlockDetails>>,
trait_impl_entry: Option<Box<TraitImplBlockDetails>>,
materializations: Vec<Use>,
}