Skip to content

Commit fc5fa9a

Browse files
Joe EllisMichael-F-Bryan
authored andcommitted
Add dynamic loading support
Closes #1541. Closes #1846. Co-authored-by: Michael-F-Bryan <[email protected]>
1 parent db3d170 commit fc5fa9a

File tree

4 files changed

+288
-3
lines changed

4 files changed

+288
-3
lines changed

src/codegen/dyngen.rs

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
use crate::ir::function::Abi;
2+
use proc_macro2::Ident;
3+
4+
/// Used to build the output tokens for dynamic bindings.
5+
pub struct DynamicItems {
6+
/// Tracks the tokens that will appears inside the library struct -- e.g.:
7+
/// ```ignore
8+
/// struct Lib {
9+
/// __library: ::libloading::Library,
10+
/// x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
11+
/// ...
12+
/// }
13+
/// ```
14+
struct_members: Vec<proc_macro2::TokenStream>,
15+
16+
/// Tracks the tokens that will appear inside the library struct's implementation, e.g.:
17+
///
18+
/// ```ignore
19+
/// impl Lib {
20+
/// ...
21+
/// pub unsafe fn foo(&self, ...) { // <- tracks these
22+
/// ...
23+
/// }
24+
/// }
25+
/// ```
26+
struct_implementation: Vec<proc_macro2::TokenStream>,
27+
28+
/// Tracks the tokens that will appear inside the struct used for checking if a symbol is
29+
/// usable, e.g.:
30+
/// ```ignore
31+
/// pub fn f(&self) -> Result<(), &'a ::libloading::Error> { // <- tracks these
32+
/// self.__library.f.as_ref().map(|_| ())
33+
/// }
34+
/// ```
35+
runtime_checks: Vec<proc_macro2::TokenStream>,
36+
37+
/// Tracks the initialization of the fields inside the `::new` constructor of the library
38+
/// struct, e.g.:
39+
/// ```ignore
40+
/// impl Lib {
41+
///
42+
/// pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
43+
/// where
44+
/// P: AsRef<::std::ffi::OsStr>,
45+
/// {
46+
/// ...
47+
/// let foo = __library.get(...) ...; // <- tracks these
48+
/// ...
49+
/// }
50+
///
51+
/// ...
52+
/// }
53+
/// ```
54+
constructor_inits: Vec<proc_macro2::TokenStream>,
55+
56+
/// Tracks the information that is passed to the library struct at the end of the `::new`
57+
/// constructor, e.g.:
58+
/// ```ignore
59+
/// impl LibFoo {
60+
/// pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
61+
/// where
62+
/// P: AsRef<::std::ffi::OsStr>,
63+
/// {
64+
/// ...
65+
/// Ok(LibFoo {
66+
/// __library: __library,
67+
/// foo,
68+
/// bar, // <- tracks these
69+
/// ...
70+
/// })
71+
/// }
72+
/// }
73+
/// ```
74+
init_fields: Vec<proc_macro2::TokenStream>,
75+
}
76+
77+
impl Default for DynamicItems {
78+
fn default() -> Self {
79+
DynamicItems {
80+
struct_members: vec![],
81+
struct_implementation: vec![],
82+
runtime_checks: vec![],
83+
constructor_inits: vec![],
84+
init_fields: vec![],
85+
}
86+
}
87+
}
88+
89+
impl DynamicItems {
90+
pub fn new() -> Self {
91+
Self::default()
92+
}
93+
94+
pub fn get_tokens(
95+
&self,
96+
lib_ident: Ident,
97+
check_struct_ident: Ident,
98+
) -> proc_macro2::TokenStream {
99+
let struct_members = &self.struct_members;
100+
let constructor_inits = &self.constructor_inits;
101+
let init_fields = &self.init_fields;
102+
let struct_implementation = &self.struct_implementation;
103+
let runtime_checks = &self.runtime_checks;
104+
quote! {
105+
extern crate libloading;
106+
107+
pub struct #lib_ident {
108+
__library: ::libloading::Library,
109+
#(#struct_members)*
110+
}
111+
112+
impl #lib_ident {
113+
pub unsafe fn new<P>(
114+
path: P
115+
) -> Result<Self, ::libloading::Error>
116+
where P: AsRef<::std::ffi::OsStr> {
117+
let __library = ::libloading::Library::new(path)?;
118+
#( #constructor_inits )*
119+
Ok(
120+
#lib_ident {
121+
__library: __library,
122+
#( #init_fields ),*
123+
}
124+
)
125+
}
126+
127+
pub fn can_call(&self) -> #check_struct_ident {
128+
#check_struct_ident { __library: self }
129+
}
130+
131+
#( #struct_implementation )*
132+
}
133+
134+
pub struct #check_struct_ident<'a> {
135+
__library: &'a #lib_ident,
136+
}
137+
138+
impl<'a> #check_struct_ident<'a> {
139+
#( #runtime_checks )*
140+
}
141+
}
142+
}
143+
144+
pub fn add_function(
145+
&mut self,
146+
ident: Ident,
147+
abi: Abi,
148+
args: Vec<proc_macro2::TokenStream>,
149+
args_identifiers: Vec<proc_macro2::TokenStream>,
150+
ret: proc_macro2::TokenStream,
151+
ret_ty: proc_macro2::TokenStream,
152+
) {
153+
assert_eq!(args.len(), args_identifiers.len());
154+
155+
self.struct_members.push(quote!{
156+
#ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
157+
});
158+
159+
self.struct_implementation.push(quote! {
160+
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
161+
let sym = self.#ident.as_ref().expect("Expected function, got error.");
162+
(sym)(#( #args_identifiers ),*)
163+
}
164+
});
165+
166+
self.runtime_checks.push(quote! {
167+
pub fn #ident (&self) -> Result<(), &'a::libloading::Error> {
168+
self.__library.#ident.as_ref().map(|_| ())
169+
}
170+
});
171+
172+
let ident_str = ident.to_string();
173+
self.constructor_inits.push(quote! {
174+
let #ident = __library.get(#ident_str.as_bytes()).map(|sym| *sym);
175+
});
176+
177+
self.init_fields.push(quote! {
178+
#ident
179+
});
180+
}
181+
}

