Skip to content

Commit d71c94d

Browse files
author
Joe Ellis
committed
Add dynamic loading support
1 parent 94bce16 commit d71c94d

File tree

3 files changed

+149
-9
lines changed

3 files changed

+149
-9
lines changed

src/codegen/mod.rs

+107-8
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use crate::ir::var::Var;
4545
use proc_macro2::{self, Ident, Span};
4646
use quote::TokenStreamExt;
4747

48+
use crate::ir::item::ItemSet;
4849
use crate::{Entry, HashMap, HashSet};
4950
use std;
5051
use std::borrow::Cow;
@@ -484,11 +485,63 @@ impl CodeGenerator for Module {
484485

485486
let codegen_self = |result: &mut CodegenResult,
486487
found_any: &mut bool| {
487-
for child in self.children() {
488-
if ctx.codegen_items().contains(child) {
489-
*found_any = true;
490-
ctx.resolve_item(*child).codegen(ctx, result, &());
488+
489+
// Partition our items into functions and non-functions.
490+
let (functions, non_functions): (ItemSet, ItemSet) =
491+
self.children().iter().partition(|&child| {
492+
let resolved = ctx.resolve_item(*child);
493+
match resolved.kind() {
494+
ItemKind::Function(_) => true,
495+
_ => false,
496+
}
497+
});
498+
499+
for &child in &non_functions {
500+
if !ctx.codegen_items().contains(&child) {
501+
continue;
502+
}
503+
*found_any = true;
504+
ctx.resolve_item(child).codegen(ctx, result, &());
505+
}
506+
507+
let counter = Cell::new(0);
508+
let mut functions_result = CodegenResult::new(&counter);
509+
for &child in &functions {
510+
if !ctx.codegen_items().contains(&child) {
511+
continue;
491512
}
513+
*found_any = true;
514+
ctx.resolve_item(child).codegen(
515+
ctx,
516+
&mut functions_result,
517+
&(),
518+
);
519+
}
520+
521+
let tokens = &functions_result.items;
522+
523+
// If we're using dynamic loading, wrap the generated bindings in a struct and append
524+
// the libloading boilerplate.
525+
if ctx.options().dynamic_loading {
526+
let lib_ident = format_ident!(
527+
"{}",
528+
ctx.options().dynamic_library_name.as_ref().unwrap()
529+
);
530+
result.push(quote! {
531+
extern crate libloading;
532+
pub struct #lib_ident<'a> {
533+
#(#tokens)*
534+
}
535+
});
536+
utils::append_libloading_boilerplate(
537+
ctx,
538+
&functions,
539+
&mut *result,
540+
);
541+
} else {
542+
result.push(quote! {
543+
#(#tokens)*
544+
});
492545
}
493546

494547
if item.id() == ctx.root_module() {
@@ -3697,11 +3750,19 @@ impl CodeGenerator for Function {
36973750
});
36983751

36993752
let ident = ctx.rust_ident(canonical_name);
3700-
let tokens = quote! {
3701-
#wasm_link_attribute
3702-
extern #abi {
3753+
3754+
let tokens = if ctx.options().dynamic_loading {
3755+
quote! {
37033756
#(#attributes)*
3704-
pub fn #ident ( #( #args ),* ) #ret;
3757+
#ident: libloading::Symbol<'a, unsafe extern #abi fn ( #( #args ),* ) #ret>,
3758+
}
3759+
} else {
3760+
quote! {
3761+
#wasm_link_attribute
3762+
extern #abi {
3763+
#(#attributes)*
3764+
pub fn #ident ( #( #args ),* ) #ret;
3765+
}
37053766
}
37063767
};
37073768
result.push(tokens);
@@ -3956,6 +4017,7 @@ mod utils {
39564017
use super::{error, ToRustTyOrOpaque};
39574018
use crate::ir::context::BindgenContext;
39584019
use crate::ir::function::{Abi, FunctionSig};
4020+
use crate::ir::item::ItemSet;
39594021
use crate::ir::item::{Item, ItemCanonicalPath};
39604022
use crate::ir::ty::TypeKind;
39614023
use proc_macro2;
@@ -4453,4 +4515,41 @@ mod utils {
44534515

44544516
true
44554517
}
4518+
4519+
pub fn append_libloading_boilerplate(
4520+
ctx: &BindgenContext,
4521+
functions: &ItemSet,
4522+
result: &mut Vec<proc_macro2::TokenStream>,
4523+
) {
4524+
let mut function_definitions: Vec<proc_macro2::TokenStream> =
4525+
Vec::new();
4526+
4527+
for &function in functions {
4528+
let function_item = ctx.resolve_item(function).expect_function();
4529+
let function_name = function_item.name();
4530+
let ident = format_ident!("{}", function_name);
4531+
let function_definition = quote! {
4532+
#ident: lib.get(#function_name.as_bytes())
4533+
};
4534+
function_definitions.push(function_definition);
4535+
}
4536+
4537+
let lib_ident = format_ident!(
4538+
"{}",
4539+
ctx.options().dynamic_library_name.as_ref().unwrap()
4540+
);
4541+
let libloading_code = quote! {
4542+
impl<'a> #lib_ident<'a> {
4543+
pub fn new(lib: &libloading::Library) -> #lib_ident {
4544+
unsafe {
4545+
#lib_ident {
4546+
#(#function_definitions.unwrap()),*
4547+
}
4548+
}
4549+
}
4550+
}
4551+
};
4552+
4553+
result.push(libloading_code);
4554+
}
44564555
}

src/lib.rs

+26
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,24 @@ impl Builder {
14311431
self.options.wasm_import_module_name = Some(import_name.into());
14321432
self
14331433
}
1434+
1435+
/// Specify whether dynamic loading will be used with these bindings.
1436+
pub fn dynamic_loading(mut self, dynamic_loading: bool) -> Self {
1437+
self.options.dynamic_loading = dynamic_loading;
1438+
if self.options.dynamic_library_name.is_none() {
1439+
self.options.dynamic_library_name = Some("Lib".to_string());
1440+
}
1441+
self
1442+
}
1443+
1444+
/// Specify the dynamic library name if we are generating bindings for a shared library.
1445+
pub fn dynamic_library_name<T: Into<String>>(
1446+
mut self,
1447+
dynamic_library_name: T,
1448+
) -> Self {
1449+
self.options.dynamic_library_name = Some(dynamic_library_name.into());
1450+
self
1451+
}
14341452
}
14351453

