From d71c94dad8411c86e0aa40bcd5702a4fd613f0da Mon Sep 17 00:00:00 2001 From: Joe Ellis Date: Mon, 27 Jul 2020 10:26:38 +0100 Subject: [PATCH] Add dynamic loading support --- src/codegen/mod.rs | 115 +++++++++++++++++++++++++++++++++++++++++---- src/lib.rs | 26 ++++++++++ src/options.rs | 17 ++++++- 3 files changed, 149 insertions(+), 9 deletions(-) diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index c68eb6da95..519f3f2f42 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -45,6 +45,7 @@ use crate::ir::var::Var; use proc_macro2::{self, Ident, Span}; use quote::TokenStreamExt; +use crate::ir::item::ItemSet; use crate::{Entry, HashMap, HashSet}; use std; use std::borrow::Cow; @@ -484,11 +485,63 @@ impl CodeGenerator for Module { let codegen_self = |result: &mut CodegenResult, found_any: &mut bool| { - for child in self.children() { - if ctx.codegen_items().contains(child) { - *found_any = true; - ctx.resolve_item(*child).codegen(ctx, result, &()); + + // Partition our items into functions and non-functions. + let (functions, non_functions): (ItemSet, ItemSet) = + self.children().iter().partition(|&child| { + let resolved = ctx.resolve_item(*child); + match resolved.kind() { + ItemKind::Function(_) => true, + _ => false, + } + }); + + for &child in &non_functions { + if !ctx.codegen_items().contains(&child) { + continue; + } + *found_any = true; + ctx.resolve_item(child).codegen(ctx, result, &()); + } + + let counter = Cell::new(0); + let mut functions_result = CodegenResult::new(&counter); + for &child in &functions { + if !ctx.codegen_items().contains(&child) { + continue; } + *found_any = true; + ctx.resolve_item(child).codegen( + ctx, + &mut functions_result, + &(), + ); + } + + let tokens = &functions_result.items; + + // If we're using dynamic loading, wrap the generated bindings in a struct and append + // the libloading boilerplate. + if ctx.options().dynamic_loading { + let lib_ident = format_ident!( + "{}", + ctx.options().dynamic_library_name.as_ref().unwrap() + ); + result.push(quote! { + extern crate libloading; + pub struct #lib_ident<'a> { + #(#tokens)* + } + }); + utils::append_libloading_boilerplate( + ctx, + &functions, + &mut *result, + ); + } else { + result.push(quote! { + #(#tokens)* + }); } if item.id() == ctx.root_module() { @@ -3697,11 +3750,19 @@ impl CodeGenerator for Function { }); let ident = ctx.rust_ident(canonical_name); - let tokens = quote! { - #wasm_link_attribute - extern #abi { + + let tokens = if ctx.options().dynamic_loading { + quote! { #(#attributes)* - pub fn #ident ( #( #args ),* ) #ret; + #ident: libloading::Symbol<'a, unsafe extern #abi fn ( #( #args ),* ) #ret>, + } + } else { + quote! { + #wasm_link_attribute + extern #abi { + #(#attributes)* + pub fn #ident ( #( #args ),* ) #ret; + } } }; result.push(tokens); @@ -3956,6 +4017,7 @@ mod utils { use super::{error, ToRustTyOrOpaque}; use crate::ir::context::BindgenContext; use crate::ir::function::{Abi, FunctionSig}; + use crate::ir::item::ItemSet; use crate::ir::item::{Item, ItemCanonicalPath}; use crate::ir::ty::TypeKind; use proc_macro2; @@ -4453,4 +4515,41 @@ mod utils { true } + + pub fn append_libloading_boilerplate( + ctx: &BindgenContext, + functions: &ItemSet, + result: &mut Vec, + ) { + let mut function_definitions: Vec = + Vec::new(); + + for &function in functions { + let function_item = ctx.resolve_item(function).expect_function(); + let function_name = function_item.name(); + let ident = format_ident!("{}", function_name); + let function_definition = quote! { + #ident: lib.get(#function_name.as_bytes()) + }; + function_definitions.push(function_definition); + } + + let lib_ident = format_ident!( + "{}", + ctx.options().dynamic_library_name.as_ref().unwrap() + ); + let libloading_code = quote! { + impl<'a> #lib_ident<'a> { + pub fn new(lib: &libloading::Library) -> #lib_ident { + unsafe { + #lib_ident { + #(#function_definitions.unwrap()),* + } + } + } + } + }; + + result.push(libloading_code); + } } diff --git a/src/lib.rs b/src/lib.rs index 5a86364e6d..e5441122d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1431,6 +1431,24 @@ impl Builder { self.options.wasm_import_module_name = Some(import_name.into()); self } + + /// Specify whether dynamic loading will be used with these bindings. + pub fn dynamic_loading(mut self, dynamic_loading: bool) -> Self { + self.options.dynamic_loading = dynamic_loading; + if self.options.dynamic_library_name.is_none() { + self.options.dynamic_library_name = Some("Lib".to_string()); + } + self + } + + /// Specify the dynamic library name if we are generating bindings for a shared library. + pub fn dynamic_library_name>( + mut self, + dynamic_library_name: T, + ) -> Self { + self.options.dynamic_library_name = Some(dynamic_library_name.into()); + self + } } /// Configuration options for generated bindings. @@ -1699,6 +1717,12 @@ struct BindgenOptions { /// Wasm import module name. wasm_import_module_name: Option, + + /// Whether or not we are generating bindings for a shared library. + dynamic_loading: bool, + + /// The name of the dynamic library if we are generating bindings for a shared library. + dynamic_library_name: Option, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1827,6 +1851,8 @@ impl Default for BindgenOptions { no_hash_types: Default::default(), array_pointers_in_arguments: false, wasm_import_module_name: None, + dynamic_loading: false, + dynamic_library_name: None, } } } diff --git a/src/options.rs b/src/options.rs index 0add7b4cbe..6fd9828fc9 100644 --- a/src/options.rs +++ b/src/options.rs @@ -449,8 +449,14 @@ where Arg::with_name("wasm-import-module-name") .long("wasm-import-module-name") .value_name("name") + .takes_value(true), + Arg::with_name("dynamic-loading") + .long("dynamic-loading") + .help("Use dynamic loading mode."), + Arg::with_name("dynamic-library-name") + .long("dynamic-library-name") .takes_value(true) - .help("The name to be used in a #[link(wasm_import_module = ...)] statement") + .help("Set the name of dynamic library. Ignored if --dynamic-loading is not used.") ]) // .args() .get_matches_from(args); @@ -837,6 +843,15 @@ where } } + if matches.is_present("dynamic-loading") { + builder = builder.dynamic_loading(true); + } + + if let Some(dynamic_library_name) = matches.value_of("dynamic-library-name") + { + builder = builder.dynamic_library_name(dynamic_library_name); + } + let verbose = matches.is_present("verbose"); Ok((builder, output, verbose))