1 use crate::ir::function::Abi;
2 use proc_macro2::Ident;
3 
4 /// Used to build the output tokens for dynamic bindings.
5 #[derive(Default)]
6 pub struct DynamicItems {
7     /// Tracks the tokens that will appears inside the library struct -- e.g.:
8     /// ```ignore
9     /// struct Lib {
10     ///    __library: ::libloading::Library,
11     ///    pub x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
12     ///    ...
13     /// }
14     /// ```
15     struct_members: Vec<proc_macro2::TokenStream>,
16 
17     /// Tracks the tokens that will appear inside the library struct's implementation, e.g.:
18     ///
19     /// ```ignore
20     /// impl Lib {
21     ///     ...
22     ///     pub unsafe fn foo(&self, ...) { // <- tracks these
23     ///         ...
24     ///     }
25     /// }
26     /// ```
27     struct_implementation: Vec<proc_macro2::TokenStream>,
28 
29     /// Tracks the initialization of the fields inside the `::new` constructor of the library
30     /// struct, e.g.:
31     /// ```ignore
32     /// impl Lib {
33     ///
34     ///     pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
35     ///     where
36     ///         P: AsRef<::std::ffi::OsStr>,
37     ///     {
38     ///         ...
39     ///         let foo = __library.get(...) ...; // <- tracks these
40     ///         ...
41     ///     }
42     ///
43     ///     ...
44     /// }
45     /// ```
46     constructor_inits: Vec<proc_macro2::TokenStream>,
47 
48     /// Tracks the information that is passed to the library struct at the end of the `::new`
49     /// constructor, e.g.:
50     /// ```ignore
51     /// impl LibFoo {
52     ///     pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
53     ///     where
54     ///         P: AsRef<::std::ffi::OsStr>,
55     ///     {
56     ///         ...
57     ///         Ok(LibFoo {
58     ///             __library: __library,
59     ///             foo,
60     ///             bar, // <- tracks these
61     ///             ...
62     ///         })
63     ///     }
64     /// }
65     /// ```
66     init_fields: Vec<proc_macro2::TokenStream>,
67 }
68 
69 impl DynamicItems {
new() -> Self70     pub fn new() -> Self {
71         Self::default()
72     }
73 
get_tokens(&self, lib_ident: Ident) -> proc_macro2::TokenStream74     pub fn get_tokens(&self, lib_ident: Ident) -> proc_macro2::TokenStream {
75         let struct_members = &self.struct_members;
76         let constructor_inits = &self.constructor_inits;
77         let init_fields = &self.init_fields;
78         let struct_implementation = &self.struct_implementation;
79         quote! {
80             extern crate libloading;
81 
82             pub struct #lib_ident {
83                 __library: ::libloading::Library,
84                 #(#struct_members)*
85             }
86 
87             impl #lib_ident {
88                 pub unsafe fn new<P>(
89                     path: P
90                 ) -> Result<Self, ::libloading::Error>
91                 where P: AsRef<::std::ffi::OsStr> {
92                     let __library = ::libloading::Library::new(path)?;
93                     #( #constructor_inits )*
94                     Ok(
95                         #lib_ident {
96                             __library,
97                             #( #init_fields ),*
98                         }
99                     )
100                 }
101 
102                 #( #struct_implementation )*
103             }
104         }
105     }
106 
push( &mut self, ident: Ident, abi: Abi, is_variadic: bool, args: Vec<proc_macro2::TokenStream>, args_identifiers: Vec<proc_macro2::TokenStream>, ret: proc_macro2::TokenStream, ret_ty: proc_macro2::TokenStream, )107     pub fn push(
108         &mut self,
109         ident: Ident,
110         abi: Abi,
111         is_variadic: bool,
112         args: Vec<proc_macro2::TokenStream>,
113         args_identifiers: Vec<proc_macro2::TokenStream>,
114         ret: proc_macro2::TokenStream,
115         ret_ty: proc_macro2::TokenStream,
116     ) {
117         if !is_variadic {
118             assert_eq!(args.len(), args_identifiers.len());
119         }
120 
121         self.struct_members.push(quote! {
122             pub #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
123         });
124 
125         // We can't implement variadic functions from C easily, so we allow to
126         // access the function pointer so that the user can call it just fine.
127         if !is_variadic {
128             self.struct_implementation.push(quote! {
129                 pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
130                     let sym = self.#ident.as_ref().expect("Expected function, got error.");
131                     (sym)(#( #args_identifiers ),*)
132                 }
133             });
134         }
135 
136         let ident_str = ident.to_string();
137         self.constructor_inits.push(quote! {
138             let #ident = __library.get(#ident_str.as_bytes()).map(|sym| *sym);
139         });
140 
141         self.init_fields.push(quote! {
142             #ident
143         });
144     }
145 }
146