diff --git a/src/codegen/dyngen.rs b/src/codegen/dyngen.rs new file mode 100644 index 0000000000..aab6cac9ea --- /dev/null +++ b/src/codegen/dyngen.rs @@ -0,0 +1,181 @@ +use crate::ir::function::Abi; +use proc_macro2::Ident; + +/// Used to build the output tokens for dynamic bindings. +pub struct DynamicItems { + /// Tracks the tokens that will appears inside the library struct -- e.g.: + /// ```ignore + /// struct Lib { + /// __library: ::libloading::Library, + /// x: Result, // <- tracks these + /// ... + /// } + /// ``` + struct_members: Vec, + + /// 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, + + /// Tracks the tokens that will appear inside the struct used for checking if a symbol is + /// usable, e.g.: + /// ```ignore + /// pub fn f(&self) -> Result<(), &'a ::libloading::Error> { // <- tracks these + /// self.__library.f.as_ref().map(|_| ()) + /// } + /// ``` + runtime_checks: Vec, + + /// Tracks the initialization of the fields inside the `::new` constructor of the library + /// struct, e.g.: + /// ```ignore + /// impl Lib { + /// + /// pub unsafe fn new

(path: P) -> Result + /// where + /// P: AsRef<::std::ffi::OsStr>, + /// { + /// ... + /// let foo = __library.get(...) ...; // <- tracks these + /// ... + /// } + /// + /// ... + /// } + /// ``` + constructor_inits: Vec, + + /// 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

(path: P) -> Result + /// where + /// P: AsRef<::std::ffi::OsStr>, + /// { + /// ... + /// Ok(LibFoo { + /// __library: __library, + /// foo, + /// bar, // <- tracks these + /// ... + /// }) + /// } + /// } + /// ``` + init_fields: Vec, +} + +impl Default for DynamicItems { + fn default() -> Self { + DynamicItems { + struct_members: vec![], + struct_implementation: vec![], + runtime_checks: vec![], + constructor_inits: vec![], + init_fields: vec![], + } + } +} + +impl DynamicItems { + pub fn new() -> Self { + Self::default() + } + + pub fn get_tokens( + &self, + lib_ident: Ident, + check_struct_ident: Ident, + ) -> 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 runtime_checks = &self.runtime_checks; + quote! { + extern crate libloading; + + pub struct #lib_ident { + __library: ::libloading::Library, + #(#struct_members)* + } + + impl #lib_ident { + pub unsafe fn new

