Skip to content

Commit 5c5c155

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

File tree

3 files changed

+144
-9
lines changed

3 files changed

+144
-9
lines changed

src/codegen/mod.rs

+106-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,62 @@ 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+
pub struct #lib_ident<'a> {
532+
#(#tokens)*
533+
}
534+
});
535+
utils::append_libloading_boilerplate(
536+
ctx,
537+
&functions,
538+
&mut *result,
539+
);
540+
} else {
541+
result.push(quote! {
542+
#(#tokens)*
543+
});
492544
}
493545

494546
if item.id() == ctx.root_module() {
@@ -3697,11 +3749,19 @@ impl CodeGenerator for Function {
36973749
});
36983750

36993751
let ident = ctx.rust_ident(canonical_name);
3700-
let tokens = quote! {
3701-
#wasm_link_attribute
3702-
extern #abi {
3752+
3753+
let tokens = if ctx.options().dynamic_loading {
3754+
quote! {
37033755
#(#attributes)*
3704-
pub fn #ident ( #( #args ),* ) #ret;
3756+
#ident: libloading::Symbol<'a, unsafe extern #abi fn ( #( #args ),* ) #ret>,
3757+
}
3758+
} else {
3759+
quote! {
3760+
#wasm_link_attribute
3761+
extern #abi {
3762+
#(#attributes)*
3763+
pub fn #ident ( #( #args ),* ) #ret;
3764+
}
37053765
}
37063766
};
37073767
result.push(tokens);
@@ -3956,6 +4016,7 @@ mod utils {
39564016
use super::{error, ToRustTyOrOpaque};
39574017
use crate::ir::context::BindgenContext;
39584018
use crate::ir::function::{Abi, FunctionSig};
4019+
use crate::ir::item::ItemSet;
39594020
use crate::ir::item::{Item, ItemCanonicalPath};
39604021
use crate::ir::ty::TypeKind;
39614022
use proc_macro2;
@@ -4453,4 +4514,41 @@ mod utils {
44534514

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

src/lib.rs

+23
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,21 @@ 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>>(mut self, dynamic_library_name: T) -> Self {
1446+
self.options.dynamic_library_name = Some(dynamic_library_name.into());
1447+
self
1448+
}
14341449
}
14351450

14361451
/// Configuration options for generated bindings.
@@ -1699,6 +1714,12 @@ struct BindgenOptions {
16991714

17001715
/// Wasm import module name.
17011716
wasm_import_module_name: Option<String>,
1717+
1718+
/// Whether or not we are generating bindings for a shared library.
1719+
dynamic_loading: bool,
1720+
1721+
/// The name of the dynamic library if we are generating bindings for a shared library.
1722+
dynamic_library_name: Option<String>,
17021723
}
17031724

17041725
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -1827,6 +1848,8 @@ impl Default for BindgenOptions {
18271848
no_hash_types: Default::default(),
18281849
array_pointers_in_arguments: false,
18291850
wasm_import_module_name: None,
1851+
dynamic_loading: false,
1852+
dynamic_library_name: None,
18301853
}
18311854
}
18321855
}

src/options.rs

+15-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,14 @@ 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+
builder = builder.dynamic_library_name(dynamic_library_name);
852+
}
853+
840854
let verbose = matches.is_present("verbose");
841855

842856
Ok((builder, output, verbose))

0 commit comments

Comments
 (0)