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