Skip to content

Commit 01cbe44

Browse files
committed
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
1 parent 7792d63 commit 01cbe44

File tree

8 files changed

+49
-139
lines changed

8 files changed

+49
-139
lines changed

src/codegen/dyngen.rs

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub struct DynamicItems {
88
/// ```ignore
99
/// struct Lib {
1010
/// __library: ::libloading::Library,
11-
/// x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
11+
/// pub x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
1212
/// ...
1313
/// }
1414
/// ```
@@ -26,15 +26,6 @@ pub struct DynamicItems {
2626
/// ```
2727
struct_implementation: Vec<proc_macro2::TokenStream>,
2828

29-
/// Tracks the tokens that will appear inside the struct used for checking if a symbol is
30-
/// usable, e.g.:
31-
/// ```ignore
32-
/// pub fn f(&self) -> Result<(), &'a ::libloading::Error> { // <- tracks these
33-
/// self.__library.f.as_ref().map(|_| ())
34-
/// }
35-
/// ```
36-
runtime_checks: Vec<proc_macro2::TokenStream>,
37-
3829
/// Tracks the initialization of the fields inside the `::new` constructor of the library
3930
/// struct, e.g.:
4031
/// ```ignore
@@ -80,16 +71,11 @@ impl DynamicItems {
8071
Self::default()
8172
}
8273

83-
pub fn get_tokens(
84-
&self,
85-
lib_ident: Ident,
86-
check_struct_ident: Ident,
87-
) -> proc_macro2::TokenStream {
74+
pub fn get_tokens(&self, lib_ident: Ident) -> proc_macro2::TokenStream {
8875
let struct_members = &self.struct_members;
8976
let constructor_inits = &self.constructor_inits;
9077
let init_fields = &self.init_fields;
9178
let struct_implementation = &self.struct_implementation;
92-
let runtime_checks = &self.runtime_checks;
9379
quote! {
9480
extern crate libloading;
9581

@@ -107,56 +93,45 @@ impl DynamicItems {
10793
#( #constructor_inits )*
10894
Ok(
10995
#lib_ident {
110-
__library: __library,
96+
__library,
11197
#( #init_fields ),*
11298
}
11399
)
114100
}
115101

116-
pub fn can_call(&self) -> #check_struct_ident {
117-
#check_struct_ident { __library: self }
118-
}
119-
120102
#( #struct_implementation )*
121103
}
122-
123-
pub struct #check_struct_ident<'a> {
124-
__library: &'a #lib_ident,
125-
}
126-
127-
impl<'a> #check_struct_ident<'a> {
128-
#( #runtime_checks )*
129-
}
130104
}
131105
}
132106

