///|
/// A failure produced while loading, resolving, or closing a dynamic library.
pub(all) enum DynlibError {
UnsupportedTarget
InvalidLibraryPath
InvalidSymbolName
InvalidUtf8
OutOfMemory
LoadFailed
ResolveFailed
SymbolNotFound(String)
InvalidHandle
CloseFailed
Closed
} derive(Eq, Debug)
///|
pub extend DynlibError with Eq::{equal}
///|
#deprecated("Use `!=` instead", skip_current_package=true)
#doc(hidden)
pub extend DynlibError with Eq::{not_equal}
///|
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
#doc(hidden)
pub extend DynlibError with @debug.Debug::{to_repr}
///|
/// An explicitly owned native dynamic-library handle.
///
/// Call close when the library is no longer needed. Closing is idempotent.
pub struct Library {
priv handle : NativeHandle
priv mut closed : Bool
}
///|
/// A resolved symbol tied to the library that produced it.
///
/// The address cannot be obtained after the source library is closed.
pub struct Symbol {
priv library : Library
priv address : NativeAddress
}
///|
fn has_nul(value : String) -> Bool {
value.contains_code_unit(0)
}
///|
fn encode_utf8(value : String) -> Bytes {
@utf8.encode(value)
}
///|
fn load_error(code : Int) -> DynlibError {
if code == ffi_error_unsupported_target {
UnsupportedTarget
} else if code == ffi_error_invalid_argument {
InvalidLibraryPath
} else if code == ffi_error_invalid_utf8 {
InvalidUtf8
} else if code == ffi_error_out_of_memory {
OutOfMemory
} else if code == ffi_error_load_failed {
LoadFailed
} else {
LoadFailed
}
}
///|
fn resolve_error(code : Int, name : String) -> DynlibError {
if code == ffi_error_unsupported_target {
UnsupportedTarget
} else if code == ffi_error_invalid_argument {
InvalidSymbolName
} else if code == ffi_error_invalid_utf8 {
InvalidUtf8
} else if code == ffi_error_out_of_memory {
OutOfMemory
} else if code == ffi_error_invalid_handle {
InvalidHandle
} else if code == ffi_error_symbol_not_found {
SymbolNotFound(name)
} else {
ResolveFailed
}
}
///|
fn close_error(code : Int) -> DynlibError {
if code == ffi_error_unsupported_target {
UnsupportedTarget
} else if code == ffi_error_invalid_handle {
InvalidHandle
} else if code == ffi_error_close_failed {
CloseFailed
} else {
CloseFailed
}
}
///|
/// Loads a dynamic library by absolute path or platform loader name.
///
/// Paths are encoded as UTF-8. On Windows they are converted to UTF-16 before
/// calling the operating system loader.
pub fn load(path : String) -> Result[Library, DynlibError] {
if path.is_empty() || has_nul(path) {
return Err(InvalidLibraryPath)
}
let handle = native_load(encode_utf8(path))
if native_is_null(handle) {
Err(load_error(native_last_error()))
} else {
Ok({ handle, closed: false, })
}
}
///|
/// Returns whether this library remains open.
pub fn Library::is_open(self : Library) -> Bool {
!self.closed
}
///|
/// Resolves a native symbol without invoking it.
///
/// Dynamic function calls are deliberately outside this package's API because
/// calling conventions and signatures must be modeled by the consuming FFI.
pub fn Library::resolve(
self : Library,
name : String,
) -> Result[Symbol, DynlibError] {
if !self.is_open() {
return Err(Closed)
}
if name.is_empty() || has_nul(name) {
return Err(InvalidSymbolName)
}
let address = native_resolve(self.handle, encode_utf8(name))
if native_address_is_null(address) {
Err(resolve_error(native_last_error(), name))
} else {
Ok({ library: self, address, })
}
}
///|
/// Returns a raw symbol address while its source library remains open.
///
/// The caller must not retain or invoke the address after Library::close.
pub fn Symbol::address(self : Symbol) -> Result[UInt64, DynlibError] {
if self.library.is_open() {
Ok(native_address_value(self.address))
} else {
Err(Closed)
}
}
///|
/// Unloads the library. Calling close again returns success without touching
/// the operating system handle.
pub fn Library::close(self : Library) -> Result[Unit, DynlibError] {
if !self.is_open() {
return Ok(())
}
let status = native_close(self.handle)
if status == ffi_error_ok {
self.closed = true
Ok(())
} else {
Err(close_error(status))
}
}