| // Copyright 2021 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. |
| |
| use indexmap::map::IndexMap as HashMap; |
| |
| use syn::{parse_quote, FnArg, PatType, Type, TypePtr}; |
| |
| use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability}; |
| use crate::conversion::analysis::pod::PodPhase; |
| use crate::conversion::api::{ |
| CppVisibility, FuncToConvert, Provenance, RustSubclassFnDetails, SubclassConstructorDetails, |
| SubclassName, SuperclassMethod, UnsafetyNeeded, Virtualness, |
| }; |
| use crate::conversion::apivec::ApiVec; |
| use crate::{ |
| conversion::{ |
| analysis::fun::function_wrapper::{ |
| CppFunction, CppFunctionBody, CppFunctionKind, TypeConversionPolicy, |
| }, |
| api::{Api, ApiName}, |
| }, |
| types::{make_ident, Namespace, QualifiedName}, |
| }; |
| |
| use super::{FnAnalysis, FnPrePhase1}; |
| |
| pub(super) fn subclasses_by_superclass( |
| apis: &ApiVec<PodPhase>, |
| ) -> HashMap<QualifiedName, Vec<SubclassName>> { |
| let mut subclasses_per_superclass: HashMap<QualifiedName, Vec<SubclassName>> = HashMap::new(); |
| |
| for api in apis.iter() { |
| if let Api::Subclass { name, superclass } = api { |
| subclasses_per_superclass |
| .entry(superclass.clone()) |
| .or_default() |
| .push(name.clone()); |
| } |
| } |
| subclasses_per_superclass |
| } |
| |
| pub(super) fn create_subclass_fn_wrapper( |
| sub: &SubclassName, |
| super_fn_name: &QualifiedName, |
| fun: &FuncToConvert, |
| ) -> Box<FuncToConvert> { |
| let self_ty = Some(sub.cpp()); |
| Box::new(FuncToConvert { |
| synthesized_this_type: self_ty.clone(), |
| self_ty, |
| ident: super_fn_name.get_final_ident(), |
| doc_attrs: fun.doc_attrs.clone(), |
| inputs: fun.inputs.clone(), |
| output: fun.output.clone(), |
| vis: fun.vis.clone(), |
| virtualness: Virtualness::None, |
| cpp_vis: CppVisibility::Public, |
| special_member: None, |
| unused_template_param: fun.unused_template_param, |
| original_name: None, |
| references: fun.references.clone(), |
| add_to_trait: fun.add_to_trait.clone(), |
| is_deleted: fun.is_deleted, |
| synthetic_cpp: None, |
| provenance: Provenance::SynthesizedOther, |
| variadic: fun.variadic, |
| }) |
| } |
| |
| pub(super) fn create_subclass_trait_item( |
| name: ApiName, |
| analysis: &FnAnalysis, |
| receiver_mutability: &ReceiverMutability, |
| receiver: QualifiedName, |
| is_pure_virtual: bool, |
| ) -> Api<FnPrePhase1> { |
| let param_names = analysis |
| .param_details |
| .iter() |
| .map(|pd| pd.name.clone()) |
| .collect(); |
| Api::SubclassTraitItem { |
| name, |
| details: SuperclassMethod { |
| name: make_ident(&analysis.rust_name), |
| params: analysis.params.clone(), |
| ret_type: analysis.ret_type.clone(), |
| param_names, |
| receiver_mutability: receiver_mutability.clone(), |
| requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false), |
| is_pure_virtual, |
| receiver, |
| }, |
| } |
| } |
| |
| pub(super) fn create_subclass_function( |
| sub: &SubclassName, |
| analysis: &super::FnAnalysis, |
| name: &ApiName, |
| receiver_mutability: &ReceiverMutability, |
| superclass: &QualifiedName, |
| dependencies: Vec<QualifiedName>, |
| ) -> Api<FnPrePhase1> { |
| let cpp = sub.cpp(); |
| let holder_name = sub.holder(); |
| let rust_call_name = make_ident(format!( |
| "{}_{}", |
| sub.0.name.get_final_item(), |
| name.name.get_final_item() |
| )); |
| let params = std::iter::once(parse_quote! { |
| me: & #holder_name |
| }) |
| .chain(analysis.params.iter().skip(1).cloned()) |
| .collect(); |
| let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) { |
| CppFunctionKind::Method |
| } else { |
| CppFunctionKind::ConstMethod |
| }; |
| let argument_conversion = analysis |
| .param_details |
| .iter() |
| .skip(1) |
| .map(|p| p.conversion.clone()) |
| .collect(); |
| Api::RustSubclassFn { |
| name: ApiName::new_in_root_namespace(rust_call_name.clone()), |
| subclass: sub.clone(), |
| details: Box::new(RustSubclassFnDetails { |
| params, |
| ret: analysis.ret_type.clone(), |
| method_name: make_ident(&analysis.rust_name), |
| cpp_impl: CppFunction { |
| payload: CppFunctionBody::FunctionCall(Namespace::new(), rust_call_name), |
| wrapper_function_name: make_ident(&analysis.rust_name), |
| original_cpp_name: name.cpp_name(), |
| return_conversion: analysis.ret_conversion.clone(), |
| argument_conversion, |
| kind, |
| pass_obs_field: true, |
| qualification: Some(cpp), |
| }, |
| superclass: superclass.clone(), |
| receiver_mutability: receiver_mutability.clone(), |
| dependencies, |
| requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false), |
| is_pure_virtual: matches!( |
| analysis.kind, |
| FnKind::Method { |
| method_kind: MethodKind::PureVirtual(..), |
| .. |
| } |
| ), |
| }), |
| } |
| } |
| |
| pub(super) fn create_subclass_constructor( |
| sub: SubclassName, |
| analysis: &FnAnalysis, |
| sup: &QualifiedName, |
| fun: &FuncToConvert, |
| ) -> (Box<FuncToConvert>, ApiName) { |
| let holder = sub.holder(); |
| let cpp = sub.cpp(); |
| let wrapper_function_name = cpp.get_final_ident(); |
| let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! { |
| rust::Box< #holder > |
| }); |
| let args = std::iter::once(initial_arg).chain( |
| analysis |
| .param_details |
| .iter() |
| .skip(1) // skip placement new destination |
| .map(|aa| aa.conversion.clone()), |
| ); |
| let cpp_impl = CppFunction { |
| payload: CppFunctionBody::ConstructSuperclass(sup.to_cpp_name()), |
| wrapper_function_name, |
| return_conversion: None, |
| argument_conversion: args.collect(), |
| kind: CppFunctionKind::SynthesizedConstructor, |
| pass_obs_field: false, |
| qualification: Some(cpp.clone()), |
| original_cpp_name: cpp.to_cpp_name(), |
| }; |
| let subclass_constructor_details = Box::new(SubclassConstructorDetails { |
| subclass: sub.clone(), |
| is_trivial: analysis.param_details.len() == 1, // just placement new |
| // destination, no other parameters |
| cpp_impl, |
| }); |
| let subclass_constructor_name = |
| make_ident(format!("{}_{}", cpp.get_final_item(), cpp.get_final_item())); |
| let mut existing_params = fun.inputs.clone(); |
| if let Some(FnArg::Typed(PatType { ty, .. })) = existing_params.first_mut() { |
| if let Type::Ptr(TypePtr { elem, .. }) = &mut **ty { |
| *elem = Box::new(Type::Path(sub.cpp().to_type_path())); |
| } else { |
| panic!("Unexpected self type parameter when creating subclass constructor"); |
| } |
| } else { |
| panic!("Unexpected self type parameter when creating subclass constructor"); |
| } |
| let mut existing_params = existing_params.into_iter(); |
| let self_param = existing_params.next(); |
| let boxed_holder_param: FnArg = parse_quote! { |
| peer: rust::Box<#holder> |
| }; |
| let inputs = self_param |
| .into_iter() |
| .chain(std::iter::once(boxed_holder_param)) |
| .chain(existing_params) |
| .collect(); |
| let maybe_wrap = Box::new(FuncToConvert { |
| ident: subclass_constructor_name.clone(), |
| doc_attrs: fun.doc_attrs.clone(), |
| inputs, |
| output: fun.output.clone(), |
| vis: fun.vis.clone(), |
| virtualness: Virtualness::None, |
| cpp_vis: CppVisibility::Public, |
| special_member: fun.special_member.clone(), |
| original_name: None, |
| unused_template_param: fun.unused_template_param, |
| references: fun.references.clone(), |
| synthesized_this_type: Some(cpp.clone()), |
| self_ty: Some(cpp), |
| add_to_trait: None, |
| is_deleted: fun.is_deleted, |
| synthetic_cpp: None, |
| provenance: Provenance::SynthesizedSubclassConstructor(subclass_constructor_details), |
| variadic: fun.variadic, |
| }); |
| let subclass_constructor_name = ApiName::new_with_cpp_name( |
| &Namespace::new(), |
| subclass_constructor_name, |
| Some(sub.cpp().get_final_item().to_string()), |
| ); |
| (maybe_wrap, subclass_constructor_name) |
| } |