src/codegen/mod.rs

+78-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod dyngen;
12
mod error;
23
mod helpers;
34
mod impl_debug;
@@ -10,6 +11,7 @@ pub(crate) mod bitfield_unit;
1011
#[cfg(all(test, target_endian = "little"))]
1112
mod bitfield_unit_tests;
1213

14+
use self::dyngen::DynamicItems;
1315
use self::helpers::attributes;
1416
use self::struct_layout::StructLayoutTracker;
1517

@@ -184,6 +186,7 @@ impl From<DerivableTraits> for Vec<&'static str> {
184186

185187
struct CodegenResult<'a> {
186188
items: Vec<proc_macro2::TokenStream>,
189+
dynamic_items: DynamicItems,
187190

188191
/// A monotonic counter used to add stable unique id's to stuff that doesn't
189192
/// need to be referenced by anything.
@@ -234,6 +237,7 @@ impl<'a> CodegenResult<'a> {
234237
fn new(codegen_id: &'a Cell<usize>) -> Self {
235238
CodegenResult {
236239
items: vec![],
240+
dynamic_items: DynamicItems::new(),
237241
saw_bindgen_union: false,
238242
saw_incomplete_array: false,
239243
saw_objc: false,
@@ -247,6 +251,10 @@ impl<'a> CodegenResult<'a> {
247251
}
248252
}
249253

254+
fn dynamic_items(&mut self) -> &mut DynamicItems {
255+
&mut self.dynamic_items
256+
}
257+
250258
fn saw_bindgen_union(&mut self) {
251259
self.saw_bindgen_union = true;
252260
}
@@ -3785,7 +3793,29 @@ impl CodeGenerator for Function {
37853793
pub fn #ident ( #( #args ),* ) #ret;
37863794
}
37873795
};
3788-
result.push(tokens);
3796+
3797+
// If we're doing dynamic binding generation, add to the dynamic items.
3798+
if ctx.options().dynamic_library_name.is_some() &&
3799+
self.kind() == FunctionKind::Function
3800+
{
3801+
let args_identifiers =
3802+
utils::fnsig_argument_identifiers(ctx, signature);
3803+
let return_item = ctx.resolve_item(signature.return_type());
3804+
let ret_ty = match *return_item.kind().expect_type().kind() {
3805+
TypeKind::Void => quote! {()},
3806+
_ => return_item.to_rust_ty_or_opaque(ctx, &()),
3807+
};
3808+
result.dynamic_items().add_function(
3809+
ident,
3810+
abi,
3811+
args,
3812+
args_identifiers,
3813+
ret,
3814+
ret_ty,
3815+
);
3816+
} else {
3817+
result.push(tokens);
3818+
}
37893819
}
37903820
}
37913821

@@ -4075,11 +4105,28 @@ pub(crate) fn codegen(
40754105
&(),
40764106
);
40774107

