summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2020-11-25 14:55:58 +0100
committerEmilio Cobos Álvarez <emilio@crisal.io>2020-11-25 17:25:49 +0100
commit01cbe4468358d843ef86e3e9a1e8ce2bb4fef95e (patch)
tree92004612b710a8be4aed7d67a428aaa499d9765b
parent7792d633c78d4ad39979e09a6215b656f7ec31f2 (diff)
dyngen: Handle variadic functions.
Right now trying to generate a dynamic library with variadic functions panics because we don't account for the extra `...` in the arguments. Keeping the current interface for variadic functions is tricky, as we cannot "wrap" a variadic function (VaList[1] is nightly-only). However, we don't need to. We're already exposing the libloading error, so exposing the function pointer field as public is just fine and allows consumers to call the variadic function. At that point the can_call() / CheckFoo libraries become pointless (you can just do library.function.is_ok() or such), so we can simplify the code as well removing those. [1]: https://doc.rust-lang.org/std/ffi/struct.VaList.html
-rw-r--r--src/codegen/dyngen.rs63
-rw-r--r--src/codegen/mod.rs8
-rw-r--r--tests/expectations/tests/dynamic_loading_simple.rs25
-rw-r--r--tests/expectations/tests/dynamic_loading_template.rs20
-rw-r--r--tests/expectations/tests/dynamic_loading_with_blacklist.rs20
-rw-r--r--tests/expectations/tests/dynamic_loading_with_class.rs20
-rw-r--r--tests/expectations/tests/dynamic_loading_with_whitelist.rs29
-rw-r--r--tests/headers/dynamic_loading_with_whitelist.hpp3
8 files changed, 49 insertions, 139 deletions
diff --git a/src/codegen/dyngen.rs b/src/codegen/dyngen.rs
index 4cd01c01..65872ea2 100644
--- a/src/codegen/dyngen.rs
+++ b/src/codegen/dyngen.rs
@@ -8,7 +8,7 @@ pub struct DynamicItems {
/// ```ignore
/// struct Lib {
/// __library: ::libloading::Library,
- /// x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
+ /// pub x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
/// ...
/// }
/// ```
@@ -26,15 +26,6 @@ pub struct DynamicItems {
/// ```
struct_implementation: Vec<proc_macro2::TokenStream>,
- /// 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<proc_macro2::TokenStream>,
-
/// Tracks the initialization of the fields inside the `::new` constructor of the library
/// struct, e.g.:
/// ```ignore
@@ -80,16 +71,11 @@ impl DynamicItems {
Self::default()
}
- pub fn get_tokens(
- &self,
- lib_ident: Ident,
- check_struct_ident: Ident,
- ) -> proc_macro2::TokenStream {
+ pub fn get_tokens(&self, lib_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;
@@ -107,26 +93,14 @@ impl DynamicItems {
#( #constructor_inits )*
Ok(
#lib_ident {
- __library: __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 )*
- }
}
}
@@ -134,29 +108,30 @@ impl DynamicItems {
&mut self,
ident: Ident,
abi: Abi,
+ is_variadic: bool,
args: Vec<proc_macro2::TokenStream>,
args_identifiers: Vec<proc_macro2::TokenStream>,
ret: proc_macro2::TokenStream,
ret_ty: proc_macro2::TokenStream,
) {
- assert_eq!(args.len(), args_identifiers.len());
-
- self.struct_members.push(quote!{
- #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
- });
+ if !is_variadic {
+ assert_eq!(args.len(), args_identifiers.len());
+ }
- 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_members.push(quote! {
+ pub #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
});
- self.runtime_checks.push(quote! {
- pub fn #ident (&self) -> Result<(), &'a::libloading::Error> {
- self.__library.#ident.as_ref().map(|_| ())
- }
- });
+ // 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 ),*)
+ }
+ });
+ }
let ident_str = ident.to_string();
self.constructor_inits.push(quote! {
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 64f95f4c..754c2c8b 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -3808,6 +3808,7 @@ impl CodeGenerator for Function {
result.dynamic_items().push(
ident,
abi,
+ signature.is_variadic(),
args,
args_identifiers,
ret,
@@ -4107,11 +4108,8 @@ pub(crate) fn codegen(
if let Some(ref lib_name) = context.options().dynamic_library_name {
let lib_ident = context.rust_ident(lib_name);
- let check_struct_ident =
- context.rust_ident(format!("Check{}", lib_name));
- let dynamic_items_tokens = result
- .dynamic_items()
- .get_tokens(lib_ident, check_struct_ident);
+ let dynamic_items_tokens =
+ result.dynamic_items().get_tokens(lib_ident);
result.push(dynamic_items_tokens);
}
diff --git a/tests/expectations/tests/dynamic_loading_simple.rs b/tests/expectations/tests/dynamic_loading_simple.rs
index b195a05d..b40711d8 100644
--- a/tests/expectations/tests/dynamic_loading_simple.rs
+++ b/tests/expectations/tests/dynamic_loading_simple.rs
@@ -8,20 +8,20 @@
extern crate libloading;
pub struct TestLib {
__library: ::libloading::Library,
- foo: Result<
+ pub foo: Result<
unsafe extern "C" fn(
x: ::std::os::raw::c_int,
y: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
::libloading::Error,
>,
- bar: Result<
+ pub bar: Result<
unsafe extern "C" fn(
x: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int,
::libloading::Error,
>,
- baz: Result<
+ pub baz: Result<
unsafe extern "C" fn() -> ::std::os::raw::c_int,
::libloading::Error,
>,
@@ -36,15 +36,12 @@ impl TestLib {
let bar = __library.get("bar".as_bytes()).map(|sym| *sym);
let baz = __library.get("baz".as_bytes()).map(|sym| *sym);
Ok(TestLib {
- __library: __library,
+ __library,
foo,
bar,
baz,
})
}
- pub fn can_call(&self) -> CheckTestLib {
- CheckTestLib { __library: self }
- }
pub unsafe fn foo(
&self,
x: ::std::os::raw::c_int,
@@ -65,17 +62,3 @@ impl TestLib {
(sym)()
}
}
-pub struct CheckTestLib<'a> {
- __library: &'a TestLib,
-}
-impl<'a> CheckTestLib<'a> {
- pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.foo.as_ref().map(|_| ())
- }
- pub fn bar(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.bar.as_ref().map(|_| ())
- }
- pub fn baz(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.baz.as_ref().map(|_| ())
- }
-}
diff --git a/tests/expectations/tests/dynamic_loading_template.rs b/tests/expectations/tests/dynamic_loading_template.rs
index b9fa1698..e24599de 100644
--- a/tests/expectations/tests/dynamic_loading_template.rs
+++ b/tests/expectations/tests/dynamic_loading_template.rs
@@ -8,11 +8,11 @@
extern crate libloading;
pub struct TestLib {
__library: ::libloading::Library,
- foo: Result<
+ pub foo: Result<
unsafe extern "C" fn(x: ::std::os::raw::c_int) -> ::std::os::raw::c_int,
::libloading::Error,
>,
- foo1: Result<unsafe extern "C" fn(x: f32) -> f32, ::libloading::Error>,
+ pub foo1: Result<unsafe extern "C" fn(x: f32) -> f32, ::libloading::Error>,
}
impl TestLib {
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
@@ -23,14 +23,11 @@ impl TestLib {
let foo = __library.get("foo".as_bytes()).map(|sym| *sym);
let foo1 = __library.get("foo1".as_bytes()).map(|sym| *sym);
Ok(TestLib {
- __library: __library,
+ __library,
foo,
foo1,
})
}
- pub fn can_call(&self) -> CheckTestLib {
- CheckTestLib { __library: self }
- }
pub unsafe fn foo(
&self,
x: ::std::os::raw::c_int,
@@ -43,14 +40,3 @@ impl TestLib {
(sym)(x)
}
}
-pub struct CheckTestLib<'a> {
- __library: &'a TestLib,
-}
-impl<'a> CheckTestLib<'a> {
- pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.foo.as_ref().map(|_| ())
- }
- pub fn foo1(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.foo1.as_ref().map(|_| ())
- }
-}
diff --git a/tests/expectations/tests/dynamic_loading_with_blacklist.rs b/tests/expectations/tests/dynamic_loading_with_blacklist.rs
index 259054b2..b3f6eb95 100644
--- a/tests/expectations/tests/dynamic_loading_with_blacklist.rs
+++ b/tests/expectations/tests/dynamic_loading_with_blacklist.rs
@@ -59,13 +59,13 @@ impl X {
extern crate libloading;
pub struct TestLib {
__library: ::libloading::Library,
- foo: Result<
+ pub foo: Result<
unsafe extern "C" fn(
x: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int,
::libloading::Error,
>,
- bar: Result<
+ pub bar: Result<
unsafe extern "C" fn(
x: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int,
@@ -81,14 +81,11 @@ impl TestLib {
let foo = __library.get("foo".as_bytes()).map(|sym| *sym);
let bar = __library.get("bar".as_bytes()).map(|sym| *sym);
Ok(TestLib {
- __library: __library,
+ __library,
foo,
bar,
})
}
- pub fn can_call(&self) -> CheckTestLib {
- CheckTestLib { __library: self }
- }
pub unsafe fn foo(
&self,
x: *mut ::std::os::raw::c_void,
@@ -104,14 +101,3 @@ impl TestLib {
(sym)(x)
}
}
-pub struct CheckTestLib<'a> {
- __library: &'a TestLib,
-}
-impl<'a> CheckTestLib<'a> {
- pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.foo.as_ref().map(|_| ())
- }
- pub fn bar(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.bar.as_ref().map(|_| ())
- }
-}
diff --git a/tests/expectations/tests/dynamic_loading_with_class.rs b/tests/expectations/tests/dynamic_loading_with_class.rs
index 636f01bb..6c98caef 100644
--- a/tests/expectations/tests/dynamic_loading_with_class.rs
+++ b/tests/expectations/tests/dynamic_loading_with_class.rs
@@ -59,13 +59,13 @@ impl A {
extern crate libloading;
pub struct TestLib {
__library: ::libloading::Library,
- foo: Result<
+ pub foo: Result<
unsafe extern "C" fn(
x: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int,
::libloading::Error,
>,
- bar: Result<unsafe extern "C" fn(), ::libloading::Error>,
+ pub bar: Result<unsafe extern "C" fn(), ::libloading::Error>,
}
impl TestLib {
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
@@ -76,14 +76,11 @@ impl TestLib {
let foo = __library.get("foo".as_bytes()).map(|sym| *sym);
let bar = __library.get("bar".as_bytes()).map(|sym| *sym);
Ok(TestLib {
- __library: __library,
+ __library,
foo,
bar,
})
}
- pub fn can_call(&self) -> CheckTestLib {
- CheckTestLib { __library: self }
- }
pub unsafe fn foo(
&self,
x: *mut ::std::os::raw::c_void,
@@ -96,14 +93,3 @@ impl TestLib {
(sym)()
}
}
-pub struct CheckTestLib<'a> {
- __library: &'a TestLib,
-}
-impl<'a> CheckTestLib<'a> {
- pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.foo.as_ref().map(|_| ())
- }
- pub fn bar(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.bar.as_ref().map(|_| ())
- }
-}
diff --git a/tests/expectations/tests/dynamic_loading_with_whitelist.rs b/tests/expectations/tests/dynamic_loading_with_whitelist.rs
index a1f8a2a4..0fcdf1da 100644
--- a/tests/expectations/tests/dynamic_loading_with_whitelist.rs
+++ b/tests/expectations/tests/dynamic_loading_with_whitelist.rs
@@ -8,18 +8,25 @@
extern crate libloading;
pub struct TestLib {
__library: ::libloading::Library,
- foo: Result<
+ pub foo: Result<
unsafe extern "C" fn(
x: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int,
::libloading::Error,
>,
- baz: Result<
+ pub baz: Result<
unsafe extern "C" fn(
x: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int,
::libloading::Error,
>,
+ pub bazz: Result<
+ unsafe extern "C" fn(
+ arg1: ::std::os::raw::c_int,
+ ...
+ ) -> ::std::os::raw::c_int,
+ ::libloading::Error,
+ >,
}
impl TestLib {
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
@@ -29,15 +36,14 @@ impl TestLib {
let __library = ::libloading::Library::new(path)?;
let foo = __library.get("foo".as_bytes()).map(|sym| *sym);
let baz = __library.get("baz".as_bytes()).map(|sym| *sym);
+ let bazz = __library.get("bazz".as_bytes()).map(|sym| *sym);
Ok(TestLib {
- __library: __library,
+ __library,
foo,
baz,
+ bazz,
})
}
- pub fn can_call(&self) -> CheckTestLib {
- CheckTestLib { __library: self }
- }
pub unsafe fn foo(
&self,
x: *mut ::std::os::raw::c_void,
@@ -53,14 +59,3 @@ impl TestLib {
(sym)(x)
}
}
-pub struct CheckTestLib<'a> {
- __library: &'a TestLib,
-}
-impl<'a> CheckTestLib<'a> {
- pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.foo.as_ref().map(|_| ())
- }
- pub fn baz(&self) -> Result<(), &'a ::libloading::Error> {
- self.__library.baz.as_ref().map(|_| ())
- }
-}
diff --git a/tests/headers/dynamic_loading_with_whitelist.hpp b/tests/headers/dynamic_loading_with_whitelist.hpp
index 922b5461..33bfe805 100644
--- a/tests/headers/dynamic_loading_with_whitelist.hpp
+++ b/tests/headers/dynamic_loading_with_whitelist.hpp
@@ -1,4 +1,4 @@
-// bindgen-flags: --dynamic-loading TestLib --whitelist-function baz --whitelist-function foo
+// bindgen-flags: --dynamic-loading TestLib --whitelist-function baz --whitelist-function foo --whitelist-function bazz
class X {
int _x;
@@ -13,3 +13,4 @@ class X {
int foo(void *x);
int bar(void *x);
int baz(void *x);
+int bazz(int, ...);