Implement the Error type

This allows us to:

a) remove the panics in this library and give the control over what to
do to the user;
b) expose the error conditions to the user better, even if just slightly;
c) implement the modern error handling currently implemented in many of
the modern Rust libraries.
This commit is contained in:
Simonas Kazlauskas
2020-04-05 22:06:04 +03:00
parent 95e4bdbe81
commit 32368c0de5
8 changed files with 255 additions and 134 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
rust_toolchain: [nightly, stable, 1.36.0]
rust_toolchain: [nightly, stable, 1.40.0]
os: [ubuntu-latest, windows-latest, macOS-latest]
timeout-minutes: 20
steps:
+7 -1
View File
@@ -4,10 +4,15 @@
///
/// * Introduced a new method [`os::unix::Library::get_singlethreaded`];
/// * Added (untested) support for building when targetting Redox and Fuchsia;
/// * The APIs exposed by this library no longer panic and instead return an `Err` when it used
/// to panic.
///
/// ## Breaking changes
///
/// * Minimum required (stable) version of Rust to build this library is now 1.36.0;
/// * Minimum required (stable) version of Rust to build this library is now 1.40.0;
/// * This crate now implements a custom [`Error`] type and all APIs now return this type rather
/// than returning the `std::io::Error`;
/// * `libloading::Result` has been removed;
/// * Removed the dependency on the C compiler to build this library on UNIX-like platforms.
/// `libloading` used to utilize a snippet written in C to work-around the unlikely possibility
/// of the target having a thread-unsafe implementation of the `dlerror` function. The effect of
@@ -21,6 +26,7 @@
///
/// [`Library::get`]: crate::Library::get
/// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded
/// [`Error`]: crate::Error
pub mod r0_6_0 {}
+114
View File
@@ -0,0 +1,114 @@
use std::ffi::CString;
pub struct DlDescription(pub(crate) CString);
impl std::fmt::Debug for DlDescription {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.0, f)
}
}
pub struct WindowsError(pub(crate) std::io::Error);
impl std::fmt::Debug for WindowsError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.0, f)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// The `dlopen` call failed.
DlOpen { desc: DlDescription },
/// The `dlopen` call failed and system did not report an error.
DlOpenUnknown,
/// The `dlsym` call failed.
DlSym { desc: DlDescription },
/// The `dlsym` call failed and system did not report an error.
DlSymUnknown,
/// The `dlclose` call failed.
DlClose { desc: DlDescription },
/// The `dlclose` call failed and system did not report an error.
DlCloseUnknown,
/// The `LoadLibraryW` call failed.
LoadLibraryW { source: WindowsError },
/// The `LoadLibraryW` call failed and system did not report an error.
LoadLibraryWUnknown,
/// The `GetProcAddress` call failed.
GetProcAddress { source: WindowsError },
/// The `GetProcAddressUnknown` call failed and system did not report an error.
GetProcAddressUnknown,
/// The `FreeLibrary` call failed.
FreeLibrary { source: WindowsError },
/// The `FreeLibrary` call failed and system did not report an error.
FreeLibraryUnknown,
/// The requested type cannot possibly work.
IncompatibleSize,
/// Could not create a new CString.
CreateCString { source: std::ffi::NulError },
/// Could not create a new CString from bytes with trailing null.
CreateCStringWithTrailing { source: std::ffi::FromBytesWithNulError },
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use Error::*;
match *self {
CreateCString { ref source } => Some(source),
CreateCStringWithTrailing { ref source } => Some(source),
LoadLibraryW { ref source } => Some(&source.0),
GetProcAddress { ref source } => Some(&source.0),
FreeLibrary { ref source } => Some(&source.0),
_ => None,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use Error::*;
match *self {
DlOpen { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
DlOpenUnknown => write!(f, "dlopen failed, but system did not report the error"),
DlSym { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
DlSymUnknown => write!(f, "dlsym failed, but system did not report the error"),
DlClose { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
DlCloseUnknown => write!(f, "dlclose failed, but system did not report the error"),
LoadLibraryW { .. } => write!(f, "LoadLibraryW failed"),
LoadLibraryWUnknown =>
write!(f, "LoadLibraryW failed, but system did not report the error"),
GetProcAddress { .. } => write!(f, "GetProcAddress failed"),
GetProcAddressUnknown =>
write!(f, "GetProcAddress failed, but system did not report the error"),
FreeLibrary { .. } => write!(f, "FreeLibrary failed"),
FreeLibraryUnknown =>
write!(f, "FreeLibrary failed, but system did not report the error"),
CreateCString { .. } => write!(f, "could not create a C string from bytes"),
CreateCStringWithTrailing { .. } =>
write!(f, "could not create a C string from bytes with trailing null"),
IncompatibleSize => write!(f, "requested type cannot possibly work"),
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn error_send() {
fn assert_send<T: Send>() {}
assert_send::<super::Error>();
}
#[test]
fn error_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<super::Error>();
}
#[test]
fn error_display() {
fn assert_display<T: std::fmt::Display>() {}
assert_display::<super::Error>();
}
}
+17 -5
View File
@@ -25,7 +25,7 @@
//! ```no_run
//! extern crate libloading as lib;
//!
//! fn call_dynamic() -> lib::Result<u32> {
//! fn call_dynamic() -> Result<u32, Box<dyn std::error::Error>> {
//! let lib = lib::Library::new("/path/to/liblibrary.so")?;
//! unsafe {
//! let func: lib::Symbol<unsafe extern fn() -> u32> = lib.get(b"my_func")?;
@@ -45,12 +45,12 @@ use std::marker;
use self::os::unix as imp;
#[cfg(windows)]
use self::os::windows as imp;
pub use self::error::Error;
pub mod os;
pub mod changelog;
mod util;
pub type Result<T> = ::std::io::Result<T>;
mod error;
/// A loaded dynamic library.
pub struct Library(imp::Library);
@@ -114,7 +114,7 @@ impl Library {
/// let _ = Library::new("../awesome.module").unwrap();
/// let _ = Library::new("libsomelib.so.1").unwrap();
/// ```
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library> {
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, Error> {
imp::Library::new(filename).map(From::from)
}
@@ -174,9 +174,21 @@ impl Library {
/// **awesome_variable = 42.0;
/// };
/// ```
pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>> {
pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>, Error> {
self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
}
/// Unload the library.
///
/// This method might be a no-op, depending on the flags with which the `Library` was opened,
/// what library was opened or other platform specifics.
///
/// You only need to call this if you are interested in handling any errors that may arise when
/// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
/// library and ignore the errors were they arise.
pub fn close(self) -> Result<(), Error> {
self.0.close()
}
}
impl fmt::Debug for Library {
+46 -34
View File
@@ -1,7 +1,7 @@
use util::{ensure_compatible_types, cstr_cow_from_bytes};
use std::ffi::{CStr, OsStr};
use std::{fmt, io, marker, mem, ptr};
use std::{fmt, marker, mem, ptr};
use std::os::raw;
use std::os::unix::ffi::OsStrExt;
@@ -14,7 +14,8 @@ use std::os::unix::ffi::OsStrExt;
// In practice (as of 2020-04-01) most of the widely used targets use a thread-local for error
// state and have been doing so for a long time. Regardless the comments in this function shall
// remain as a documentation for the future generations.
fn with_dlerror<T, F>(closure: F) -> Result<T, Option<io::Error>>
fn with_dlerror<T, F>(wrap: fn(crate::error::DlDescription) -> crate::Error, closure: F)
-> Result<T, Option<crate::Error>>
where F: FnOnce() -> Option<T> {
// We used to guard all uses of dl* functions with our own mutex. This made them safe to use in
// MT programs provided the only way a program used dl* was via this library. However, it also
@@ -52,8 +53,8 @@ where F: FnOnce() -> Option<T> {
// ownership over the message?
// TODO: should do locale-aware conversion here. OTOH Rust doesnt seem to work well in
// any system that uses non-utf8 locale, so I doubt theres a problem here.
let message = CStr::from_ptr(error).to_string_lossy().into_owned();
Some(io::Error::new(io::ErrorKind::Other, message))
let message = CStr::from_ptr(error).into();
Some(wrap(crate::error::DlDescription(message)))
// Since we do a copy of the error string above, maybe we should call dlerror again to
// let libdl know it may free its copy of the string now?
}
@@ -91,7 +92,7 @@ impl Library {
///
/// Corresponds to `dlopen(filename, RTLD_NOW)`.
#[inline]
pub fn new<P: AsRef<OsStr>>(filename: P) -> ::Result<Library> {
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
Library::open(Some(filename), RTLD_NOW)
}
@@ -103,7 +104,7 @@ impl Library {
/// Corresponds to `dlopen(NULL, RTLD_NOW)`.
#[inline]
pub fn this() -> Library {
Library::open(None::<&OsStr>, RTLD_NOW).unwrap()
Library::open(None::<&OsStr>, RTLD_NOW).expect("this should never fail")
}
/// Find and load a shared library (module).
@@ -114,13 +115,13 @@ impl Library {
/// If the `filename` is None, null pointer is passed to `dlopen`.
///
/// Corresponds to `dlopen(filename, flags)`.
pub fn open<P>(filename: Option<P>, flags: raw::c_int) -> ::Result<Library>
pub fn open<P>(filename: Option<P>, flags: raw::c_int) -> Result<Library, crate::Error>
where P: AsRef<OsStr> {
let filename = match filename {
None => None,
Some(ref f) => Some(cstr_cow_from_bytes(f.as_ref().as_bytes())?),
};
with_dlerror(move || {
with_dlerror(|desc| crate::Error::DlOpen { desc }, move || {
let result = unsafe {
let r = dlopen(match filename {
None => ptr::null(),
@@ -137,18 +138,13 @@ impl Library {
handle: result
})
}
}).map_err(|e| e.unwrap_or_else(||
io::Error::new(
io::ErrorKind::Other,
"dlopen returned a null and dlerror reported no error"
)
))
}).map_err(|e| e.unwrap_or(crate::Error::DlOpenUnknown))
}
unsafe fn get_impl<T, F>(&self, symbol: &[u8], on_null: F) -> ::Result<Symbol<T>>
where F: FnOnce() -> ::Result<Symbol<T>>
unsafe fn get_impl<T, F>(&self, symbol: &[u8], on_null: F) -> Result<Symbol<T>, crate::Error>
where F: FnOnce() -> Result<Symbol<T>, crate::Error>
{
ensure_compatible_types::<T, *mut raw::c_void>();
ensure_compatible_types::<T, *mut raw::c_void>()?;
let symbol = cstr_cow_from_bytes(symbol)?;
// `dlsym` may return nullptr in two cases: when a symbol genuinely points to a null
// pointer or the symbol cannot be found. In order to detect this case a double dlerror
@@ -156,7 +152,7 @@ impl Library {
//
// We try to leave as little space as possible for this to occur, but we cant exactly
// fully prevent it.
match with_dlerror(|| {
match with_dlerror(|desc| crate::Error::DlSym { desc }, || {
dlerror();
let symbol = dlsym(self.handle, symbol.as_ptr());
if symbol.is_null() {
@@ -202,17 +198,12 @@ impl Library {
/// pointer without it being an error. If loading a null pointer is something you care about,
/// consider using the [`Library::get_singlethreaded`] call.
#[inline(always)]
pub unsafe fn get<T>(&self, symbol: &[u8]) -> ::Result<Symbol<T>> {
pub unsafe fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
#[cfg(mtsafe_dlerror)]
{ return self.get_singlethreaded(symbol); }
#[cfg(not(mtsafe_dlerror))]
{
return self.get_impl(symbol, || {
Err(io::Error::new(
io::ErrorKind::Other,
"dlsym returned a null and MT-unsafe dlerror reported no error"
))
});
return self.get_impl(symbol, || Err(crate::Error::DlSymUnknown));
}
}
@@ -239,7 +230,7 @@ impl Library {
/// OS X uses some sort of lazy initialization scheme, which makes loading TLS variables
/// impossible. Using a TLS variable loaded this way on OS X is undefined behaviour.
#[inline(always)]
pub unsafe fn get_singlethreaded<T>(&self, symbol: &[u8]) -> ::Result<Symbol<T>> {
pub unsafe fn get_singlethreaded<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
self.get_impl(symbol, || Ok(Symbol {
pointer: ptr::null_mut(),
pd: marker::PhantomData
@@ -268,15 +259,33 @@ impl Library {
handle: handle
}
}
/// Unload the library.
///
/// This method might be a no-op, depending on the flags with which the `Library` was opened,
/// what library was opened or other platform specifics.
///
/// You only need to call this if you are interested in handling any errors that may arise when
/// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
/// library and ignore the errors were they arise.
pub fn close(self) -> Result<(), crate::Error> {
let result = with_dlerror(|desc| crate::Error::DlClose { desc }, || {
if unsafe { dlclose(self.handle) } == 0 {
Some(())
} else {
None
}
}).map_err(|e| e.unwrap_or(crate::Error::DlCloseUnknown));
std::mem::forget(self);
result
}
}
impl Drop for Library {
fn drop(&mut self) {
with_dlerror(|| if unsafe { dlclose(self.handle) } == 0 {
Some(())
} else {
None
}).unwrap();
unsafe {
dlclose(self.handle);
}
}
}
@@ -382,7 +391,10 @@ struct DlInfo {
dli_saddr: *mut raw::c_void
}
#[test]
fn this() {
Library::this();
#[cfg(test)]
mod tests {
#[test]
fn this() {
super::Library::this();
}
}
+49 -46
View File
@@ -11,7 +11,6 @@ use std::{fmt, io, marker, mem, ptr};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
/// A platform-specific equivalent of the cross-platform `Library`.
pub struct Library(HMODULE);
@@ -36,11 +35,11 @@ impl Library {
///
/// Corresponds to `LoadLibraryW(filename)`.
#[inline]
pub fn new<P: AsRef<OsStr>>(filename: P) -> ::Result<Library> {
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
let wide_filename: Vec<u16> = filename.as_ref().encode_wide().chain(Some(0)).collect();
let _guard = ErrorModeGuard::new();
let ret = with_get_last_error(|| {
let ret = with_get_last_error(|source| crate::Error::LoadLibraryW { source }, || {
// Make sure no winapi calls as a result of drop happen inside this closure, because
// otherwise that might change the return value of the GetLastError.
let handle = unsafe { libloaderapi::LoadLibraryW(wide_filename.as_ptr()) };
@@ -49,10 +48,7 @@ impl Library {
} else {
Some(Library(handle))
}
}).map_err(|e| e.unwrap_or_else(||
panic!("LoadLibraryW failed but GetLastError did not report the error")
));
}).map_err(|e| e.unwrap_or(crate::Error::LoadLibraryWUnknown));
drop(wide_filename); // Drop wide_filename here to ensure it doesnt get moved and dropped
// inside the closure by mistake. See comment inside the closure.
ret
@@ -71,10 +67,10 @@ impl Library {
/// This function does not validate the type `T`. It is up to the user of this function to
/// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no
/// definied behaviour.
pub unsafe fn get<T>(&self, symbol: &[u8]) -> ::Result<Symbol<T>> {
ensure_compatible_types::<T, FARPROC>();
pub unsafe fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
ensure_compatible_types::<T, FARPROC>()?;
let symbol = cstr_cow_from_bytes(symbol)?;
with_get_last_error(|| {
with_get_last_error(|source| crate::Error::GetProcAddress { source }, || {
let symbol = libloaderapi::GetProcAddress(self.0, symbol.as_ptr());
if symbol.is_null() {
None
@@ -84,9 +80,7 @@ impl Library {
pd: marker::PhantomData
})
}
}).map_err(|e| e.unwrap_or_else(||
panic!("GetProcAddress failed but GetLastError did not report the error")
))
}).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown))
}
/// Get a pointer to function or static variable by ordinal number.
@@ -95,9 +89,9 @@ impl Library {
///
/// Pointer to a value of arbitrary type is returned. Using a value with wrong type is
/// undefined.
pub unsafe fn get_ordinal<T>(&self, ordinal: WORD) -> ::Result<Symbol<T>> {
ensure_compatible_types::<T, FARPROC>();
with_get_last_error(|| {
pub unsafe fn get_ordinal<T>(&self, ordinal: WORD) -> Result<Symbol<T>, crate::Error> {
ensure_compatible_types::<T, FARPROC>()?;
with_get_last_error(|source| crate::Error::GetProcAddress { source }, || {
let ordinal = ordinal as usize as *mut _;
let symbol = libloaderapi::GetProcAddress(self.0, ordinal);
if symbol.is_null() {
@@ -108,9 +102,7 @@ impl Library {
pd: marker::PhantomData
})
}
}).map_err(|e| e.unwrap_or_else(||
panic!("GetProcAddress failed but GetLastError did not report the error")
))
}).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown))
}
/// Convert the `Library` to a raw handle.
@@ -129,17 +121,22 @@ impl Library {
pub unsafe fn from_raw(handle: HMODULE) -> Library {
Library(handle)
}
}
impl Drop for Library {
fn drop(&mut self) {
with_get_last_error(|| {
/// Unload the library.
pub fn close(self) -> Result<(), crate::Error> {
with_get_last_error(|source| crate::Error::FreeLibrary { source }, || {
if unsafe { libloaderapi::FreeLibrary(self.0) == 0 } {
None
} else {
Some(())
}
}).unwrap()
}).map_err(|e| e.unwrap_or(crate::Error::FreeLibraryUnknown))
}
}
impl Drop for Library {
fn drop(&mut self) {
unsafe { libloaderapi::FreeLibrary(self.0); }
}
}
@@ -277,38 +274,44 @@ impl Drop for ErrorModeGuard {
}
}
fn with_get_last_error<T, F>(closure: F) -> Result<T, Option<io::Error>>
fn with_get_last_error<T, F>(wrap: fn(crate::error::WindowsError) -> crate::Error, closure: F)
-> Result<T, Option<crate::Error>>
where F: FnOnce() -> Option<T> {
closure().ok_or_else(|| {
let error = unsafe { errhandlingapi::GetLastError() };
if error == 0 {
None
} else {
Some(io::Error::from_raw_os_error(error as i32))
Some(wrap(crate::error::WindowsError(io::Error::from_raw_os_error(error as i32))))
}
})
}
#[test]
fn works_getlasterror() {
let lib = Library::new("kernel32.dll").unwrap();
let gle: Symbol<unsafe extern "system" fn() -> DWORD> = unsafe {
lib.get(b"GetLastError").unwrap()
};
unsafe {
errhandlingapi::SetLastError(42);
assert_eq!(errhandlingapi::GetLastError(), gle())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn works_getlasterror0() {
let lib = Library::new("kernel32.dll").unwrap();
let gle: Symbol<unsafe extern "system" fn() -> DWORD> = unsafe {
lib.get(b"GetLastError\0").unwrap()
};
unsafe {
errhandlingapi::SetLastError(42);
assert_eq!(errhandlingapi::GetLastError(), gle())
#[test]
fn works_getlasterror() {
let lib = Library::new("kernel32.dll").unwrap();
let gle: Symbol<unsafe extern "system" fn() -> DWORD> = unsafe {
lib.get(b"GetLastError").unwrap()
};
unsafe {
errhandlingapi::SetLastError(42);
assert_eq!(errhandlingapi::GetLastError(), gle())
}
}
#[test]
fn works_getlasterror0() {
let lib = Library::new("kernel32.dll").unwrap();
let gle: Symbol<unsafe extern "system" fn() -> DWORD> = unsafe {
lib.get(b"GetLastError\0").unwrap()
};
unsafe {
errhandlingapi::SetLastError(42);
assert_eq!(errhandlingapi::GetLastError(), gle())
}
}
}
+11 -41
View File
@@ -1,61 +1,31 @@
use std::ffi::{CStr, CString, NulError, FromBytesWithNulError};
use std::ffi::{CStr, CString};
use std::borrow::Cow;
use std::os::raw;
#[derive(Debug)]
pub struct NullError;
impl From<NulError> for NullError {
fn from(_: NulError) -> NullError {
NullError
}
}
impl From<FromBytesWithNulError> for NullError {
fn from(_: FromBytesWithNulError) -> NullError {
NullError
}
}
impl From<NullError> for ::std::io::Error {
fn from(e: NullError) -> ::std::io::Error {
::std::io::Error::new(::std::io::ErrorKind::Other, format!("{}", e))
}
}
impl ::std::error::Error for NullError {
fn description(&self) -> &str { "non-final null byte found" }
}
impl ::std::fmt::Display for NullError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "non-final null byte found")
}
}
use crate::Error;
/// Checks for last byte and avoids allocating if it is zero.
///
/// Non-last null bytes still result in an error.
pub fn cstr_cow_from_bytes<'a>(slice: &'a [u8]) -> Result<Cow<'a, CStr>, NullError> {
pub(crate) fn cstr_cow_from_bytes<'a>(slice: &'a [u8]) -> Result<Cow<'a, CStr>, Error> {
static ZERO: raw::c_char = 0;
Ok(match slice.last() {
// Slice out of 0 elements
None => unsafe { Cow::Borrowed(CStr::from_ptr(&ZERO)) },
// Slice with trailing 0
Some(&0) => Cow::Borrowed(CStr::from_bytes_with_nul(slice)?),
Some(&0) => Cow::Borrowed(CStr::from_bytes_with_nul(slice)
.map_err(|source| Error::CreateCStringWithTrailing { source })?),
// Slice with no trailing 0
Some(_) => Cow::Owned(CString::new(slice)?),
Some(_) => Cow::Owned(CString::new(slice)
.map_err(|source| Error::CreateCString { source })?),
})
}
#[inline]
pub fn ensure_compatible_types<T, E>() {
#[cold]
#[inline(never)]
fn dopanic() {
panic!("value of requested type cannot be dynamically loaded");
}
pub(crate) fn ensure_compatible_types<T, E>() -> Result<(), Error> {
if ::std::mem::size_of::<T>() != ::std::mem::size_of::<E>() {
dopanic()
Err(Error::IncompatibleSize)
} else {
Ok(())
}
}
+10 -6
View File
@@ -4,7 +4,7 @@ use libloading::{Symbol, Library};
const LIBPATH: &'static str = concat!(env!("OUT_DIR"), "/libtest_helpers.dll");
fn make_helpers() {
static ONCE: ::std::sync::Once = ::std::sync::ONCE_INIT;
static ONCE: ::std::sync::Once = ::std::sync::Once::new();
ONCE.call_once(|| {
let mut outpath = String::from(if let Some(od) = option_env!("OUT_DIR") { od } else { return });
let rustc = option_env!("RUSTC").unwrap_or_else(|| { "rustc".into() });
@@ -85,25 +85,29 @@ fn interior_null_fails() {
}
#[test]
#[should_panic]
fn test_incompatible_type() {
make_helpers();
let lib = Library::new(LIBPATH).unwrap();
unsafe {
let _ = lib.get::<()>(b"test_identity_u32\0");
assert!(match lib.get::<()>(b"test_identity_u32\0") {
Err(libloading::Error::IncompatibleSize) => true,
_ => false,
})
}
}
#[test]
#[should_panic]
fn test_incompatible_type_named_fn() {
make_helpers();
unsafe fn get<'a, T>(l: &'a Library, _: T) -> libloading::Result<Symbol<'a, T>> {
unsafe fn get<'a, T>(l: &'a Library, _: T) -> Result<Symbol<'a, T>, libloading::Error> {
l.get::<T>(b"test_identity_u32\0")
}
let lib = Library::new(LIBPATH).unwrap();
unsafe {
let _ = get(&lib, test_incompatible_type_named_fn);
assert!(match get(&lib, test_incompatible_type_named_fn) {
Err(libloading::Error::IncompatibleSize) => true,
_ => false,
})
}
}