4108+
if context.options().dynamic_library_name.is_some() {
4109+
let lib_ident = context.rust_ident(
4110+
context.options().dynamic_library_name.as_ref().unwrap(),
4111+
);
4112+
let check_struct_ident = context.rust_ident(
4113+
[
4114+
"Check",
4115+
context.options().dynamic_library_name.as_ref().unwrap(),
4116+
]
4117+
.join(""),
4118+
);
4119+
let dynamic_items_tokens = result
4120+
.dynamic_items()
4121+
.get_tokens(lib_ident, check_struct_ident);
4122+
result.push(dynamic_items_tokens);
4123+
}
4124+
40784125
result.items
40794126
})
40804127
}
40814128

4082-
mod utils {
4129+
pub mod utils {
40834130
use super::{error, ToRustTyOrOpaque};
40844131
use crate::ir::context::BindgenContext;
40854132
use crate::ir::function::{Abi, FunctionSig};
@@ -4484,6 +4531,35 @@ mod utils {
44844531
args
44854532
}
44864533

4534+
pub fn fnsig_argument_identifiers(
4535+
ctx: &BindgenContext,
4536+
sig: &FunctionSig,
4537+
) -> Vec<proc_macro2::TokenStream> {
4538+
let mut unnamed_arguments = 0;
4539+
let args = sig
4540+
.argument_types()
4541+
.iter()
4542+
.map(|&(ref name, _ty)| {
4543+
let arg_name = match *name {
4544+
Some(ref name) => ctx.rust_mangle(name).into_owned(),
4545+
None => {
4546+
unnamed_arguments += 1;
4547+
format!("arg{}", unnamed_arguments)
4548+
}
4549+
};
4550+
4551+
assert!(!arg_name.is_empty());
4552+
let arg_name = ctx.rust_ident(arg_name);
4553+
4554+
quote! {
4555+
#arg_name
4556+
}
4557+
})
4558+
.collect::<Vec<_>>();
4559+
4560+
args
4561+
}
4562+
44874563
pub fn fnsig_block(
44884564
ctx: &BindgenContext,
44894565
sig: &FunctionSig,

src/lib.rs

+20
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,12 @@ impl Builder {
518518
output_vector.push(path.into());
519519
}
520520

521+
if self.options.dynamic_library_name.is_some() {
522+
let libname = self.options.dynamic_library_name.as_ref().unwrap();
523+
output_vector.push("--dynamic-loading".into());
524+
output_vector.push(libname.clone());
525+
}
526+
521527
// Add clang arguments
522528

523529
output_vector.push("--".into());
@@ -1468,6 +1474,15 @@ impl Builder {
14681474
self.options.wasm_import_module_name = Some(import_name.into());
14691475
self
14701476
}
1477+
1478+
/// Specify the dynamic library name if we are generating bindings for a shared library.
1479+
pub fn dynamic_library_name<T: Into<String>>(
1480+
mut self,
1481+
dynamic_library_name: T,
1482+
) -> Self {
1483+
self.options.dynamic_library_name = Some(dynamic_library_name.into());
1484+
self
1485+
}
14711486
}
14721487

14731488
/// Configuration options for generated bindings.
@@ -1745,6 +1760,10 @@ struct BindgenOptions {
17451760

17461761
/// Wasm import module name.
17471762
wasm_import_module_name: Option<String>,
1763+
1764+
/// The name of the dynamic library (if we are generating bindings for a shared library). If
1765+
/// this is None, no dynamic bindings are created.
1766+
dynamic_library_name: Option<String>,
17481767
}
17491768

17501769
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -1877,6 +1896,7 @@ impl Default for BindgenOptions {
18771896
no_hash_types: Default::default(),
18781897
array_pointers_in_arguments: false,
18791898
wasm_import_module_name: None,
1899+
dynamic_library_name: None,
18801900
}
18811901
}
18821902
}

src/options.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,11 @@ where
471471
.long("wasm-import-module-name")
472472
.value_name("name")
473473
.takes_value(true)
474-
.help("The name to be used in a #[link(wasm_import_module = ...)] statement")
474+
.help("The name to be used in a #[link(wasm_import_module = ...)] statement"),
475+
Arg::with_name("dynamic-loading")
476+
.long("dynamic-loading")
477+
.takes_value(true)
478+
.help("Use dynamic loading mode with the given library name."),
475479
]) // .args()
476480
.get_matches_from(args);
477481

@@ -873,6 +877,10 @@ where
873877
}
874878
}
875879

880+
if let Some(dynamic_library_name) = matches.value_of("dynamic-loading") {
881+
builder = builder.dynamic_library_name(dynamic_library_name);
882+
}
883+
876884
let verbose = matches.is_present("verbose");
877885

878886
Ok((builder, output, verbose))

0 commit comments

Comments
 (0)