Skip to content

Commit 51e3702

Browse files
Joe EllisMichael-F-Bryan
Joe Ellis
andcommitted
Add dynamic loading support
Co-authored-by: Michael-F-Bryan <[email protected]>
1 parent 94bce16 commit 51e3702

15 files changed

+929
-4
lines changed

src/codegen/dyngen.rs

+306
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
use super::ToRustTyOrOpaque;
2+
use crate::codegen::utils;
3+
use crate::ir::context::{BindgenContext, ItemId};
4+
use crate::ir::function::{Abi, Function, FunctionKind};
5+
use crate::ir::item::{Item, ItemCanonicalName};
6+
use crate::ir::item_kind::ItemKind;
7+
use crate::ir::ty::TypeKind;
8+
use crate::HashSet;
9+
use proc_macro2::Ident;
10+
11+
/// This trait is similar to the CodeGenerator trait in src/codegen/mod.rs, but is instead focused
12+
/// on the generation of dynamic bindings rather than static.
13+
pub trait DynamicBindingGenerator {
14+
/// Extra information from the caller.
15+
type Extra;
16+
17+
/// Generate dynamic bindings for a particular item type.
18+
fn dyngen<'a>(
19+
&self,
20+
ctx: &BindgenContext,
21+
result: &mut DynamicBindingCodegenResult,
22+
extra: &Self::Extra,
23+
);
24+
}
25+
26+
/// Used to build the output tokens for dynamic bindings.
27+
pub struct DynamicBindingCodegenResult {
28+
/// Tracks the tokens that will appears inside the library struct -- e.g.:
29+
/// ```ignore
30+
/// struct Lib {
31+
/// __library: ::libloading::Library,
32+
/// x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
33+
/// ...
34+
/// }
35+
/// ```
36+
struct_members: Vec<proc_macro2::TokenStream>,
37+
38+
/// Tracks the tokens that will appear inside the library struct's implementation, e.g.:
39+
///
40+
/// ```ignore
41+
/// impl Lib {
42+
/// ...
43+
/// pub unsafe fn foo(&self, ...) { // <- tracks these
44+
/// ...
45+
/// }
46+
/// }
47+
/// ```
48+
struct_implementation: Vec<proc_macro2::TokenStream>,
49+
50+
/// Tracks the tokens that will appear inside the struct used for checking if a symbol is
51+
/// usable, e.g.:
52+
/// ```ignore
53+
/// pub fn f(&self) -> Result<(), &'a ::libloading::Error> { // <- tracks these
54+
/// self.__library.f.as_ref().map(|_| ())
55+
/// }
56+
/// ```
57+
runtime_checks: Vec<proc_macro2::TokenStream>,
58+
59+
/// Tracks the initialization of the fields inside the `::new` constructor of the library
60+
/// struct, e.g.:
61+
/// ```ignore
62+
/// impl Lib {
63+
///
64+
/// pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
65+
/// where
66+
/// P: AsRef<::std::ffi::OsStr>,
67+
/// {
68+
/// ...
69+
/// let foo = __library.get(...) ...; // <- tracks these
70+
/// ...
71+
/// }
72+
///
73+
/// ...
74+
/// }
75+
/// ```
76+
constructor_inits: Vec<proc_macro2::TokenStream>,
77+
78+
/// Tracks the information that is passed to the library struct at the end of the `::new`
79+
/// constructor, e.g.:
80+
/// ```ignore
81+
/// impl LibFoo {
82+
/// pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
83+
/// where
84+
/// P: AsRef<::std::ffi::OsStr>,
85+
/// {
86+
/// ...
87+
/// Ok(LibFoo {
88+
/// __library: __library,
89+
/// foo,
90+
/// bar, // <- tracks these
91+
/// ...
92+
/// })
93+
/// }
94+
/// }
95+
/// ```
96+
init_fields: Vec<proc_macro2::TokenStream>,
97+
98+
/// Keeps track of the items that we have seen.
99+
items_seen: HashSet<ItemId>,
100+
}
101+
102+
impl DynamicBindingCodegenResult {
103+
pub fn new() -> Self {
104+
DynamicBindingCodegenResult {
105+
struct_members: vec![],
106+
struct_implementation: vec![],
107+
runtime_checks: vec![],
108+
constructor_inits: vec![],
109+
init_fields: vec![],
110+
items_seen: Default::default(),
111+
}
112+
}
113+
114+
pub fn seen<Id: Into<ItemId>>(&self, item: Id) -> bool {
115+
self.items_seen.contains(&item.into())
116+
}
117+
118+
pub fn set_seen<Id: Into<ItemId>>(&mut self, item: Id) {
119+
self.items_seen.insert(item.into());
120+
}
121+
122+
pub fn get_tokens(
123+
&self,
124+
lib_ident: Ident,
125+
check_struct_ident: Ident,
126+
) -> proc_macro2::TokenStream {
127+
let struct_members = &self.struct_members;
128+
let constructor_inits = &self.constructor_inits;
129+
let init_fields = &self.init_fields;
130+
let struct_implementation = &self.struct_implementation;
131+
let runtime_checks = &self.runtime_checks;
132+
quote! {
133+
extern crate libloading;
134+
135+
pub struct #lib_ident {
136+
__library: ::libloading::Library,
137+
#(#struct_members)*
138+
}
139+
140+
impl #lib_ident {
141+
pub unsafe fn new<P>(
142+
path: P
143+
) -> Result<Self, ::libloading::Error>
144+
where P: AsRef<::std::ffi::OsStr> {
145+
let __library = ::libloading::Library::new(path)?;
146+
#( #constructor_inits )*
147+
Ok(
148+
#lib_ident {
149+
__library: __library,
150+
#( #init_fields ),*
151+
}
152+
)
153+
}
154+
155+
pub fn can_call(&self) -> #check_struct_ident {
156+
#check_struct_ident { __library: self }
157+
}
158+
159+
#( #struct_implementation )*
160+
}
161+
162+
pub struct #check_struct_ident<'a> {
163+
__library: &'a #lib_ident,
164+
}
165+
166+
impl<'a> #check_struct_ident<'a> {
167+
#( #runtime_checks )*
168+
}
169+
}
170+
}
171+
172+
pub fn add_function(
173+
&mut self,
174+
ident: Ident,
175+
abi: Abi,
176+
args: Vec<proc_macro2::TokenStream>,
177+
args_identifiers: Vec<proc_macro2::TokenStream>,
178+
ret: proc_macro2::TokenStream,
179+
ret_ty: proc_macro2::TokenStream,
180+
) {
181+
self.struct_members.push(quote!{
182+
#ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
183+
});
184+
185+
self.struct_implementation.push(quote! {
186+
pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
187+
let sym = self.#ident.as_ref().expect("Expected function, got error.");
188+
(sym)(#( #args_identifiers ),*)
189+
}
190+
});
191+
192+
self.runtime_checks.push(quote! {
193+
pub fn #ident (&self) -> Result<(), &'a::libloading::Error> {
194+
self.__library.#ident.as_ref().map(|_| ())
195+
}
196+
});
197+
198+
let ident_str = ident.to_string();
199+
self.constructor_inits.push(quote! {
200+
let #ident = __library.get(#ident_str.as_bytes()).map(|sym| *sym);
201+
});
202+
203+
self.init_fields.push(quote! {
204+
#ident
205+
});
206+
}
207+
}
208+
209+
impl DynamicBindingGenerator for Item {
210+
type Extra = ();
211+
212+
fn dyngen<'a>(
213+
&self,
214+
ctx: &BindgenContext,
215+
result: &mut DynamicBindingCodegenResult,
216+
_extra: &(),
217+
) {
218+
assert!(self.is_dynamic(ctx));
219+
if !self.is_enabled_for_codegen(ctx) {
220+
return;
221+
}
222+
223+
// If this item is blacklisted, or we've already seen it, nothing to do.
224+
if self.is_blacklisted(ctx) || result.seen(self.id()) {
225+
debug!(
226+
"<Item as DynamicBindingGenerator>::dyngen: Ignoring hidden or seen: \
227+
self = {:?}",
228+
self
229+
);
230+
return;
231+
}
232+
233+
debug!(
234+
"<Item as DynamicBindingGenerator>::dyngen: self = {:?}",
235+
self
236+
);
237+
238+
if !ctx.codegen_items().contains(&self.id()) {
239+
warn!("Found non-whitelisted item in dynamic binding generation: {:?}", self);
240+
}
241+
242+
result.set_seen(self.id());
243+
244+
match *self.kind() {
245+
ItemKind::Function(ref fun) => {
246+
assert!(fun.kind() == FunctionKind::Function);
247+
fun.dyngen(ctx, result, self);
248+
}
249+
_ => panic!(
250+
"Unexpected item type when doing dynamic bindings generation."
251+
),
252+
}
253+
}
254+
}
255+
256+
impl DynamicBindingGenerator for Function {
257+
type Extra = Item;
258+
259+
fn dyngen<'a>(
260+
&self,
261+
ctx: &BindgenContext,
262+
result: &mut DynamicBindingCodegenResult,
263+
item: &Item,
264+
) {
265+
let signature_item = ctx.resolve_item(self.signature());
266+
let signature = signature_item.kind().expect_type().canonical_type(ctx);
267+
let signature = match *signature.kind() {
268+
TypeKind::Function(ref sig) => sig,
269+
_ => panic!("Signature kind is not a Function: {:?}", signature),
270+
};
271+
272+
let canonical_name = item.canonical_name(ctx);
273+
let abi = match signature.abi() {
274+
Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => {
275+
warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target");
276+
return;
277+
}
278+
Abi::Win64 if signature.is_variadic() => {
279+
warn!("Skipping variadic function with Win64 ABI that isn't supported");
280+
return;
281+
}
282+
Abi::Unknown(unknown_abi) => {
283+
panic!(
284+
"Invalid or unknown abi {:?} for function {:?} ({:?})",
285+
unknown_abi, canonical_name, self
286+
);
287+
}
288+
abi => abi,
289+
};
290+
291+
let args = utils::fnsig_arguments(ctx, signature);
292+
let args_identifiers =
293+
utils::fnsig_argument_identifiers(ctx, signature);
294+
let ret = utils::fnsig_return_ty(ctx, signature);
295+
296+
let ident = ctx.rust_ident(&canonical_name);
297+
298+
let return_item = ctx.resolve_item(signature.return_type());
299+
let ret_ty = match *return_item.kind().expect_type().kind() {
300+
TypeKind::Void => quote! {()},
301+
_ => return_item.to_rust_ty_or_opaque(ctx, &()),
302+
};
303+
304+
result.add_function(ident, abi, args, args_identifiers, ret, ret_ty);
305+
}
306+
}

0 commit comments

Comments
 (0)