Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame^] | 1 | // Copyright 2021 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 | use indexmap::map::IndexMap as HashMap; |
| 10 | |
| 11 | use syn::{parse_quote, FnArg, PatType, Type, TypePtr}; |
| 12 | |
| 13 | use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability}; |
| 14 | use crate::conversion::analysis::pod::PodPhase; |
| 15 | use crate::conversion::api::{ |
| 16 | CppVisibility, FuncToConvert, Provenance, RustSubclassFnDetails, SubclassConstructorDetails, |
| 17 | SubclassName, SuperclassMethod, UnsafetyNeeded, Virtualness, |
| 18 | }; |
| 19 | use crate::conversion::apivec::ApiVec; |
| 20 | use crate::{ |
| 21 | conversion::{ |
| 22 | analysis::fun::function_wrapper::{ |
| 23 | CppFunction, CppFunctionBody, CppFunctionKind, TypeConversionPolicy, |
| 24 | }, |
| 25 | api::{Api, ApiName}, |
| 26 | }, |
| 27 | types::{make_ident, Namespace, QualifiedName}, |
| 28 | }; |
| 29 | |
| 30 | use super::{FnAnalysis, FnPrePhase1}; |
| 31 | |
| 32 | pub(super) fn subclasses_by_superclass( |
| 33 | apis: &ApiVec<PodPhase>, |
| 34 | ) -> HashMap<QualifiedName, Vec<SubclassName>> { |
| 35 | let mut subclasses_per_superclass: HashMap<QualifiedName, Vec<SubclassName>> = HashMap::new(); |
| 36 | |
| 37 | for api in apis.iter() { |
| 38 | if let Api::Subclass { name, superclass } = api { |
| 39 | subclasses_per_superclass |
| 40 | .entry(superclass.clone()) |
| 41 | .or_default() |
| 42 | .push(name.clone()); |
| 43 | } |
| 44 | } |
| 45 | subclasses_per_superclass |
| 46 | } |
| 47 | |
| 48 | pub(super) fn create_subclass_fn_wrapper( |
| 49 | sub: &SubclassName, |
| 50 | super_fn_name: &QualifiedName, |
| 51 | fun: &FuncToConvert, |
| 52 | ) -> Box<FuncToConvert> { |
| 53 | let self_ty = Some(sub.cpp()); |
| 54 | Box::new(FuncToConvert { |
| 55 | synthesized_this_type: self_ty.clone(), |
| 56 | self_ty, |
| 57 | ident: super_fn_name.get_final_ident(), |
| 58 | doc_attrs: fun.doc_attrs.clone(), |
| 59 | inputs: fun.inputs.clone(), |
| 60 | output: fun.output.clone(), |
| 61 | vis: fun.vis.clone(), |
| 62 | virtualness: Virtualness::None, |
| 63 | cpp_vis: CppVisibility::Public, |
| 64 | special_member: None, |
| 65 | unused_template_param: fun.unused_template_param, |
| 66 | original_name: None, |
| 67 | references: fun.references.clone(), |
| 68 | add_to_trait: fun.add_to_trait.clone(), |
| 69 | is_deleted: fun.is_deleted, |
| 70 | synthetic_cpp: None, |
| 71 | provenance: Provenance::SynthesizedOther, |
| 72 | variadic: fun.variadic, |
| 73 | }) |
| 74 | } |
| 75 | |
| 76 | pub(super) fn create_subclass_trait_item( |
| 77 | name: ApiName, |
| 78 | analysis: &FnAnalysis, |
| 79 | receiver_mutability: &ReceiverMutability, |
| 80 | receiver: QualifiedName, |
| 81 | is_pure_virtual: bool, |
| 82 | ) -> Api<FnPrePhase1> { |
| 83 | let param_names = analysis |
| 84 | .param_details |
| 85 | .iter() |
| 86 | .map(|pd| pd.name.clone()) |
| 87 | .collect(); |
| 88 | Api::SubclassTraitItem { |
| 89 | name, |
| 90 | details: SuperclassMethod { |
| 91 | name: make_ident(&analysis.rust_name), |
| 92 | params: analysis.params.clone(), |
| 93 | ret_type: analysis.ret_type.clone(), |
| 94 | param_names, |
| 95 | receiver_mutability: receiver_mutability.clone(), |
| 96 | requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false), |
| 97 | is_pure_virtual, |
| 98 | receiver, |
| 99 | }, |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | pub(super) fn create_subclass_function( |
| 104 | sub: &SubclassName, |
| 105 | analysis: &super::FnAnalysis, |
| 106 | name: &ApiName, |
| 107 | receiver_mutability: &ReceiverMutability, |
| 108 | superclass: &QualifiedName, |
| 109 | dependencies: Vec<QualifiedName>, |
| 110 | ) -> Api<FnPrePhase1> { |
| 111 | let cpp = sub.cpp(); |
| 112 | let holder_name = sub.holder(); |
| 113 | let rust_call_name = make_ident(format!( |
| 114 | "{}_{}", |
| 115 | sub.0.name.get_final_item(), |
| 116 | name.name.get_final_item() |
| 117 | )); |
| 118 | let params = std::iter::once(parse_quote! { |
| 119 | me: & #holder_name |
| 120 | }) |
| 121 | .chain(analysis.params.iter().skip(1).cloned()) |
| 122 | .collect(); |
| 123 | let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) { |
| 124 | CppFunctionKind::Method |
| 125 | } else { |
| 126 | CppFunctionKind::ConstMethod |
| 127 | }; |
| 128 | let argument_conversion = analysis |
| 129 | .param_details |
| 130 | .iter() |
| 131 | .skip(1) |
| 132 | .map(|p| p.conversion.clone()) |
| 133 | .collect(); |
| 134 | Api::RustSubclassFn { |
| 135 | name: ApiName::new_in_root_namespace(rust_call_name.clone()), |
| 136 | subclass: sub.clone(), |
| 137 | details: Box::new(RustSubclassFnDetails { |
| 138 | params, |
| 139 | ret: analysis.ret_type.clone(), |
| 140 | method_name: make_ident(&analysis.rust_name), |
| 141 | cpp_impl: CppFunction { |
| 142 | payload: CppFunctionBody::FunctionCall(Namespace::new(), rust_call_name), |
| 143 | wrapper_function_name: make_ident(&analysis.rust_name), |
| 144 | original_cpp_name: name.cpp_name(), |
| 145 | return_conversion: analysis.ret_conversion.clone(), |
| 146 | argument_conversion, |
| 147 | kind, |
| 148 | pass_obs_field: true, |
| 149 | qualification: Some(cpp), |
| 150 | }, |
| 151 | superclass: superclass.clone(), |
| 152 | receiver_mutability: receiver_mutability.clone(), |
| 153 | dependencies, |
| 154 | requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false), |
| 155 | is_pure_virtual: matches!( |
| 156 | analysis.kind, |
| 157 | FnKind::Method { |
| 158 | method_kind: MethodKind::PureVirtual(..), |
| 159 | .. |
| 160 | } |
| 161 | ), |
| 162 | }), |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | pub(super) fn create_subclass_constructor( |
| 167 | sub: SubclassName, |
| 168 | analysis: &FnAnalysis, |
| 169 | sup: &QualifiedName, |
| 170 | fun: &FuncToConvert, |
| 171 | ) -> (Box<FuncToConvert>, ApiName) { |
| 172 | let holder = sub.holder(); |
| 173 | let cpp = sub.cpp(); |
| 174 | let wrapper_function_name = cpp.get_final_ident(); |
| 175 | let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! { |
| 176 | rust::Box< #holder > |
| 177 | }); |
| 178 | let args = std::iter::once(initial_arg).chain( |
| 179 | analysis |
| 180 | .param_details |
| 181 | .iter() |
| 182 | .skip(1) // skip placement new destination |
| 183 | .map(|aa| aa.conversion.clone()), |
| 184 | ); |
| 185 | let cpp_impl = CppFunction { |
| 186 | payload: CppFunctionBody::ConstructSuperclass(sup.to_cpp_name()), |
| 187 | wrapper_function_name, |
| 188 | return_conversion: None, |
| 189 | argument_conversion: args.collect(), |
| 190 | kind: CppFunctionKind::SynthesizedConstructor, |
| 191 | pass_obs_field: false, |
| 192 | qualification: Some(cpp.clone()), |
| 193 | original_cpp_name: cpp.to_cpp_name(), |
| 194 | }; |
| 195 | let subclass_constructor_details = Box::new(SubclassConstructorDetails { |
| 196 | subclass: sub.clone(), |
| 197 | is_trivial: analysis.param_details.len() == 1, // just placement new |
| 198 | // destination, no other parameters |
| 199 | cpp_impl, |
| 200 | }); |
| 201 | let subclass_constructor_name = |
| 202 | make_ident(format!("{}_{}", cpp.get_final_item(), cpp.get_final_item())); |
| 203 | let mut existing_params = fun.inputs.clone(); |
| 204 | if let Some(FnArg::Typed(PatType { ty, .. })) = existing_params.first_mut() { |
| 205 | if let Type::Ptr(TypePtr { elem, .. }) = &mut **ty { |
| 206 | *elem = Box::new(Type::Path(sub.cpp().to_type_path())); |
| 207 | } else { |
| 208 | panic!("Unexpected self type parameter when creating subclass constructor"); |
| 209 | } |
| 210 | } else { |
| 211 | panic!("Unexpected self type parameter when creating subclass constructor"); |
| 212 | } |
| 213 | let mut existing_params = existing_params.into_iter(); |
| 214 | let self_param = existing_params.next(); |
| 215 | let boxed_holder_param: FnArg = parse_quote! { |
| 216 | peer: rust::Box<#holder> |
| 217 | }; |
| 218 | let inputs = self_param |
| 219 | .into_iter() |
| 220 | .chain(std::iter::once(boxed_holder_param)) |
| 221 | .chain(existing_params) |
| 222 | .collect(); |
| 223 | let maybe_wrap = Box::new(FuncToConvert { |
| 224 | ident: subclass_constructor_name.clone(), |
| 225 | doc_attrs: fun.doc_attrs.clone(), |
| 226 | inputs, |
| 227 | output: fun.output.clone(), |
| 228 | vis: fun.vis.clone(), |
| 229 | virtualness: Virtualness::None, |
| 230 | cpp_vis: CppVisibility::Public, |
| 231 | special_member: fun.special_member.clone(), |
| 232 | original_name: None, |
| 233 | unused_template_param: fun.unused_template_param, |
| 234 | references: fun.references.clone(), |
| 235 | synthesized_this_type: Some(cpp.clone()), |
| 236 | self_ty: Some(cpp), |
| 237 | add_to_trait: None, |
| 238 | is_deleted: fun.is_deleted, |
| 239 | synthetic_cpp: None, |
| 240 | provenance: Provenance::SynthesizedSubclassConstructor(subclass_constructor_details), |
| 241 | variadic: fun.variadic, |
| 242 | }); |
| 243 | let subclass_constructor_name = ApiName::new_with_cpp_name( |
| 244 | &Namespace::new(), |
| 245 | subclass_constructor_name, |
| 246 | Some(sub.cpp().get_final_item().to_string()), |
| 247 | ); |
| 248 | (maybe_wrap, subclass_constructor_name) |
| 249 | } |