133107
pub fn push(
134108
&mut self,
135109
ident: Ident,
136110
abi: Abi,
111+
is_variadic: bool,
137112
args: Vec<proc_macro2::TokenStream>,
138113
args_identifiers: Vec<proc_macro2::TokenStream>,
139114
ret: proc_macro2::TokenStream,
140115
ret_ty: proc_macro2::TokenStream,
141116
) {
142-
assert_eq!(args.len(), args_identifiers.len());
143-
144-
self.struct_members.push(quote!{
145-
#ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
146-
});
117+
if !is_variadic {
118+
assert_eq!(args.len(), args_identifiers.len());
119+
}
147120

148-
self.struct_implementation.push(quote! {
149-
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
150-
let sym = self.#ident.as_ref().expect("Expected function, got error.");
151-
(sym)(#( #args_identifiers ),*)
152-
}
121+
self.struct_members.push(quote! {
122+
pub #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
153123
});
154124

155-
self.runtime_checks.push(quote! {
156-
pub fn #ident (&self) -> Result<(), &'a::libloading::Error> {
157-
self.__library.#ident.as_ref().map(|_| ())
158-
}
159-
});
125+
// We can't implement variadic functions from C easily, so we allow to
126+
// access the function pointer so that the user can call it just fine.
127+
if !is_variadic {
128+
self.struct_implementation.push(quote! {
129+
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
130+
let sym = self.#ident.as_ref().expect("Expected function, got error.");
131+
(sym)(#( #args_identifiers ),*)
132+
}
133+
});
134+
}
160135

161136
let ident_str = ident.to_string();
162137
self.constructor_inits.push(quote! {

src/codegen/mod.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3808,6 +3808,7 @@ impl CodeGenerator for Function {
38083808
result.dynamic_items().push(
38093809
ident,
38103810
abi,
3811+
signature.is_variadic(),
38113812
args,
38123813
args_identifiers,
38133814
ret,
@@ -4107,11 +4108,8 @@ pub(crate) fn codegen(
41074108

41084109
if let Some(ref lib_name) = context.options().dynamic_library_name {
41094110
let lib_ident = context.rust_ident(lib_name);
4110-
let check_struct_ident =
4111-
context.rust_ident(format!("Check{}", lib_name));
4112-
let dynamic_items_tokens = result
4113-
.dynamic_items()
4114-
.get_tokens(lib_ident, check_struct_ident);
4111+
let dynamic_items_tokens =
4112+
result.dynamic_items().get_tokens(lib_ident);
41154113
result.push(dynamic_items_tokens);
41164114
}
41174115

tests/expectations/tests/dynamic_loading_simple.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@
88
extern crate libloading;
99
pub struct TestLib {
1010
__library: ::libloading::Library,
11-
foo: Result<
11+
pub foo: Result<
1212
unsafe extern "C" fn(
1313
x: ::std::os::raw::c_int,
1414
y: ::std::os::raw::c_int,
1515
) -> ::std::os::raw::c_int,
1616
::libloading::Error,
1717
>,
18-
bar: Result<
18+
pub bar: Result<
1919
unsafe extern "C" fn(
2020
x: *mut ::std::os::raw::c_void,
2121
) -> ::std::os::raw::c_int,
2222
::libloading::Error,
2323
>,
24-
baz: Result<
24+
pub baz: Result<
2525
unsafe extern "C" fn() -> ::std::os::raw::c_int,
2626
::libloading::Error,
2727
>,
@@ -36,15 +36,12 @@ impl TestLib {
3636
let bar = __library.get("bar".as_bytes()).map(|sym| *sym);
3737
let baz = __library.get("baz".as_bytes()).map(|sym| *sym);
3838
Ok(TestLib {
39-
__library: __library,
39+
__library,
4040
foo,
4141
bar,
4242
baz,
4343
})
4444
}
45-
pub fn can_call(&self) -> CheckTestLib {
46-
CheckTestLib { __library: self }
47-
}
4845
pub unsafe fn foo(
4946
&self,
5047
x: ::std::os::raw::c_int,
@@ -65,17 +62,3 @@ impl TestLib {
6562
(sym)()
6663
}
6764
}
68-
pub struct CheckTestLib<'a> {
69-
__library: &'a TestLib,
70-
}
71-
impl<'a> CheckTestLib<'a> {
72-
pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
73-
self.__library.foo.as_ref().map(|_| ())
74-
}
75-
pub fn bar(&self) -> Result<(), &'a ::libloading::Error> {
76-
self.__library.bar.as_ref().map(|_| ())
77-
}
78-
pub fn baz(&self) -> Result<(), &'a ::libloading::Error> {
79-
self.__library.baz.as_ref().map(|_| ())
80-
}
81-
}

tests/expectations/tests/dynamic_loading_template.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
extern crate libloading;
99
pub struct TestLib {
1010
__library: ::libloading::Library,
11-
foo: Result<
11+
pub foo: Result<
1212
unsafe extern "C" fn(x: ::std::os::raw::c_int) -> ::std::os::raw::c_int,
1313
::libloading::Error,
1414
>,
15-
foo1: Result<unsafe extern "C" fn(x: f32) -> f32, ::libloading::Error>,
15+
pub foo1: Result<unsafe extern "C" fn(x: f32) -> f32, ::libloading::Error>,
1616
}
1717
impl TestLib {
1818
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
@@ -23,14 +23,11 @@ impl TestLib {
2323
let foo = __library.get("foo".as_bytes()).map(|sym| *sym);
2424
let foo1 = __library.get("foo1".as_bytes()).map(|sym| *sym);
2525
Ok(TestLib {
26-
__library: __library,
26+
__library,
2727
foo,
2828
foo1,
2929
})
3030
}
31-
pub fn can_call(&self) -> CheckTestLib {
32-
CheckTestLib { __library: self }
33-
}
3431
pub unsafe fn foo(
3532
&self,
3633
x: ::std::os::raw::c_int,
@@ -43,14 +40,3 @@ impl TestLib {
4340
(sym)(x)
4441
}
4542
}
46-
pub struct CheckTestLib<'a> {
47-
__library: &'a TestLib,
48-
}
49-
impl<'a> CheckTestLib<'a> {
50-
pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
51-
self.__library.foo.as_ref().map(|_| ())
52-
}
53-
pub fn foo1(&self) -> Result<(), &'a ::libloading::Error> {
54-
self.__library.foo1.as_ref().map(|_| ())
55-
}
56-
}

tests/expectations/tests/dynamic_loading_with_blacklist.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ impl X {
5959
extern crate libloading;
6060
pub struct TestLib {
6161
__library: ::libloading::Library,
62-
foo: Result<
62+
pub foo: Result<
6363
unsafe extern "C" fn(
6464
x: *mut ::std::os::raw::c_void,
6565
) -> ::std::os::raw::c_int,
6666
::libloading::Error,
6767
>,
68-
bar: Result<
68+
pub bar: Result<
6969
unsafe extern "C" fn(
7070
x: *mut ::std::os::raw::c_void,
7171
) -> ::std::os::raw::c_int,
@@ -81,14 +81,11 @@ impl TestLib {
8181
let foo = __library.get("foo".as_bytes()).map(|sym| *sym);
8282
let bar = __library.get("bar".as_bytes()).map(|sym| *sym);
8383
Ok(TestLib {
84-
__library: __library,
84+
__library,
8585
foo,
8686
bar,
8787
})
8888
}
89-
pub fn can_call(&self) -> CheckTestLib {
90-
CheckTestLib { __library: self }
91-
}
9289
pub unsafe fn foo(
9390
&self,
9491
x: *mut ::std::os::raw::c_void,
@@ -104,14 +101,3 @@ impl TestLib {
104101
(sym)(x)
105102
}
106103
}
107-
pub struct CheckTestLib<'a> {
108-
__library: &'a TestLib,
109-
}
110-
impl<'a> CheckTestLib<'a> {
111-
pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
112-
self.__library.foo.as_ref().map(|_| ())
113-
}
114-
pub fn bar(&self) -> Result<(), &'a ::libloading::Error> {
115-
self.__library.bar.as_ref().map(|_| ())
116-
}
117-
}

tests/expectations/tests/dynamic_loading_with_class.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ impl A {
5959
extern crate libloading;
6060
pub struct TestLib {
6161
__library: ::libloading::Library,
62-
foo: Result<
62+
pub foo: Result<
6363
unsafe extern "C" fn(
6464
x: *mut ::std::os::raw::c_void,
6565
) -> ::std::os::raw::c_int,
6666
::libloading::Error,
6767
>,
68-
bar: Result<unsafe extern "C" fn(), ::libloading::Error>,
68+
pub bar: Result<unsafe extern "C" fn(), ::libloading::Error>,
6969
}
7070
impl TestLib {
7171
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
@@ -76,14 +76,11 @@ impl TestLib {
7676
let foo = __library.get("foo".as_bytes()).map(|sym| *sym);
7777
let bar = __library.get("bar".as_bytes()).map(|sym| *sym);
7878
Ok(TestLib {
79-
__library: __library,
79+
__library,
8080
foo,
8181
bar,
8282
})
8383
}
84-
pub fn can_call(&self) -> CheckTestLib {
85-
CheckTestLib { __library: self }
86-
}
8784
pub unsafe fn foo(
8885
&self,
8986
x: *mut ::std::os::raw::c_void,
@@ -96,14 +93,3 @@ impl TestLib {
9693
(sym)()
9794
}
9895
}
99-
pub struct CheckTestLib<'a> {
100-
__library: &'a TestLib,
101-
}
102-
impl<'a> CheckTestLib<'a> {
103-
pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
104-
self.__library.foo.as_ref().map(|_| ())
105-
}
106-
pub fn bar(&self) -> Result<(), &'a ::libloading::Error> {
107-
self.__library.bar.as_ref().map(|_| ())
108-
}
109-
}