( + path: P + ) -> Result + where P: AsRef<::std::ffi::OsStr> { + let __library = ::libloading::Library::new(path)?; + #( #constructor_inits )* + Ok( + #lib_ident { + __library: __library, + #( #init_fields ),* + } + ) + } + + pub fn can_call(&self) -> #check_struct_ident { + #check_struct_ident { __library: self } + } + + #( #struct_implementation )* + } + + pub struct #check_struct_ident<'a> { + __library: &'a #lib_ident, + } + + impl<'a> #check_struct_ident<'a> { + #( #runtime_checks )* + } + } + } + + pub fn add_function( + &mut self, + ident: Ident, + abi: Abi, + args: Vec, + args_identifiers: Vec, + ret: proc_macro2::TokenStream, + ret_ty: proc_macro2::TokenStream, + ) { + assert_eq!(args.len(), args_identifiers.len()); + + self.struct_members.push(quote!{ + #ident: Result, + }); + + self.struct_implementation.push(quote! { + pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty { + let sym = self.#ident.as_ref().expect("Expected function, got error."); + (sym)(#( #args_identifiers ),*) + } + }); + + self.runtime_checks.push(quote! { + pub fn #ident (&self) -> Result<(), &'a::libloading::Error> { + self.__library.#ident.as_ref().map(|_| ()) + } + }); + + let ident_str = ident.to_string(); + self.constructor_inits.push(quote! { + let #ident = __library.get(#ident_str.as_bytes()).map(|sym| *sym); + }); + + self.init_fields.push(quote! { + #ident + }); + } +} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 91acf5b03a..a62979b0bf 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,3 +1,4 @@ +mod dyngen; mod error; mod helpers; mod impl_debug; @@ -10,6 +11,7 @@ pub(crate) mod bitfield_unit; #[cfg(all(test, target_endian = "little"))] mod bitfield_unit_tests; +use self::dyngen::DynamicItems; use self::helpers::attributes; use self::struct_layout::StructLayoutTracker; @@ -184,6 +186,7 @@ impl From for Vec<&'static str> { struct CodegenResult<'a> { items: Vec, + dynamic_items: DynamicItems, /// A monotonic counter used to add stable unique id's to stuff that doesn't /// need to be referenced by anything. @@ -234,6 +237,7 @@ impl<'a> CodegenResult<'a> { fn new(codegen_id: &'a Cell) -> Self { CodegenResult { items: vec![], + dynamic_items: DynamicItems::new(), saw_bindgen_union: false, saw_incomplete_array: false, saw_objc: false, @@ -247,6 +251,10 @@ impl<'a> CodegenResult<'a> { } } + fn dynamic_items(&mut self) -> &mut DynamicItems { + &mut self.dynamic_items + } + fn saw_bindgen_union(&mut self) { self.saw_bindgen_union = true; } @@ -3785,7 +3793,29 @@ impl CodeGenerator for Function { pub fn #ident ( #( #args ),* ) #ret; } }; - result.push(tokens); + + // If we're doing dynamic binding generation, add to the dynamic items. + if ctx.options().dynamic_library_name.is_some() && + self.kind() == FunctionKind::Function + { + let args_identifiers = + utils::fnsig_argument_identifiers(ctx, signature); + let return_item = ctx.resolve_item(signature.return_type()); + let ret_ty = match *return_item.kind().expect_type().kind() { + TypeKind::Void => quote! {()}, + _ => return_item.to_rust_ty_or_opaque(ctx, &()), + }; + result.dynamic_items().add_function( + ident, + abi, + args, + args_identifiers, + ret, + ret_ty, + ); + } else { + result.push(tokens); + } } } @@ -4075,11 +4105,28 @@ pub(crate) fn codegen( &(), ); + if context.options().dynamic_library_name.is_some() { + let lib_ident = context.rust_ident( + context.options().dynamic_library_name.as_ref().unwrap(), + ); + let check_struct_ident = context.rust_ident( + [ + "Check", + context.options().dynamic_library_name.as_ref().unwrap(), + ] + .join(""), + ); + let dynamic_items_tokens = result + .dynamic_items() + .get_tokens(lib_ident, check_struct_ident); + result.push(dynamic_items_tokens); + } + result.items }) } -mod utils { +pub mod utils { use super::{error, ToRustTyOrOpaque}; use crate::ir::context::BindgenContext; use crate::ir::function::{Abi, FunctionSig}; @@ -4484,6 +4531,35 @@ mod utils { args } + pub fn fnsig_argument_identifiers( + ctx: &BindgenContext, + sig: &FunctionSig, + ) -> Vec { + let mut unnamed_arguments = 0; + let args = sig + .argument_types() + .iter() + .map(|&(ref name, _ty)| { + let arg_name = match *name { + Some(ref name) => ctx.rust_mangle(name).into_owned(), + None => { + unnamed_arguments += 1; + format!("arg{}", unnamed_arguments) + } + }; + + assert!(!arg_name.is_empty()); + let arg_name = ctx.rust_ident(arg_name); + + quote! { + #arg_name + } + }) + .collect::>(); + + args + } + pub fn fnsig_block( ctx: &BindgenContext, sig: &FunctionSig, diff --git a/src/lib.rs b/src/lib.rs index 08b9381785..7aa87831f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -518,6 +518,12 @@ impl Builder { output_vector.push(path.into()); } + if self.options.dynamic_library_name.is_some() { + let libname = self.options.dynamic_library_name.as_ref().unwrap(); + output_vector.push("--dynamic-loading".into()); + output_vector.push(libname.clone()); + } + // Add clang arguments output_vector.push("--".into()); @@ -1468,6 +1474,15 @@ impl Builder { self.options.wasm_import_module_name = Some(import_name.into()); self } + + /// Specify the dynamic library name if we are generating bindings for a shared library. + pub fn dynamic_library_name>( + mut self, + dynamic_library_name: T, + ) -> Self { + self.options.dynamic_library_name = Some(dynamic_library_name.into()); + self + } } /// Configuration options for generated bindings. @@ -1745,6 +1760,10 @@ struct BindgenOptions { /// Wasm import module name. wasm_import_module_name: Option, + + /// The name of the dynamic library (if we are generating bindings for a shared library). If + /// this is None, no dynamic bindings are created. + dynamic_library_name: Option, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1877,6 +1896,7 @@ impl Default for BindgenOptions { no_hash_types: Default::default(), array_pointers_in_arguments: false, wasm_import_module_name: None, + dynamic_library_name: None, } } } diff --git a/src/options.rs b/src/options.rs index a850dbbbfe..8c85abae49 100644 --- a/src/options.rs +++ b/src/options.rs @@ -471,7 +471,11 @@ where .long("wasm-import-module-name") .value_name("name") .takes_value(true) - .help("The name to be used in a #[link(wasm_import_module = ...)] statement") + .help("The name to be used in a #[link(wasm_import_module = ...)] statement"), + Arg::with_name("dynamic-loading") + .long("dynamic-loading") + .takes_value(true) + .help("Use dynamic loading mode with the given library name."), ]) // .args() .get_matches_from(args); @@ -873,6 +877,10 @@ where } } + if let Some(dynamic_library_name) = matches.value_of("dynamic-loading") { + builder = builder.dynamic_library_name(dynamic_library_name); + } + let verbose = matches.is_present("verbose"); Ok((builder, output, verbose))