diff options
author | Dr. Chat <arkolbed@gmail.com> | 2021-03-26 17:12:23 -0500 |
---|---|---|
committer | Emilio Cobos Álvarez <emilio@crisal.io> | 2021-04-03 12:24:15 +0200 |
commit | 2c5a1ea6b1bd56085d726dac347a31c49c95be76 (patch) | |
tree | a15de55e4d75073c218877d105fb478d83f6bc15 /src | |
parent | e0157a648f87c4f86fe1a1e6d5bd0502b5eb265a (diff) |
Add a flag to ensure all symbols are resolved when a library is loaded
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/dyngen.rs | 65 | ||||
-rw-r--r-- | src/codegen/mod.rs | 1 | ||||
-rw-r--r-- | src/lib.rs | 18 | ||||
-rw-r--r-- | src/options.rs | 7 |
4 files changed, 77 insertions, 14 deletions
diff --git a/src/codegen/dyngen.rs b/src/codegen/dyngen.rs index 768fb02e..de9e0f98 100644 --- a/src/codegen/dyngen.rs +++ b/src/codegen/dyngen.rs @@ -5,6 +5,11 @@ use proc_macro2::Ident; /// Used to build the output tokens for dynamic bindings. #[derive(Default)] pub struct DynamicItems { + /// Tracks whether or not we contain any required symbols. + /// If so, the signature of the generated `from_library` function + /// will be altered to return a `Result<Self, ::libloading::Error>` + has_required: bool, + /// Tracks the tokens that will appears inside the library struct -- e.g.: /// ```ignore /// struct Lib { @@ -77,6 +82,11 @@ impl DynamicItems { let constructor_inits = &self.constructor_inits; let init_fields = &self.init_fields; let struct_implementation = &self.struct_implementation; + + // FIXME: Is there a better way to lay this out? Conditional in the quote + // macro? + // If we have any required symbols, we must alter the signature of `from_library` + // so that it can return a failure code. quote! { extern crate libloading; @@ -91,19 +101,19 @@ impl DynamicItems { ) -> Result<Self, ::libloading::Error> where P: AsRef<::std::ffi::OsStr> { let library = ::libloading::Library::new(path)?; - Ok(Self::from_library(library)) + Self::from_library(library) } pub unsafe fn from_library<L>( library: L - ) -> Self + ) -> Result<Self, ::libloading::Error> where L: Into<::libloading::Library> { let __library = library.into(); #( #constructor_inits )* - #lib_ident { + Ok(#lib_ident { __library, #( #init_fields ),* - } + }) } #( #struct_implementation )* @@ -116,6 +126,7 @@ impl DynamicItems { 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, @@ -125,24 +136,50 @@ impl DynamicItems { assert_eq!(args.len(), args_identifiers.len()); } - self.struct_members.push(quote! { - pub #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>, - }); + self.has_required |= is_required; + + self.struct_members.push( + if is_required { + quote! { + pub #ident: unsafe extern #abi fn ( #( #args),* ) #ret, + } + } else { + quote! { + pub #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>, + } + } + ); // 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! { - pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty { - let sym = self.#ident.as_ref().expect("Expected function, got error."); - (sym)(#( #args_identifiers ),*) + self.struct_implementation.push( + if is_required { + quote! { + pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty { + self.#ident(#( #args_identifiers ),*) + } + } + } else { + quote! { + pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty { + let sym = self.#ident.as_ref().expect("Expected function, got error."); + (sym)(#( #args_identifiers ),*) + } + } } - }); + ); } let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string()); - self.constructor_inits.push(quote! { - let #ident = __library.get(#ident_str).map(|sym| *sym); + self.constructor_inits.push(if is_required { + quote! { + let #ident = __library.get(#ident_str).map(|sym| *sym)?; + } + } else { + quote! { + let #ident = __library.get(#ident_str).map(|sym| *sym); + } }); self.init_fields.push(quote! { diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 1fba8316..95162204 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -3869,6 +3869,7 @@ impl CodeGenerator for Function { ident, abi, signature.is_variadic(), + ctx.options().dynamic_link_require_all, args, args_identifiers, ret, @@ -545,6 +545,10 @@ impl Builder { output_vector.push(name.clone()); } + if self.options.dynamic_link_require_all { + output_vector.push("--dynamic-link-require-all".into()); + } + if self.options.respect_cxx_access_specs { output_vector.push("--respect-cxx-access-specs".into()); } @@ -1567,6 +1571,14 @@ impl Builder { self } + /// Require successful linkage for all routines in a shared library. + /// This allows us to optimize function calls by being able to safely assume function pointers + /// are valid. + pub fn dynamic_link_require_all(mut self, req: bool) -> Self { + self.options.dynamic_link_require_all = req; + self + } + /// Generate bindings as `pub` only if the bound item is publically accessible by C++. pub fn respect_cxx_access_specs(mut self, doit: bool) -> Self { self.options.respect_cxx_access_specs = doit; @@ -1870,6 +1882,11 @@ struct BindgenOptions { /// this is None, no dynamic bindings are created. dynamic_library_name: Option<String>, + /// Require successful linkage for all routines in a shared library. + /// This allows us to optimize function calls by being able to safely assume function pointers + /// are valid. No effect if `dynamic_library_name` is None. + dynamic_link_require_all: bool, + /// Only make generated bindings `pub` if the items would be publically accessible /// by C++. respect_cxx_access_specs: bool, @@ -2012,6 +2029,7 @@ impl Default for BindgenOptions { array_pointers_in_arguments: false, wasm_import_module_name: None, dynamic_library_name: None, + dynamic_link_require_all: false, respect_cxx_access_specs: false, translate_enum_integer_types: false, } diff --git a/src/options.rs b/src/options.rs index 4f75424a..36267321 100644 --- a/src/options.rs +++ b/src/options.rs @@ -500,6 +500,9 @@ where .long("dynamic-loading") .takes_value(true) .help("Use dynamic loading mode with the given library name."), + Arg::with_name("dynamic-link-require-all") + .long("dynamic-link-require-all") + .help("Require successful linkage to all functions in the library."), Arg::with_name("respect-cxx-access-specs") .long("respect-cxx-access-specs") .help("Makes generated bindings `pub` only for items if the items are publically accessible in C++."), @@ -928,6 +931,10 @@ where builder = builder.dynamic_library_name(dynamic_library_name); } + if matches.is_present("dynamic-link-require-all") { + builder = builder.dynamic_link_require_all(true); + } + if matches.is_present("respect-cxx-access-specs") { builder = builder.respect_cxx_access_specs(true); } |