• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::codegen;
2 use crate::ir::context::BindgenContext;
3 use crate::ir::function::ClangAbi;
4 use proc_macro2::{Ident, TokenStream};
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             pub struct #lib_ident {
94                 __library: ::libloading::Library,
95                 #(#struct_members)*
96             }
97 
98             impl #lib_ident {
99                 pub unsafe fn new<P>(
100                     path: P
101                 ) -> Result<Self, ::libloading::Error>
102                 where P: AsRef<::std::ffi::OsStr> {
103                     let library = ::libloading::Library::new(path)?;
104                     #from_library
105                 }
106 
107                 pub unsafe fn from_library<L>(
108                     library: L
109                 ) -> Result<Self, ::libloading::Error>
110                 where L: Into<::libloading::Library> {
111                     let __library = library.into();
112                     #( #constructor_inits )*
113                     Ok(#lib_ident {
114                         __library,
115                         #( #init_fields ),*
116                     })
117                 }
118 
119                 #( #struct_implementation )*
120             }
121         }
122     }
123 
124     #[allow(clippy::too_many_arguments)]
push_func( &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, )125     pub(crate) fn push_func(
126         &mut self,
127         ident: Ident,
128         abi: ClangAbi,
129         is_variadic: bool,
130         is_required: bool,
131         args: Vec<proc_macro2::TokenStream>,
132         args_identifiers: Vec<proc_macro2::TokenStream>,
133         ret: proc_macro2::TokenStream,
134         ret_ty: proc_macro2::TokenStream,
135         attributes: Vec<proc_macro2::TokenStream>,
136         ctx: &BindgenContext,
137     ) {
138         if !is_variadic {
139             assert_eq!(args.len(), args_identifiers.len());
140         }
141 
142         let signature = quote! { unsafe extern #abi fn ( #( #args),* ) #ret };
143         let member = if is_required {
144             signature
145         } else {
146             quote! { Result<#signature, ::libloading::Error> }
147         };
148 
149         self.struct_members.push(quote! {
150             pub #ident: #member,
151         });
152 
153         // N.B: If the signature was required, it won't be wrapped in a Result<...>
154         //      and we can simply call it directly.
155         let fn_ = if is_required {
156             quote! { self.#ident }
157         } else {
158             quote! { self.#ident.as_ref().expect("Expected function, got error.") }
159         };
160         let call_body = if ctx.options().wrap_unsafe_ops {
161             quote!(unsafe { (#fn_)(#( #args_identifiers ),*) })
162         } else {
163             quote!((#fn_)(#( #args_identifiers ),*) )
164         };
165 
166         // We can't implement variadic functions from C easily, so we allow to
167         // access the function pointer so that the user can call it just fine.
168         if !is_variadic {
169             self.struct_implementation.push(quote! {
170                 #(#attributes)*
171                 pub unsafe fn #ident ( &self, #( #args ),* ) #ret_ty {
172                     #call_body
173                 }
174             });
175         }
176 
177         // N.B: Unwrap the signature upon construction if it is required to be resolved.
178         let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
179         let library_get = if ctx.options().wrap_unsafe_ops {
180             quote!(unsafe { __library.get(#ident_str) })
181         } else {
182             quote!(__library.get(#ident_str))
183         };
184 
185         self.constructor_inits.push(if is_required {
186             quote! {
187                 let #ident = #library_get.map(|sym| *sym)?;
188             }
189         } else {
190             quote! {
191                 let #ident = #library_get.map(|sym| *sym);
192             }
193         });
194 
195         self.init_fields.push(quote! {
196             #ident
197         });
198     }
199 
push_var( &mut self, ident: Ident, ty: TokenStream, is_required: bool, )200     pub fn push_var(
201         &mut self,
202         ident: Ident,
203         ty: TokenStream,
204         is_required: bool,
205     ) {
206         let member = if is_required {
207             quote! { *mut #ty }
208         } else {
209             quote! { Result<*mut #ty, ::libloading::Error> }
210         };
211 
212         self.struct_members.push(quote! {
213             pub #ident: #member,
214         });
215 
216         let deref = if is_required {
217             quote! { self.#ident }
218         } else {
219             quote! { *self.#ident.as_ref().expect("Expected variable, got error.") }
220         };
221         self.struct_implementation.push(quote! {
222             pub unsafe fn #ident (&self) -> *mut #ty {
223                 #deref
224             }
225         });
226 
227         let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
228         self.constructor_inits.push(if is_required {
229             quote! {
230                 let #ident = __library.get::<*mut #ty>(#ident_str).map(|sym| *sym)?;
231             }
232         } else {
233             quote! {
234                 let #ident = __library.get::<*mut #ty>(#ident_str).map(|sym| *sym);
235             }
236         });
237 
238         self.init_fields.push(quote! {
239             #ident
240         });
241     }
242 }
243