1 use crate::codegen;
2 use crate::ir::context::BindgenContext;
3 use crate::ir::function::ClangAbi;
4 use proc_macro2::Ident;
5 
6 /// Used to build the output tokens for dynamic bindings.
7 #[derive(Default)]
8 pub(crate) struct DynamicItems {
9     /// Tracks the tokens that will appears inside the library struct -- e.g.:
10     /// ```ignore
11     /// struct Lib {
12     ///    __library: ::libloading::Library,
13     ///    pub x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
14     ///    ...
15     /// }
16     /// ```
17     struct_members: Vec<proc_macro2::TokenStream>,
18 
19     /// Tracks the tokens that will appear inside the library struct's implementation, e.g.:
20     ///
21     /// ```ignore
22     /// impl Lib {
23     ///     ...
24     ///     pub unsafe fn foo(&self, ...) { // <- tracks these
25     ///         ...
26     ///     }
27     /// }
28     /// ```
29     struct_implementation: Vec<proc_macro2::TokenStream>,
30 
31     /// Tracks the initialization of the fields inside the `::new` constructor of the library
32     /// struct, e.g.:
33     /// ```ignore
34     /// impl Lib {
35     ///
36     ///     pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
37     ///     where
38     ///         P: AsRef<::std::ffi::OsStr>,
39     ///     {
40     ///         ...
41     ///         let foo = __library.get(...) ...; // <- tracks these
42     ///         ...
43     ///     }
44     ///
45     ///     ...
46     /// }
47     /// ```
48     constructor_inits: Vec<proc_macro2::TokenStream>,
49 
50     /// Tracks the information that is passed to the library struct at the end of the `::new`
51     /// constructor, e.g.:
52     /// ```ignore
53     /// impl LibFoo {
54     ///     pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
55     ///     where
56     ///         P: AsRef<::std::ffi::OsStr>,
57     ///     {
58     ///         ...
59     ///         Ok(LibFoo {
60     ///             __library: __library,
61     ///             foo,
62     ///             bar, // <- tracks these
63     ///             ...
64     ///         })
65     ///     }
66     /// }
67     /// ```
68     init_fields: Vec<proc_macro2::TokenStream>,
69 }
70 
71 impl DynamicItems {
new() -> Self72     pub(crate) fn new() -> Self {
73         Self::default()
74     }
75 
get_tokens( &self, lib_ident: Ident, ctx: &BindgenContext, ) -> proc_macro2::TokenStream76     pub(crate) fn get_tokens(
77         &self,
78         lib_ident: Ident,
79         ctx: &BindgenContext,
80     ) -> proc_macro2::TokenStream {
81         let struct_members = &self.struct_members;
82         let constructor_inits = &self.constructor_inits;
83         let init_fields = &self.init_fields;
84         let struct_implementation = &self.struct_implementation;
85 
86         let from_library = if ctx.options().wrap_unsafe_ops {
87             quote!(unsafe { Self::from_library(library) })
88         } else {
89             quote!(Self::from_library(library))
90         };
91 
92         quote! {
93             extern crate libloading;
94 
95             pub struct #lib_ident {
96                 __library: ::libloading::Library,
97                 #(#struct_members)*
98             }
99 
100             impl #lib_ident {
101                 pub unsafe fn new<P>(
102                     path: P
103                 ) -> Result<Self, ::libloading::Error>
104                 where P: AsRef<::std::ffi::OsStr> {
105                     let library = ::libloading::Library::new(path)?;
106                     #from_library
107                 }
108 
109                 pub unsafe fn from_library<L>(
110                     library: L
111                 ) -> Result<Self, ::libloading::Error>
112                 where L: Into<::libloading::Library> {
113                     let __library = library.into();
114                     #( #constructor_inits )*
115                     Ok(#lib_ident {
116                         __library,
117                         #( #init_fields ),*
118                     })
119                 }
120 
121                 #( #struct_implementation )*
122             }
123         }
124     }
125 
126     #[allow(clippy::too_many_arguments)]
push( &mut self, ident: Ident, abi: ClangAbi, is_variadic: bool, is_required: bool, args: Vec<proc_macro2::TokenStream>, args_identifiers: Vec<proc_macro2::TokenStream>, ret: proc_macro2::TokenStream, ret_ty: proc_macro2::TokenStream, attributes: Vec<proc_macro2::TokenStream>, ctx: &BindgenContext, )127     pub(crate) fn push(
128         &mut self,
129         ident: Ident,
130         abi: ClangAbi,
131         is_variadic: bool,
132         is_required: bool,
133         args: Vec<proc_macro2::TokenStream>,
134         args_identifiers: Vec<proc_macro2::TokenStream>,
135         ret: proc_macro2::TokenStream,
136         ret_ty: proc_macro2::TokenStream,
137         attributes: Vec<proc_macro2::TokenStream>,
138         ctx: &BindgenContext,
139     ) {
140         if !is_variadic {
141             assert_eq!(args.len(), args_identifiers.len());
142         }
143 
144         let signature = quote! { unsafe extern #abi fn ( #( #args),* ) #ret };
145         let member = if is_required {
146             signature
147         } else {
148             quote! { Result<#signature, ::libloading::Error> }
149         };
150 
151         self.struct_members.push(quote! {
152             pub #ident: #member,
153         });
154 
155         // N.B: If the signature was required, it won't be wrapped in a Result<...>
156         //      and we can simply call it directly.
157         let fn_ = if is_required {
158             quote! { self.#ident }
159         } else {
160             quote! { self.#ident.as_ref().expect("Expected function, got error.") }
161         };
162         let call_body = if ctx.options().wrap_unsafe_ops {
163             quote!(unsafe { (#fn_)(#( #args_identifiers ),*) })
164         } else {
165             quote!((#fn_)(#( #args_identifiers ),*) )
166         };
167 
168         // We can't implement variadic functions from C easily, so we allow to
169         // access the function pointer so that the user can call it just fine.
170         if !is_variadic {
171             self.struct_implementation.push(quote! {
172                 #(#attributes)*
173                 pub unsafe fn #ident ( &self, #( #args ),* ) #ret_ty {
174                     #call_body
175                 }
176             });
177         }
178 
179         // N.B: Unwrap the signature upon construction if it is required to be resolved.
180         let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
181         let library_get = if ctx.options().wrap_unsafe_ops {
182             quote!(unsafe { __library.get(#ident_str) })
183         } else {
184             quote!(__library.get(#ident_str))
185         };
186 
187         self.constructor_inits.push(if is_required {
188             quote! {
189                 let #ident = #library_get.map(|sym| *sym)?;
190             }
191         } else {
192             quote! {
193                 let #ident = #library_get.map(|sym| *sym);
194             }
195         });
196 
197         self.init_fields.push(quote! {
198             #ident
199         });
200     }
201 }
202