14361454
/// Configuration options for generated bindings.
@@ -1699,6 +1717,12 @@ struct BindgenOptions {
16991717

17001718
/// Wasm import module name.
17011719
wasm_import_module_name: Option<String>,
1720+
1721+
/// Whether or not we are generating bindings for a shared library.
1722+
dynamic_loading: bool,
1723+
1724+
/// The name of the dynamic library if we are generating bindings for a shared library.
1725+
dynamic_library_name: Option<String>,
17021726
}
17031727

17041728
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -1827,6 +1851,8 @@ impl Default for BindgenOptions {
18271851
no_hash_types: Default::default(),
18281852
array_pointers_in_arguments: false,
18291853
wasm_import_module_name: None,
1854+
dynamic_loading: false,
1855+
dynamic_library_name: None,
18301856
}
18311857
}
18321858
}

src/options.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,14 @@ where
449449
Arg::with_name("wasm-import-module-name")
450450
.long("wasm-import-module-name")
451451
.value_name("name")
452+
.takes_value(true),
453+
Arg::with_name("dynamic-loading")
454+
.long("dynamic-loading")
455+
.help("Use dynamic loading mode."),
456+
Arg::with_name("dynamic-library-name")
457+
.long("dynamic-library-name")
452458
.takes_value(true)
453-
.help("The name to be used in a #[link(wasm_import_module = ...)] statement")
459+
.help("Set the name of dynamic library. Ignored if --dynamic-loading is not used.")
454460
]) // .args()
455461
.get_matches_from(args);
456462

@@ -837,6 +843,15 @@ where
837843
}
838844
}
839845

846+
if matches.is_present("dynamic-loading") {
847+
builder = builder.dynamic_loading(true);
848+
}
849+
850+
if let Some(dynamic_library_name) = matches.value_of("dynamic-library-name")
851+
{
852+
builder = builder.dynamic_library_name(dynamic_library_name);
853+
}
854+
840855
let verbose = matches.is_present("verbose");
841856

842857
Ok((builder, output, verbose))

0 commit comments

Comments
 (0)