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 byvalue_checker; |
| 10 | |
| 11 | use indexmap::map::IndexMap as HashMap; |
| 12 | use indexmap::set::IndexSet as HashSet; |
| 13 | |
| 14 | use autocxx_parser::IncludeCppConfig; |
| 15 | use byvalue_checker::ByValueChecker; |
| 16 | use syn::{ItemEnum, ItemStruct, Type, Visibility}; |
| 17 | |
| 18 | use crate::{ |
| 19 | conversion::{ |
| 20 | analysis::type_converter::{self, add_analysis, TypeConversionContext, TypeConverter}, |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame^] | 21 | api::{AnalysisPhase, Api, ApiName, NullPhase, StructDetails, TypeKind}, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 22 | apivec::ApiVec, |
| 23 | convert_error::{ConvertErrorWithContext, ErrorContext}, |
| 24 | error_reporter::convert_apis, |
| 25 | parse::BindgenSemanticAttributes, |
| 26 | ConvertError, |
| 27 | }, |
| 28 | types::{Namespace, QualifiedName}, |
| 29 | }; |
| 30 | |
| 31 | use super::tdef::{TypedefAnalysis, TypedefPhase}; |
| 32 | |
| 33 | pub(crate) struct FieldInfo { |
| 34 | pub(crate) ty: Type, |
| 35 | pub(crate) type_kind: type_converter::TypeKind, |
| 36 | } |
| 37 | |
| 38 | pub(crate) struct PodAnalysis { |
| 39 | pub(crate) kind: TypeKind, |
| 40 | pub(crate) bases: HashSet<QualifiedName>, |
| 41 | /// Base classes for which we should create casts. |
| 42 | /// That's just those which are on the allowlist, |
| 43 | /// because otherwise we don't know whether they're |
| 44 | /// abstract or not. |
| 45 | pub(crate) castable_bases: HashSet<QualifiedName>, |
| 46 | pub(crate) field_deps: HashSet<QualifiedName>, |
| 47 | pub(crate) field_info: Vec<FieldInfo>, |
| 48 | pub(crate) is_generic: bool, |
| 49 | pub(crate) in_anonymous_namespace: bool, |
| 50 | } |
| 51 | |
| 52 | pub(crate) struct PodPhase; |
| 53 | |
| 54 | impl AnalysisPhase for PodPhase { |
| 55 | type TypedefAnalysis = TypedefAnalysis; |
| 56 | type StructAnalysis = PodAnalysis; |
| 57 | type FunAnalysis = (); |
| 58 | } |
| 59 | |
| 60 | /// In our set of APIs, work out which ones are safe to represent |
| 61 | /// by value in Rust (e.g. they don't have a destructor) and record |
| 62 | /// as such. Return a set of APIs annotated with extra metadata, |
| 63 | /// and an object which can be used to query the POD status of any |
| 64 | /// type whether or not it's one of the [Api]s. |
| 65 | pub(crate) fn analyze_pod_apis( |
| 66 | apis: ApiVec<TypedefPhase>, |
| 67 | config: &IncludeCppConfig, |
| 68 | ) -> Result<ApiVec<PodPhase>, ConvertError> { |
| 69 | // This next line will return an error if any of the 'generate_pod' |
| 70 | // directives from the user can't be met because, for instance, |
| 71 | // a type contains a std::string or some other type which can't be |
| 72 | // held safely by value in Rust. |
| 73 | let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?; |
| 74 | let mut extra_apis = ApiVec::new(); |
| 75 | let mut type_converter = TypeConverter::new(config, &apis); |
| 76 | let mut results = ApiVec::new(); |
| 77 | convert_apis( |
| 78 | apis, |
| 79 | &mut results, |
| 80 | Api::fun_unchanged, |
| 81 | |name, details, _| { |
| 82 | analyze_struct( |
| 83 | &byvalue_checker, |
| 84 | &mut type_converter, |
| 85 | &mut extra_apis, |
| 86 | name, |
| 87 | details, |
| 88 | config, |
| 89 | ) |
| 90 | }, |
| 91 | analyze_enum, |
| 92 | Api::typedef_unchanged, |
| 93 | ); |
| 94 | // Conceivably, the process of POD-analysing the first set of APIs could result |
| 95 | // in us creating new APIs to concretize generic types. |
| 96 | let extra_apis: ApiVec<PodPhase> = extra_apis.into_iter().map(add_analysis).collect(); |
| 97 | let mut more_extra_apis = ApiVec::new(); |
| 98 | convert_apis( |
| 99 | extra_apis, |
| 100 | &mut results, |
| 101 | Api::fun_unchanged, |
| 102 | |name, details, _| { |
| 103 | analyze_struct( |
| 104 | &byvalue_checker, |
| 105 | &mut type_converter, |
| 106 | &mut more_extra_apis, |
| 107 | name, |
| 108 | details, |
| 109 | config, |
| 110 | ) |
| 111 | }, |
| 112 | analyze_enum, |
| 113 | Api::typedef_unchanged, |
| 114 | ); |
| 115 | assert!(more_extra_apis.is_empty()); |
| 116 | Ok(results) |
| 117 | } |
| 118 | |
| 119 | fn analyze_enum( |
| 120 | name: ApiName, |
| 121 | mut item: ItemEnum, |
| 122 | ) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> { |
| 123 | let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs); |
| 124 | metadata.check_for_fatal_attrs(&name.name.get_final_ident())?; |
| 125 | Ok(Box::new(std::iter::once(Api::Enum { name, item }))) |
| 126 | } |
| 127 | |
| 128 | fn analyze_struct( |
| 129 | byvalue_checker: &ByValueChecker, |
| 130 | type_converter: &mut TypeConverter, |
| 131 | extra_apis: &mut ApiVec<NullPhase>, |
| 132 | name: ApiName, |
| 133 | mut details: Box<StructDetails>, |
| 134 | config: &IncludeCppConfig, |
| 135 | ) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> { |
| 136 | let id = name.name.get_final_ident(); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 137 | let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs); |
| 138 | metadata.check_for_fatal_attrs(&id)?; |
| 139 | let bases = get_bases(&details.item); |
| 140 | let mut field_deps = HashSet::new(); |
| 141 | let mut field_info = Vec::new(); |
| 142 | let field_conversion_errors = get_struct_field_types( |
| 143 | type_converter, |
| 144 | name.name.get_namespace(), |
| 145 | &details.item, |
| 146 | &mut field_deps, |
| 147 | &mut field_info, |
| 148 | extra_apis, |
| 149 | ); |
| 150 | let type_kind = if byvalue_checker.is_pod(&name.name) { |
| 151 | // It's POD so any errors encountered parsing its fields are important. |
| 152 | // Let's not allow anything to be POD if it's got rvalue reference fields. |
| 153 | if details.has_rvalue_reference_fields { |
| 154 | return Err(ConvertErrorWithContext( |
| 155 | ConvertError::RValueReferenceField, |
| 156 | Some(ErrorContext::new_for_item(id)), |
| 157 | )); |
| 158 | } |
| 159 | if let Some(err) = field_conversion_errors.into_iter().next() { |
| 160 | return Err(ConvertErrorWithContext( |
| 161 | err, |
| 162 | Some(ErrorContext::new_for_item(id)), |
| 163 | )); |
| 164 | } |
| 165 | TypeKind::Pod |
| 166 | } else { |
| 167 | TypeKind::NonPod |
| 168 | }; |
| 169 | let castable_bases = bases |
| 170 | .iter() |
| 171 | .filter(|(_, is_public)| **is_public) |
| 172 | .map(|(base, _)| base) |
| 173 | .filter(|base| config.is_on_allowlist(&base.to_cpp_name())) |
| 174 | .cloned() |
| 175 | .collect(); |
| 176 | let is_generic = !details.item.generics.params.is_empty(); |
| 177 | let in_anonymous_namespace = name |
| 178 | .name |
| 179 | .ns_segment_iter() |
| 180 | .any(|ns| ns.starts_with("_bindgen_mod")); |
| 181 | Ok(Box::new(std::iter::once(Api::Struct { |
| 182 | name, |
| 183 | details, |
| 184 | analysis: PodAnalysis { |
| 185 | kind: type_kind, |
| 186 | bases: bases.into_keys().collect(), |
| 187 | castable_bases, |
| 188 | field_deps, |
| 189 | field_info, |
| 190 | is_generic, |
| 191 | in_anonymous_namespace, |
| 192 | }, |
| 193 | }))) |
| 194 | } |
| 195 | |
| 196 | fn get_struct_field_types( |
| 197 | type_converter: &mut TypeConverter, |
| 198 | ns: &Namespace, |
| 199 | s: &ItemStruct, |
| 200 | field_deps: &mut HashSet<QualifiedName>, |
| 201 | field_info: &mut Vec<FieldInfo>, |
| 202 | extra_apis: &mut ApiVec<NullPhase>, |
| 203 | ) -> Vec<ConvertError> { |
| 204 | let mut convert_errors = Vec::new(); |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame^] | 205 | let struct_type_params = s |
| 206 | .generics |
| 207 | .type_params() |
| 208 | .map(|tp| tp.ident.clone()) |
| 209 | .collect(); |
| 210 | let type_conversion_context = TypeConversionContext::WithinStructField { struct_type_params }; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 211 | for f in &s.fields { |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame^] | 212 | let annotated = type_converter.convert_type(f.ty.clone(), ns, &type_conversion_context); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 213 | match annotated { |
| 214 | Ok(mut r) => { |
| 215 | extra_apis.append(&mut r.extra_apis); |
| 216 | // Skip base classes represented as fields. Anything which wants to include bases can chain |
| 217 | // those to the list we're building. |
| 218 | if !f |
| 219 | .ident |
| 220 | .as_ref() |
| 221 | .map(|id| { |
| 222 | id.to_string().starts_with("_base") |
| 223 | || id.to_string().starts_with("__bindgen_padding") |
| 224 | }) |
| 225 | .unwrap_or(false) |
| 226 | { |
| 227 | field_deps.extend(r.types_encountered); |
| 228 | field_info.push(FieldInfo { |
| 229 | ty: r.ty, |
| 230 | type_kind: r.kind, |
| 231 | }); |
| 232 | } |
| 233 | } |
| 234 | Err(e) => convert_errors.push(e), |
| 235 | }; |
| 236 | } |
| 237 | convert_errors |
| 238 | } |
| 239 | |
| 240 | /// Map to whether the bases are public. |
| 241 | fn get_bases(item: &ItemStruct) -> HashMap<QualifiedName, bool> { |
| 242 | item.fields |
| 243 | .iter() |
| 244 | .filter_map(|f| { |
| 245 | let is_public = matches!(f.vis, Visibility::Public(_)); |
| 246 | match &f.ty { |
| 247 | Type::Path(typ) => f |
| 248 | .ident |
| 249 | .as_ref() |
| 250 | .filter(|id| id.to_string().starts_with("_base")) |
| 251 | .map(|_| (QualifiedName::from_type_path(typ), is_public)), |
| 252 | _ => None, |
| 253 | } |
| 254 | }) |
| 255 | .collect() |
| 256 | } |