diff options
Diffstat (limited to 'third_party/rust/bindgen/codegen/dyngen.rs')
-rw-r--r-- | third_party/rust/bindgen/codegen/dyngen.rs | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/third_party/rust/bindgen/codegen/dyngen.rs b/third_party/rust/bindgen/codegen/dyngen.rs new file mode 100644 index 0000000000..d8ea81175e --- /dev/null +++ b/third_party/rust/bindgen/codegen/dyngen.rs @@ -0,0 +1,201 @@ +use crate::codegen; +use crate::ir::context::BindgenContext; +use crate::ir::function::ClangAbi; +use proc_macro2::Ident; + +/// Used to build the output tokens for dynamic bindings. +#[derive(Default)] +pub struct DynamicItems { + /// Tracks the tokens that will appears inside the library struct -- e.g.: + /// ```ignore + /// struct Lib { + /// __library: ::libloading::Library, + /// pub x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these + /// ... + /// } + /// ``` + struct_members: Vec<proc_macro2::TokenStream>, + + /// Tracks the tokens that will appear inside the library struct's implementation, e.g.: + /// + /// ```ignore + /// impl Lib { + /// ... + /// pub unsafe fn foo(&self, ...) { // <- tracks these + /// ... + /// } + /// } + /// ``` + struct_implementation: Vec<proc_macro2::TokenStream>, + + /// Tracks the initialization of the fields inside the `::new` constructor of the library + /// struct, e.g.: + /// ```ignore + /// impl Lib { + /// + /// pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error> + /// where + /// P: AsRef<::std::ffi::OsStr>, + /// { + /// ... + /// let foo = __library.get(...) ...; // <- tracks these + /// ... + /// } + /// + /// ... + /// } + /// ``` + constructor_inits: Vec<proc_macro2::TokenStream>, + + /// Tracks the information that is passed to the library struct at the end of the `::new` + /// constructor, e.g.: + /// ```ignore + /// impl LibFoo { + /// pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error> + /// where + /// P: AsRef<::std::ffi::OsStr>, + /// { + /// ... + /// Ok(LibFoo { + /// __library: __library, + /// foo, + /// bar, // <- tracks these + /// ... + /// }) + /// } + /// } + /// ``` + init_fields: Vec<proc_macro2::TokenStream>, +} + +impl DynamicItems { + pub fn new() -> Self { + Self::default() + } + + pub fn get_tokens( + &self, + lib_ident: Ident, + ctx: &BindgenContext, + ) -> proc_macro2::TokenStream { + let struct_members = &self.struct_members; + let constructor_inits = &self.constructor_inits; + let init_fields = &self.init_fields; + let struct_implementation = &self.struct_implementation; + + let from_library = if ctx.options().wrap_unsafe_ops { + quote!(unsafe { Self::from_library(library) }) + } else { + quote!(Self::from_library(library)) + }; + + quote! { + extern crate libloading; + + pub struct #lib_ident { + __library: ::libloading::Library, + #(#struct_members)* + } + + impl #lib_ident { + pub unsafe fn new<P>( + path: P + ) -> Result<Self, ::libloading::Error> + where P: AsRef<::std::ffi::OsStr> { + let library = ::libloading::Library::new(path)?; + #from_library + } + + pub unsafe fn from_library<L>( + library: L + ) -> Result<Self, ::libloading::Error> + where L: Into<::libloading::Library> { + let __library = library.into(); + #( #constructor_inits )* + Ok(#lib_ident { + __library, + #( #init_fields ),* + }) + } + + #( #struct_implementation )* + } + } + } + + #[allow(clippy::too_many_arguments)] + pub(crate) fn push( + &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, + ) { + if !is_variadic { + assert_eq!(args.len(), args_identifiers.len()); + } + + let signature = quote! { unsafe extern #abi fn ( #( #args),* ) #ret }; + let member = if is_required { + signature + } else { + quote! { Result<#signature, ::libloading::Error> } + }; + + self.struct_members.push(quote! { + pub #ident: #member, + }); + + // N.B: If the signature was required, it won't be wrapped in a Result<...> + // and we can simply call it directly. + let fn_ = if is_required { + quote! { self.#ident } + } else { + quote! { self.#ident.as_ref().expect("Expected function, got error.") } + }; + let call_body = if ctx.options().wrap_unsafe_ops { + quote!(unsafe { (#fn_)(#( #args_identifiers ),*) }) + } else { + quote!((#fn_)(#( #args_identifiers ),*) ) + }; + + // We can't implement variadic functions from C easily, so we allow to + // access the function pointer so that the user can call it just fine. + if !is_variadic { + self.struct_implementation.push(quote! { + #(#attributes)* + pub unsafe fn #ident ( &self, #( #args ),* ) #ret_ty { + #call_body + } + }); + } + + // N.B: Unwrap the signature upon construction if it is required to be resolved. + let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string()); + let library_get = if ctx.options().wrap_unsafe_ops { + quote!(unsafe { __library.get(#ident_str) }) + } else { + quote!(__library.get(#ident_str)) + }; + + self.constructor_inits.push(if is_required { + quote! { + let #ident = #library_get.map(|sym| *sym)?; + } + } else { + quote! { + let #ident = #library_get.map(|sym| *sym); + } + }); + + self.init_fields.push(quote! { + #ident + }); + } +} |