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