tests/expectations/tests/dynamic_loading_with_whitelist.rs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,25 @@
88
extern crate libloading;
99
pub struct TestLib {
1010
__library: ::libloading::Library,
11-
foo: Result<
11+
pub foo: Result<
1212
unsafe extern "C" fn(
1313
x: *mut ::std::os::raw::c_void,
1414
) -> ::std::os::raw::c_int,
1515
::libloading::Error,
1616
>,
17-
baz: Result<
17+
pub baz: Result<
1818
unsafe extern "C" fn(
1919
x: *mut ::std::os::raw::c_void,
2020
) -> ::std::os::raw::c_int,
2121
::libloading::Error,
2222
>,
23+
pub bazz: Result<
24+
unsafe extern "C" fn(
25+
arg1: ::std::os::raw::c_int,
26+
...
27+
) -> ::std::os::raw::c_int,
28+
::libloading::Error,
29+
>,
2330
}
2431
impl TestLib {
2532
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
@@ -29,15 +36,14 @@ impl TestLib {
2936
let __library = ::libloading::Library::new(path)?;
3037
let foo = __library.get("foo".as_bytes()).map(|sym| *sym);
3138
let baz = __library.get("baz".as_bytes()).map(|sym| *sym);
39+
let bazz = __library.get("bazz".as_bytes()).map(|sym| *sym);
3240
Ok(TestLib {
33-
__library: __library,
41+
__library,
3442
foo,
3543
baz,
44+
bazz,
3645
})
3746
}
38-
pub fn can_call(&self) -> CheckTestLib {
39-
CheckTestLib { __library: self }
40-
}
4147
pub unsafe fn foo(
4248
&self,
4349
x: *mut ::std::os::raw::c_void,
@@ -53,14 +59,3 @@ impl TestLib {
5359
(sym)(x)
5460
}
5561
}
56-
pub struct CheckTestLib<'a> {
57-
__library: &'a TestLib,
58-
}
59-
impl<'a> CheckTestLib<'a> {
60-
pub fn foo(&self) -> Result<(), &'a ::libloading::Error> {
61-
self.__library.foo.as_ref().map(|_| ())
62-
}
63-
pub fn baz(&self) -> Result<(), &'a ::libloading::Error> {
64-
self.__library.baz.as_ref().map(|_| ())
65-
}
66-
}

0 commit comments

Comments
 (0)