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